diff options
Diffstat (limited to 'src')
233 files changed, 14315 insertions, 3795 deletions
diff --git a/src/3rdparty b/src/3rdparty -Subproject 6c9be50c2d901e66119679155fb3c7c9200448d +Subproject 9424dc7ceeccf6e6d5edc7757edb39a7ae4983a diff --git a/src/buildtools/config/common.pri b/src/buildtools/config/common.pri index 97d39535c..f2b4a854e 100644 --- a/src/buildtools/config/common.pri +++ b/src/buildtools/config/common.pri @@ -8,6 +8,8 @@ gn_args += \ closure_compile=false \ is_component_build=false \ is_shared=true \ + enable_debugallocation=false \ + enable_media_remoting=false \ enable_message_center=false \ enable_nacl=false \ enable_remoting=false \ @@ -18,15 +20,15 @@ gn_args += \ enable_web_speech=false \ enable_widevine=true \ has_native_accessibility=false \ - enable_debugallocation=false \ + optimize_webui=false \ + safe_browsing_mode=0 \ + strip_absolute_paths_from_debug_symbols=false \ + toolkit_views=false \ + treat_warnings_as_errors=false \ use_allocator_shim=false \ use_allocator=\"none\" \ use_custom_libcxx=false \ v8_use_external_startup_data=false \ - toolkit_views=false \ - treat_warnings_as_errors=false \ - safe_browsing_mode=0 \ - optimize_webui=false \ forbid_non_component_debug_builds=false greaterThan(QMAKE_JUMBO_MERGE_LIMIT,0) { @@ -39,7 +41,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 +49,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 +128,7 @@ 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) { - 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 +139,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..2aa345c21 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 \ @@ -17,8 +17,6 @@ gn_args += \ use_sysroot=false \ enable_session_service=false \ is_cfi=false \ - strip_absolute_paths_from_debug_symbols=false \ - toolkit_views=false \ use_ozone=true \ ozone_auto_platforms=false \ ozone_platform_headless=false \ @@ -26,7 +24,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 +90,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 +156,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 +182,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..a910e3e31 100644 --- a/src/buildtools/config/windows.pri +++ b/src/buildtools/config/windows.pri @@ -56,12 +56,17 @@ defineTest(usingMSVC32BitCrossCompiler) { } CL_DIR = $$system_path($$CL_DIR) CL_DIR = $$split(CL_DIR, \\) - CL_PLATFORM = $$last(CL_DIR) + CL_PLATFORM = $$take_last(CL_DIR) equals(CL_PLATFORM, amd64_x86): return(true) + equals(CL_PLATFORM, x86)|equals(CL_PLATFORM, x64) { + CL_PLATFORM = $$take_last(CL_DIR) + equals(CL_PLATFORM, HostX64): return(true) + } return(false) } msvc:contains(QT_ARCH, "i386"):!usingMSVC32BitCrossCompiler() { + warning(Full debug info is disabled for chromium due to 32bit compiler) # The 32 bit MSVC linker runs out of memory if we do not remove all debug information. force_debug_info: gn_args -= symbol_level=1 gn_args *= symbol_level=0 @@ -89,7 +94,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..c2531a744 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"} } }, @@ -263,10 +264,11 @@ "main": [ "vpx_codec_cx_pkt pkt;", "pkt.data.frame.width[0] = 0u;", - "pkt.data.frame.height[0] = 0u;" + "pkt.data.frame.height[0] = 0u;", + "auto a = CONSTRAINED_FROM_ABOVE_DROP;" ] }, - "headers": "vpx/vpx_encoder.h", + "headers": [ "vpx/vpx_encoder.h", "vpx/vp8cx.h" ], "sources": [ { "type": "pkgConfig", "args": "vpx" }, "-lvpx" @@ -386,6 +388,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 +700,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 +711,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 +721,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 +736,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/accessibility_tree_formatter_qt.cpp b/src/core/accessibility_tree_formatter_qt.cpp index 3520087ae..334759abb 100644 --- a/src/core/accessibility_tree_formatter_qt.cpp +++ b/src/core/accessibility_tree_formatter_qt.cpp @@ -59,7 +59,7 @@ public: ~AccessibilityTreeFormatterQt() override; private: - const base::FilePath::StringType GetExpectedFileSuffix() override; + base::FilePath::StringType GetExpectedFileSuffix() override; const std::string GetAllowEmptyString() override; const std::string GetAllowString() override; const std::string GetDenyString() override; @@ -179,7 +179,7 @@ base::string16 AccessibilityTreeFormatterQt::ProcessTreeForOutput(const base::Di return line + base::ASCIIToUTF16("\n"); } -const base::FilePath::StringType AccessibilityTreeFormatterQt::GetExpectedFileSuffix() +base::FilePath::StringType AccessibilityTreeFormatterQt::GetExpectedFileSuffix() { return FILE_PATH_LITERAL("-expected-qt.txt"); } diff --git a/src/core/api/core_api.pro b/src/core/api/core_api.pro index 5e8b8387e..6b4291cb7 100644 --- a/src/core/api/core_api.pro +++ b/src/core/api/core_api.pro @@ -67,11 +67,13 @@ SOURCES = \ ### Qt6 Remove this workaround unix:!isEmpty(QMAKE_LFLAGS_VERSION_SCRIPT):!static { - CONFIG -= warning_clean SOURCES += qtbug-60565.cpp \ qtbug-61521.cpp } +# Chromium headers included are not remotely clean +CONFIG -= warning_clean + msvc { # Create a list of object files that can be used as response file for the linker. # This is done to simulate -whole-archive on MSVC. diff --git a/src/core/api/qwebengineurlrequestinfo.cpp b/src/core/api/qwebengineurlrequestinfo.cpp index e44410099..3816f08ca 100644 --- a/src/core/api/qwebengineurlrequestinfo.cpp +++ b/src/core/api/qwebengineurlrequestinfo.cpp @@ -145,11 +145,25 @@ QWebEngineUrlRequestInfoPrivate::QWebEngineUrlRequestInfoPrivate(QWebEngineUrlRe /*! \internal */ +QWebEngineUrlRequestInfo::QWebEngineUrlRequestInfo() {} + +/*! + \internal +*/ QWebEngineUrlRequestInfo::QWebEngineUrlRequestInfo(QWebEngineUrlRequestInfo &&p) : d_ptr(p.d_ptr.take()) {} /*! \internal */ +QWebEngineUrlRequestInfo &QWebEngineUrlRequestInfo::operator=(QWebEngineUrlRequestInfo &&p) +{ + d_ptr.reset(p.d_ptr.take()); + return *this; +} + +/*! + \internal +*/ QWebEngineUrlRequestInfo::~QWebEngineUrlRequestInfo() {} diff --git a/src/core/api/qwebengineurlrequestinfo.h b/src/core/api/qwebengineurlrequestinfo.h index 75b6e3a81..21e44d2f1 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,13 +111,16 @@ public: private: friend class QtWebEngineCore::NetworkDelegateQt; friend class QtWebEngineCore::URLRequestNotification; + friend class QtWebEngineCore::InterceptedRequest; Q_DISABLE_COPY(QWebEngineUrlRequestInfo) Q_DECLARE_PRIVATE(QWebEngineUrlRequestInfo) void resetChanged(); + QWebEngineUrlRequestInfo(); QWebEngineUrlRequestInfo(QWebEngineUrlRequestInfoPrivate *p); QWebEngineUrlRequestInfo(QWebEngineUrlRequestInfo &&p); + QWebEngineUrlRequestInfo &operator=(QWebEngineUrlRequestInfo &&p); ~QWebEngineUrlRequestInfo(); QScopedPointer<QWebEngineUrlRequestInfoPrivate> d_ptr; }; diff --git a/src/core/authentication_dialog_controller.cpp b/src/core/authentication_dialog_controller.cpp index 23dd62979..e37ffab44 100644 --- a/src/core/authentication_dialog_controller.cpp +++ b/src/core/authentication_dialog_controller.cpp @@ -53,11 +53,9 @@ AuthenticationDialogControllerPrivate::AuthenticationDialogControllerPrivate(bas void AuthenticationDialogControllerPrivate::dialogFinished(bool accepted, const QString &user, const QString &password) { - base::PostTaskWithTraits( - FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(&LoginDelegateQt::sendAuthToRequester, - loginDelegate, - accepted, user, password)); + base::PostTask(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(&LoginDelegateQt::sendAuthToRequester, + loginDelegate, accepted, user, password)); } AuthenticationDialogController::AuthenticationDialogController(AuthenticationDialogControllerPrivate *dd) diff --git a/src/core/browser_accessibility_qt.cpp b/src/core/browser_accessibility_qt.cpp index 816a46041..d816678e3 100644 --- a/src/core/browser_accessibility_qt.cpp +++ b/src/core/browser_accessibility_qt.cpp @@ -225,8 +225,15 @@ QAccessible::Role BrowserAccessibilityQt::role() const return QAccessible::AlertMessage; case ax::mojom::Role::kAnchor: return QAccessible::Link; - case ax::mojom::Role::kAnnotation: - return QAccessible::StaticText; + + // REMINDER: annotation roles are removed from Chromium 80: https://chromium-review.googlesource.com/c/chromium/src/+/1907074 + case ax::mojom::Role::kAnnotationAttribution: + case ax::mojom::Role::kAnnotationCommentary: + case ax::mojom::Role::kAnnotationPresence: + case ax::mojom::Role::kAnnotationRevision: + case ax::mojom::Role::kAnnotationSuggestion: + return QAccessible::Section; + case ax::mojom::Role::kApplication: return QAccessible::Document; // returning Application here makes Qt return the top level app object case ax::mojom::Role::kArticle: @@ -282,17 +289,17 @@ QAccessible::Role BrowserAccessibilityQt::role() const case ax::mojom::Role::kDetails: return QAccessible::Grouping; case ax::mojom::Role::kDesktop: - return QAccessible::NoRole; // FIXME + return QAccessible::Pane; case ax::mojom::Role::kDialog: return QAccessible::Dialog; case ax::mojom::Role::kDirectory: - return QAccessible::NoRole; // FIXME + return QAccessible::List; case ax::mojom::Role::kDisclosureTriangle: - return QAccessible::NoRole; // FIXME + return QAccessible::Button; case ax::mojom::Role::kGenericContainer: return QAccessible::Section; case ax::mojom::Role::kDocCover: - return QAccessible::Graphic; + return QAccessible::Graphic; case ax::mojom::Role::kDocBackLink: case ax::mojom::Role::kDocBiblioRef: case ax::mojom::Role::kDocGlossRef: @@ -331,14 +338,16 @@ QAccessible::Role BrowserAccessibilityQt::role() const case ax::mojom::Role::kDocPrologue: case ax::mojom::Role::kDocPullquote: case ax::mojom::Role::kDocQna: + return QAccessible::Section; case ax::mojom::Role::kDocSubtitle: + return QAccessible::Heading; case ax::mojom::Role::kDocTip: case ax::mojom::Role::kDocToc: return QAccessible::Section; case ax::mojom::Role::kDocument: return QAccessible::Document; case ax::mojom::Role::kEmbeddedObject: - return QAccessible::Grouping; // FIXME + return QAccessible::Grouping; case ax::mojom::Role::kFeed: return QAccessible::Section; case ax::mojom::Role::kFigcaption: @@ -347,6 +356,8 @@ QAccessible::Role BrowserAccessibilityQt::role() const return QAccessible::Section; case ax::mojom::Role::kFooter: return QAccessible::Footer; + case ax::mojom::Role::kFooterAsNonLandmark: + return QAccessible::Section; case ax::mojom::Role::kForm: return QAccessible::Form; case ax::mojom::Role::kGraphicsDocument: @@ -359,20 +370,23 @@ QAccessible::Role BrowserAccessibilityQt::role() const return QAccessible::Table; case ax::mojom::Role::kGroup: return QAccessible::Grouping; + case ax::mojom::Role::kHeader: + case ax::mojom::Role::kHeaderAsNonLandmark: + return QAccessible::Section; case ax::mojom::Role::kHeading: return QAccessible::Heading; case ax::mojom::Role::kIframe: - return QAccessible::Grouping; + return QAccessible::WebDocument; case ax::mojom::Role::kIframePresentational: - return QAccessible::NoRole; // FIXME + return QAccessible::Grouping; case ax::mojom::Role::kIgnored: return QAccessible::NoRole; case ax::mojom::Role::kImage: return QAccessible::Graphic; case ax::mojom::Role::kImageMap: - return QAccessible::Graphic; + return QAccessible::Document; case ax::mojom::Role::kInlineTextBox: - return QAccessible::EditableText; + return QAccessible::StaticText; case ax::mojom::Role::kInputTime: return QAccessible::SpinBox; case ax::mojom::Role::kKeyboard: @@ -383,8 +397,7 @@ QAccessible::Role BrowserAccessibilityQt::role() const case ax::mojom::Role::kLayoutTableCell: case ax::mojom::Role::kLayoutTableColumn: case ax::mojom::Role::kLayoutTableRow: - // No role description. - return QAccessible::NoRole; + return QAccessible::Section; case ax::mojom::Role::kLegend: return QAccessible::StaticText; case ax::mojom::Role::kLineBreak: @@ -459,18 +472,21 @@ QAccessible::Role BrowserAccessibilityQt::role() const return QAccessible::RowHeader; case ax::mojom::Role::kRuby: return QAccessible::StaticText; + case ax::mojom::Role::kRubyAnnotation: + return QAccessible::StaticText; case ax::mojom::Role::kScrollBar: return QAccessible::ScrollBar; case ax::mojom::Role::kScrollView: - return QAccessible::NoRole; // FIXME + return QAccessible::Pane; case ax::mojom::Role::kSearch: return QAccessible::Section; case ax::mojom::Role::kSearchBox: return QAccessible::EditableText; + case ax::mojom::Role::kSection: + return QAccessible::Section; case ax::mojom::Role::kSlider: - return QAccessible::Slider; case ax::mojom::Role::kSliderThumb: - return QAccessible::NoRole; // FIXME + return QAccessible::Slider; case ax::mojom::Role::kSpinButton: return QAccessible::SpinBox; case ax::mojom::Role::kSplitter: @@ -501,7 +517,7 @@ QAccessible::Role BrowserAccessibilityQt::role() const case ax::mojom::Role::kTimer: return QAccessible::Clock; case ax::mojom::Role::kTitleBar: - return QAccessible::NoRole; // FIXME + return QAccessible::Document; case ax::mojom::Role::kToggleButton: return QAccessible::Button; case ax::mojom::Role::kToolbar: diff --git a/src/core/browser_main_parts_qt.cpp b/src/core/browser_main_parts_qt.cpp index 3e9af8167..925c18664 100644 --- a/src/core/browser_main_parts_qt.cpp +++ b/src/core/browser_main_parts_qt.cpp @@ -61,12 +61,10 @@ #include "extensions/extension_system_factory_qt.h" #include "common/extensions/extensions_client_qt.h" #endif //BUILDFLAG(ENABLE_EXTENSIONS) -#include "services/resource_coordinator/public/cpp/resource_coordinator_features.h" #include "services/service_manager/public/cpp/connector.h" #include "services/service_manager/public/cpp/service.h" #include "ui/display/screen.h" -#include "service/service_qt.h" #include "web_engine_context.h" #include <QtGui/qtgui-config.h> @@ -281,8 +279,6 @@ int BrowserMainPartsQt::PreCreateThreads() void BrowserMainPartsQt::PostCreateThreads() { - ServiceQt::GetInstance()->InitConnector(); - content::GetSystemConnector()->WarmService(service_manager::ServiceFilter::ByName("qtwebengine")); } } // namespace QtWebEngineCore diff --git a/src/core/certificate_error_controller.h b/src/core/certificate_error_controller.h index d7e057adf..7c7db37ef 100644 --- a/src/core/certificate_error_controller.h +++ b/src/core/certificate_error_controller.h @@ -85,8 +85,7 @@ public: CertificateValidityTooLong = -213, CertificateTransparencyRequired = -214, CertificateSymantecLegacy = -215, - - CertificateErrorEnd = -216 // not an error, just an enum boundary + CertificateErrorEnd = -217 // not an error, just an enum boundary }; CertificateError error() const; diff --git a/src/core/chromium_overrides.cpp b/src/core/chromium_overrides.cpp index 9d3e3f08a..01937d3e7 100644 --- a/src/core/chromium_overrides.cpp +++ b/src/core/chromium_overrides.cpp @@ -170,26 +170,26 @@ bool GrabViewSnapshot(gfx::NativeView view, void GrabWindowSnapshotAndScaleAsync(gfx::NativeWindow window, const gfx::Rect& source_rect, const gfx::Size& target_size, - const GrabWindowSnapshotAsyncCallback& callback) + GrabWindowSnapshotAsyncCallback callback) { NOTIMPLEMENTED(); - callback.Run(gfx::Image()); + std::move(callback).Run(gfx::Image()); } void GrabWindowSnapshotAsync(gfx::NativeWindow window, const gfx::Rect& source_rect, - const GrabWindowSnapshotAsyncCallback& callback) + GrabWindowSnapshotAsyncCallback callback) { NOTIMPLEMENTED(); - callback.Run(gfx::Image()); + std::move(callback).Run(gfx::Image()); } void GrabViewSnapshotAsync(gfx::NativeView view, const gfx::Rect& source_rect, - const GrabWindowSnapshotAsyncCallback& callback) + GrabWindowSnapshotAsyncCallback callback) { NOTIMPLEMENTED(); - callback.Run(gfx::Image()); + std::move(callback).Run(gfx::Image()); } } // namespace ui diff --git a/src/core/clipboard_qt.cpp b/src/core/clipboard_qt.cpp index 70e0a2376..ef7a05299 100644 --- a/src/core/clipboard_qt.cpp +++ b/src/core/clipboard_qt.cpp @@ -105,30 +105,37 @@ Clipboard *Clipboard::Create() namespace QtWebEngineCore { -void ClipboardQt::WriteObjects(ui::ClipboardType type, const ObjectMap &objects) +void ClipboardQt::WritePortableRepresentations(ui::ClipboardBuffer type, const ObjectMap &objects) { DCHECK(CalledOnValidThread()); - DCHECK(IsSupportedClipboardType(type)); + DCHECK(IsSupportedClipboardBuffer(type)); - for (ObjectMap::const_iterator iter = objects.begin(); iter != objects.end(); ++iter) - DispatchObject(static_cast<ObjectType>(iter->first), iter->second); + for (const auto &object : objects) + DispatchPortableRepresentation(object.first, object.second); // Commit the accumulated data. if (uncommittedData) QGuiApplication::clipboard()->setMimeData(uncommittedData.take(), - type == ui::ClipboardType::kCopyPaste ? QClipboard::Clipboard - : QClipboard::Selection); + type == ui::ClipboardBuffer::kCopyPaste ? QClipboard::Clipboard + : QClipboard::Selection); - if (type == ui::ClipboardType::kCopyPaste && IsSupportedClipboardType(ui::ClipboardType::kSelection)) { - ObjectMap::const_iterator text_iter = objects.find(CBF_TEXT); + if (type == ui::ClipboardBuffer::kCopyPaste && IsSupportedClipboardBuffer(ui::ClipboardBuffer::kSelection)) { + ObjectMap::const_iterator text_iter = objects.find(PortableFormat::kText); if (text_iter != objects.end()) { // Copy text and SourceTag to the selection clipboard. - ObjectMap::const_iterator next_iter = text_iter; - WriteObjects(ui::ClipboardType::kSelection, ObjectMap(text_iter, ++next_iter, base::KEEP_FIRST_OF_DUPES)); + WritePortableRepresentations(ui::ClipboardBuffer::kSelection, + ObjectMap(text_iter, text_iter + 1)); } } } +void ClipboardQt::WritePlatformRepresentations(ui::ClipboardBuffer buffer, std::vector<ui::Clipboard::PlatformRepresentation> platform_representations) +{ + DCHECK(CalledOnValidThread()); + DCHECK(IsSupportedClipboardBuffer(buffer)); + DispatchPlatformRepresentations(std::move(platform_representations)); +} + void ClipboardQt::WriteText(const char *text_data, size_t text_len) { getUncommittedData()->setText(QString::fromUtf8(text_data, text_len)); @@ -173,20 +180,20 @@ void ClipboardQt::WriteData(const ui::ClipboardFormatType &format, const char *d getUncommittedData()->setData(QString::fromStdString(format.ToString()), QByteArray(data_data, data_len)); } -bool ClipboardQt::IsFormatAvailable(const ui::ClipboardFormatType &format, ui::ClipboardType type) const +bool ClipboardQt::IsFormatAvailable(const ui::ClipboardFormatType &format, ui::ClipboardBuffer type) const { const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData( - type == ui::ClipboardType::kCopyPaste ? QClipboard::Clipboard : QClipboard::Selection); + type == ui::ClipboardBuffer::kCopyPaste ? QClipboard::Clipboard : QClipboard::Selection); return mimeData && mimeData->hasFormat(QString::fromStdString(format.ToString())); } -void ClipboardQt::Clear(ui::ClipboardType type) +void ClipboardQt::Clear(ui::ClipboardBuffer type) { - QGuiApplication::clipboard()->clear(type == ui::ClipboardType::kCopyPaste ? QClipboard::Clipboard + QGuiApplication::clipboard()->clear(type == ui::ClipboardBuffer::kCopyPaste ? QClipboard::Clipboard : QClipboard::Selection); } -void ClipboardQt::ReadAvailableTypes(ui::ClipboardType type, std::vector<base::string16> *types, +void ClipboardQt::ReadAvailableTypes(ui::ClipboardBuffer type, std::vector<base::string16> *types, bool *contains_filenames) const { if (!types || !contains_filenames) { @@ -196,7 +203,7 @@ void ClipboardQt::ReadAvailableTypes(ui::ClipboardType type, std::vector<base::s types->clear(); const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData( - type == ui::ClipboardType::kCopyPaste ? QClipboard::Clipboard : QClipboard::Selection); + type == ui::ClipboardBuffer::kCopyPaste ? QClipboard::Clipboard : QClipboard::Selection); if (!mimeData) return; if (mimeData->hasImage() && !mimeData->formats().contains(QStringLiteral("image/png"))) @@ -210,23 +217,23 @@ void ClipboardQt::ReadAvailableTypes(ui::ClipboardType type, std::vector<base::s ui::ReadCustomDataTypes(customData.constData(), customData.size(), types); } -void ClipboardQt::ReadText(ui::ClipboardType type, base::string16 *result) const +void ClipboardQt::ReadText(ui::ClipboardBuffer type, base::string16 *result) const { const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData( - type == ui::ClipboardType::kCopyPaste ? QClipboard::Clipboard : QClipboard::Selection); + type == ui::ClipboardBuffer::kCopyPaste ? QClipboard::Clipboard : QClipboard::Selection); if (mimeData) *result = toString16(mimeData->text()); } -void ClipboardQt::ReadAsciiText(ui::ClipboardType type, std::string *result) const +void ClipboardQt::ReadAsciiText(ui::ClipboardBuffer type, std::string *result) const { const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData( - type == ui::ClipboardType::kCopyPaste ? QClipboard::Clipboard : QClipboard::Selection); + type == ui::ClipboardBuffer::kCopyPaste ? QClipboard::Clipboard : QClipboard::Selection); if (mimeData) *result = mimeData->text().toStdString(); } -void ClipboardQt::ReadHTML(ui::ClipboardType type, base::string16 *markup, std::string *src_url, +void ClipboardQt::ReadHTML(ui::ClipboardBuffer type, base::string16 *markup, std::string *src_url, uint32_t *fragment_start, uint32_t *fragment_end) const { markup->clear(); @@ -236,27 +243,27 @@ void ClipboardQt::ReadHTML(ui::ClipboardType type, base::string16 *markup, std:: *fragment_end = 0; const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData( - type == ui::ClipboardType::kCopyPaste ? QClipboard::Clipboard : QClipboard::Selection); + type == ui::ClipboardBuffer::kCopyPaste ? QClipboard::Clipboard : QClipboard::Selection); if (!mimeData) return; *markup = toString16(mimeData->html()); *fragment_end = static_cast<uint32_t>(markup->length()); } -void ClipboardQt::ReadRTF(ui::ClipboardType type, std::string *result) const +void ClipboardQt::ReadRTF(ui::ClipboardBuffer type, std::string *result) const { const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData( - type == ui::ClipboardType::kCopyPaste ? QClipboard::Clipboard : QClipboard::Selection); + type == ui::ClipboardBuffer::kCopyPaste ? QClipboard::Clipboard : QClipboard::Selection); if (!mimeData) return; const QByteArray byteArray = mimeData->data(QString::fromLatin1(ui::kMimeTypeRTF)); *result = std::string(byteArray.constData(), byteArray.length()); } -SkBitmap ClipboardQt::ReadImage(ui::ClipboardType type) const +SkBitmap ClipboardQt::ReadImage(ui::ClipboardBuffer type) const { const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData( - type == ui::ClipboardType::kCopyPaste ? QClipboard::Clipboard : QClipboard::Selection); + type == ui::ClipboardBuffer::kCopyPaste ? QClipboard::Clipboard : QClipboard::Selection); if (!mimeData) return SkBitmap(); QImage image = qvariant_cast<QImage>(mimeData->imageData()); @@ -279,10 +286,10 @@ SkBitmap ClipboardQt::ReadImage(ui::ClipboardType type) const return bitmap; } -void ClipboardQt::ReadCustomData(ui::ClipboardType clipboard_type, const base::string16 &type, base::string16 *result) const +void ClipboardQt::ReadCustomData(ui::ClipboardBuffer clipboard_type, const base::string16 &type, base::string16 *result) const { const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData( - clipboard_type == ui::ClipboardType::kCopyPaste ? QClipboard::Clipboard : QClipboard::Selection); + clipboard_type == ui::ClipboardBuffer::kCopyPaste ? QClipboard::Clipboard : QClipboard::Selection); if (!mimeData) return; const QByteArray customData = mimeData->data(QString::fromLatin1(ui::kMimeTypeWebCustomData)); @@ -303,10 +310,10 @@ void ClipboardQt::ReadData(const ui::ClipboardFormatType &format, std::string *r *result = std::string(byteArray.constData(), byteArray.length()); } -uint64_t ClipboardQt::GetSequenceNumber(ui::ClipboardType type) const +uint64_t ClipboardQt::GetSequenceNumber(ui::ClipboardBuffer type) const { - return clipboardChangeObserver()->getSequenceNumber(type == ui::ClipboardType::kCopyPaste ? QClipboard::Clipboard - : QClipboard::Selection); + return clipboardChangeObserver()->getSequenceNumber(type == ui::ClipboardBuffer::kCopyPaste ? QClipboard::Clipboard + : QClipboard::Selection); } } // namespace QtWebEngineCore diff --git a/src/core/clipboard_qt.h b/src/core/clipboard_qt.h index 7884da167..6d86e8f6c 100644 --- a/src/core/clipboard_qt.h +++ b/src/core/clipboard_qt.h @@ -46,25 +46,27 @@ namespace QtWebEngineCore { class ClipboardQt : public ui::Clipboard { public: - uint64_t GetSequenceNumber(ui::ClipboardType type) const override; - bool IsFormatAvailable(const ui::ClipboardFormatType &format, ui::ClipboardType type) const override; - void Clear(ui::ClipboardType type) override; - void ReadAvailableTypes(ui::ClipboardType type, std::vector<base::string16> *types, + uint64_t GetSequenceNumber(ui::ClipboardBuffer type) const override; + bool IsFormatAvailable(const ui::ClipboardFormatType &format, ui::ClipboardBuffer type) const override; + void Clear(ui::ClipboardBuffer type) override; + void ReadAvailableTypes(ui::ClipboardBuffer type, std::vector<base::string16> *types, bool *contains_filenames) const override; - void ReadText(ui::ClipboardType type, base::string16 *result) const override; - void ReadAsciiText(ui::ClipboardType type, std::string *result) const override; - void ReadHTML(ui::ClipboardType type, base::string16 *markup, std::string *src_url, uint32_t *fragment_start, + void ReadText(ui::ClipboardBuffer type, base::string16 *result) const override; + void ReadAsciiText(ui::ClipboardBuffer type, std::string *result) const override; + void ReadHTML(ui::ClipboardBuffer type, base::string16 *markup, std::string *src_url, uint32_t *fragment_start, uint32_t *fragment_end) const override; - void ReadRTF(ui::ClipboardType type, std::string *result) const override; - SkBitmap ReadImage(ui::ClipboardType type) const override; - void ReadCustomData(ui::ClipboardType clipboard_type, const base::string16 &type, base::string16 *result) const override; + void ReadRTF(ui::ClipboardBuffer type, std::string *result) const override; + SkBitmap ReadImage(ui::ClipboardBuffer type) const override; + void ReadCustomData(ui::ClipboardBuffer clipboard_type, const base::string16 &type, base::string16 *result) const override; void ReadBookmark(base::string16 *title, std::string *url) const override; void ReadData(const ui::ClipboardFormatType &format, std::string *result) const override; void OnPreShutdown() override {} protected: - void WriteObjects(ui::ClipboardType type, const ObjectMap &objects) override; + void WritePortableRepresentations(ui::ClipboardBuffer type, const ObjectMap &objects) override; + void WritePlatformRepresentations(ui::ClipboardBuffer type, + std::vector<ui::Clipboard::PlatformRepresentation> platform_representations) override; void WriteText(const char *text_data, size_t text_len) override; void WriteHTML(const char *markup_data, size_t markup_len, const char *url_data, size_t url_len) override; void WriteRTF(const char *rtf_data, size_t data_len) override; diff --git a/src/core/command_line_pref_store_qt.cpp b/src/core/command_line_pref_store_qt.cpp deleted file mode 100644 index 5c5c82e1a..000000000 --- a/src/core/command_line_pref_store_qt.cpp +++ /dev/null @@ -1,90 +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$ -** -****************************************************************************/ - -// Copyright (c) 2012 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 "command_line_pref_store_qt.h" - -#include "chrome/common/chrome_switches.h" -#include "components/proxy_config/proxy_config_dictionary.h" -#include "components/proxy_config/proxy_config_pref_names.h" -#include "content/public/common/content_switches.h" -#include <QDebug> - -CommandLinePrefStoreQt::CommandLinePrefStoreQt(const base::CommandLine *commandLine) - : CommandLinePrefStore(commandLine) -{ - - if (commandLine->HasSwitch(switches::kNoProxyServer)) { - SetValue(proxy_config::prefs::kProxy, - std::make_unique<base::Value>(ProxyConfigDictionary::CreateDirect()), - WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); - } else if (commandLine->HasSwitch(switches::kProxyPacUrl)) { - std::string pac_script_url = - commandLine->GetSwitchValueASCII(switches::kProxyPacUrl); - SetValue(proxy_config::prefs::kProxy, - std::make_unique<base::Value>(ProxyConfigDictionary::CreatePacScript( - pac_script_url, false)), - WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); - } else if (commandLine->HasSwitch(switches::kProxyAutoDetect)) { - SetValue(proxy_config::prefs::kProxy, - std::make_unique<base::Value>( - ProxyConfigDictionary::CreateAutoDetect()), - WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); - } else if (commandLine->HasSwitch(switches::kProxyServer)) { - std::string proxy_server = - commandLine->GetSwitchValueASCII(switches::kProxyServer); - std::string bypass_list = - commandLine->GetSwitchValueASCII(switches::kProxyBypassList); - SetValue( - proxy_config::prefs::kProxy, - std::make_unique<base::Value>(ProxyConfigDictionary::CreateFixedServers( - proxy_server, bypass_list)), - WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); - } - - if (commandLine->HasSwitch(switches::kNoProxyServer) && (commandLine->HasSwitch(switches::kProxyAutoDetect) || commandLine->HasSwitch(switches::kProxyServer) || commandLine->HasSwitch(switches::kProxyPacUrl) || commandLine->HasSwitch(switches::kProxyBypassList))) { - qWarning("Additional command-line proxy switches specified when --%s was also specified", - qPrintable(switches::kNoProxyServer)); - } -} - -CommandLinePrefStoreQt::~CommandLinePrefStoreQt() = default; diff --git a/src/core/command_line_pref_store_qt.h b/src/core/command_line_pref_store_qt.h deleted file mode 100644 index a509f8ca9..000000000 --- a/src/core/command_line_pref_store_qt.h +++ /dev/null @@ -1,56 +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$ -** -****************************************************************************/ - -#ifndef COMMAND_LINE_PREF_STORE_QT_H -#define COMMAND_LINE_PREF_STORE_QT_H - -#include "base/command_line.h" -#include "components/prefs/command_line_pref_store.h" - -class CommandLinePrefStoreQt : public CommandLinePrefStore -{ -public: - explicit CommandLinePrefStoreQt(const base::CommandLine *commandLine); - -protected: - ~CommandLinePrefStoreQt() override; - DISALLOW_COPY_AND_ASSIGN(CommandLinePrefStoreQt); -}; - -#endif // COMMAND_LINE_PREF_STORE_QT_H diff --git a/src/core/compositor/compositor.cpp b/src/core/compositor/compositor.cpp index 1578e431e..82a9f7ee4 100644 --- a/src/core/compositor/compositor.cpp +++ b/src/core/compositor/compositor.cpp @@ -46,7 +46,7 @@ #include "components/viz/common/resources/returned_resource.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" -#include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom.h" +#include "services/viz/privileged/mojom/compositing/frame_sink_manager.mojom.h" namespace QtWebEngineCore { @@ -56,7 +56,7 @@ Compositor::Compositor(content::RenderWidgetHost *host) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - m_taskRunner = base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::UI, base::TaskPriority::USER_VISIBLE}); + m_taskRunner = base::CreateSingleThreadTaskRunner({content::BrowserThread::UI, base::TaskPriority::USER_VISIBLE}); m_beginFrameSource = std::make_unique<viz::DelayBasedBeginFrameSource>( std::make_unique<viz::DelayBasedTimeSource>(m_taskRunner.get()), @@ -127,8 +127,7 @@ QSGNode *Compositor::updatePaintNode(QSGNode *oldNode, RenderWidgetHostViewQtDel } m_updatePaintNodeShouldCommit = false; - gfx::PresentationFeedback dummyFeedback(base::TimeTicks::Now(), base::TimeDelta(), gfx::PresentationFeedback::Flags::kVSync); - m_presentations.emplace(m_committedFrame.metadata.frame_token, viz::FrameTimingDetails{dummyFeedback}); + m_presentations.emplace(m_committedFrame.metadata.frame_token, viz::FrameTimingDetails{base::TimeTicks::Now()}); m_resourceTracker->commitResources(); frameNode->commit(m_pendingFrame, m_committedFrame, m_resourceTracker.get(), viewDelegate); @@ -160,8 +159,7 @@ void Compositor::notifyFrameCommitted() void Compositor::sendPresentationFeedback(uint frame_token) { - gfx::PresentationFeedback dummyFeedback(base::TimeTicks::Now(), base::TimeDelta(), gfx::PresentationFeedback::Flags::kVSync); - viz::FrameTimingDetails dummyDetails = {dummyFeedback}; + viz::FrameTimingDetails dummyDetails = {base::TimeTicks::Now()}; m_presentations.emplace(frame_token, dummyDetails); } diff --git a/src/core/compositor/compositor_resource_tracker.cpp b/src/core/compositor/compositor_resource_tracker.cpp index 741c2717c..1e7108571 100644 --- a/src/core/compositor/compositor_resource_tracker.cpp +++ b/src/core/compositor/compositor_resource_tracker.cpp @@ -250,7 +250,7 @@ void CompositorResourceTracker::scheduleRunSubmitCallback() thread_local bool currentThreadIsUi = content::BrowserThread::CurrentlyOn(content::BrowserThread::UI); if (currentThreadIsUi) return runSubmitCallback(); - base::PostTaskWithTraits( + base::PostTask( FROM_HERE, { content::BrowserThread::UI, base::TaskPriority::USER_VISIBLE }, base::BindOnce(&CompositorResourceTracker::runSubmitCallback, m_weakPtrFactory.GetWeakPtr())); diff --git a/src/core/configure.json b/src/core/configure.json index 9059575b9..55e68ab04 100644 --- a/src/core/configure.json +++ b/src/core/configure.json @@ -26,7 +26,6 @@ "webengine-extensions": "boolean", "webengine-webrtc": "boolean", "webengine-geolocation": "boolean", - "webengine-v8-snapshot": "boolean", "webengine-webchannel": "boolean", "webengine-kerberos": "boolean", "alsa": { "type": "boolean", "name": "webengine-alsa" }, @@ -104,7 +103,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" ] @@ -114,14 +112,8 @@ "condition": "config.unix && libs.webengine-alsa", "output": [ "privateFeature" ] }, - "webengine-v8-snapshot": { - "label" : "Use v8 snapshot", - "purpose": "Enables the v8 snapshot, for fast v8 context creation", - "output": [ "privateFeature" ] - }, "webengine-v8-snapshot-support": { "label" : "Building v8 snapshot supported", - "autoDetect": "features.webengine-v8-snapshot", "condition": "!config.unix || !features.cross_compile || arch.arm64 || tests.webengine-host-compiler", "output": [ "privateFeature" ] }, @@ -139,14 +131,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 +151,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 +164,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 +184,6 @@ "webengine-webrtc": { "label": "WebRTC", "purpose": "Provides WebRTC support.", - "section": "WebEngine", "autoDetect": "!features.webengine-embedded-build", "output": [ "privateFeature" ] }, @@ -264,7 +250,7 @@ }, { "type": "warning", - "condition": "config.linux && features.webengine-v8-snapshot && !features.webengine-v8-snapshot-support", + "condition": "config.unix && config.cross_compile && !features.webengine-v8-snapshot-support", "message": "V8 snapshot cannot be built. Most likely, the 32-bit host compiler does not work. Please make sure you have 32-bit devel environment installed." } ], @@ -284,7 +270,6 @@ "webengine-webrtc", "webengine-geolocation", "webengine-webchannel", - "webengine-v8-snapshot", "webengine-kerberos", "webengine-extensions", { @@ -295,7 +280,7 @@ { "type": "feature", "args": "webengine-v8-snapshot-support", - "condition": "config.unix && config.cross_compile && features.webengine-v8-snapshot" + "condition": "config.unix && config.cross_compile" }, { "type": "feature", diff --git a/src/core/content_browser_client_qt.cpp b/src/core/content_browser_client_qt.cpp index 29b6e09ed..a8d9e6ddc 100644 --- a/src/core/content_browser_client_qt.cpp +++ b/src/core/content_browser_client_qt.cpp @@ -41,31 +41,38 @@ #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/guest_view/browser/guest_view_base.h" +#include "components/navigation_interception/intercept_navigation_throttle.h" +#include "components/navigation_interception/navigation_params.h" #include "components/network_hints/browser/network_hints_message_filter.h" +#include "components/spellcheck/spellcheck_buildflags.h" #include "content/browser/renderer_host/render_view_host_delegate.h" #include "content/common/url_schemes.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/child_process_security_policy.h" #include "content/public/browser/client_certificate_delegate.h" +#include "content/public/browser/file_url_loader.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" -#include "content/public/browser/resource_dispatcher_host.h" -#include "content/public/browser/resource_dispatcher_host_delegate.h" +#include "content/public/browser/shared_cors_origin_access_list.h" #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" @@ -74,20 +81,24 @@ #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" #include "printing/buildflags/buildflags.h" #include "qtwebengine/browser/qtwebengine_content_browser_overlay_manifest.h" #include "qtwebengine/browser/qtwebengine_content_renderer_overlay_manifest.h" -#include "qtwebengine/browser/qtwebengine_packaged_service_manifest.h" -#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" +#include "storage/browser/quota/quota_settings.h" #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h" +#include "third_party/blink/public/common/loader/url_loader_throttle.h" #include "third_party/blink/public/mojom/insecure_input/insecure_input_service.mojom.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/ui_base_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" @@ -119,13 +134,14 @@ #include "profile_io_data_qt.h" #include "quota_permission_context_qt.h" #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" #include "api/qwebenginecookiestore.h" #include "api/qwebenginecookiestore_p.h" +#include "api/qwebengineurlscheme.h" #if defined(Q_OS_LINUX) #include "global_descriptors_qt.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) @@ -157,8 +176,14 @@ #include "media/mojo/services/media_service_factory.h" #endif +#if BUILDFLAG(ENABLE_SPELLCHECK) +#include "chrome/browser/spellchecker/spell_check_host_chrome_impl.h" +#include "components/spellcheck/common/spellcheck.mojom.h" +#endif + #include <QGuiApplication> #include <QLocale> +#include <QStandardPaths> #if QT_CONFIG(opengl) # include <QOpenGLContext> # include <QOpenGLExtraFunctions> @@ -271,54 +296,36 @@ std::unique_ptr<content::BrowserMainParts> ContentBrowserClientQt::CreateBrowser return std::make_unique<BrowserMainPartsQt>(); } -void ContentBrowserClientQt::RenderProcessWillLaunch(content::RenderProcessHost* host, - service_manager::mojom::ServiceRequest *service_request) +void ContentBrowserClientQt::RenderProcessWillLaunch(content::RenderProcessHost *host) { 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()); + host->AddFilter(new extensions::ExtensionsGuestViewMessageFilter(id, profile)); #endif //ENABLE_EXTENSIONS bool is_incognito_process = profile->IsOffTheRecord(); qtwebengine::mojom::RendererConfigurationAssociatedPtr renderer_configuration; host->GetChannel()->GetRemoteAssociatedInterface(&renderer_configuration); renderer_configuration->SetInitialConfiguration(is_incognito_process); - - mojo::PendingRemote<service_manager::mojom::Service> service; - *service_request = service.InitWithNewPipeAndPassReceiver(); - service_manager::Identity renderer_identity = host->GetChildIdentity(); - mojo::Remote<service_manager::mojom::ProcessMetadata> metadata; - ServiceQt::GetInstance()->connector()->RegisterServiceInstance( - service_manager::Identity("qtwebengine_renderer", - renderer_identity.instance_group(), - renderer_identity.instance_id(), - base::Token::CreateRandom()), - std::move(service), metadata.BindNewPipeAndPassReceiver()); -} - -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()); } gl::GLShareGroup *ContentBrowserClientQt::GetInProcessGpuShareGroup() @@ -353,9 +360,10 @@ scoped_refptr<content::QuotaPermissionContext> ContentBrowserClientQt::CreateQuo void ContentBrowserClientQt::GetQuotaSettings(content::BrowserContext* context, content::StoragePartition* partition, - storage::OptionalQuotaSettingsCallback callback) + base::OnceCallback<void(base::Optional<storage::QuotaSettings>)> callback) { - storage::GetNominalDynamicSettings(partition->GetPath(), context->IsOffTheRecord(), storage::GetDefaultDiskInfoHelper(), std::move(callback)); + storage::GetNominalDynamicSettings(partition->GetPath(), context->IsOffTheRecord(), + storage::GetDefaultDiskInfoHelper(), std::move(callback)); } // Copied from chrome/browser/ssl/ssl_error_handler.cc: @@ -390,7 +398,6 @@ void ContentBrowserClientQt::AllowCertificateError(content::WebContents *webCont const GURL &request_url, bool is_main_frame_request, bool strict_enforcement, - bool expired_previous_decision, const base::Callback<void(content::CertificateRequestResultType)> &callback) { WebContentsDelegateQt* contentsDelegate = static_cast<WebContentsDelegateQt*>(webContents->GetDelegate()); @@ -459,12 +466,24 @@ 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); + +#if BUILDFLAG(ENABLE_EXTENSIONS) + additional_schemes->push_back(extensions::kExtensionScheme); +#endif +} + +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) @@ -569,6 +588,17 @@ void ContentBrowserClientQt::BindInterfaceRequestFromFrame(content::RenderFrameH m_frameInterfaces->TryBindInterface(interface_name, &interface_pipe); } +void ContentBrowserClientQt::BindHostReceiverForRenderer(content::RenderProcessHost *render_process_host, + mojo::GenericPendingReceiver receiver) +{ +#if BUILDFLAG(ENABLE_SPELLCHECK) + if (auto host_receiver = receiver.As<spellcheck::mojom::SpellCheckHost>()) { + SpellCheckHostChromeImpl::Create(render_process_host->GetID(), std::move(host_receiver)); + return; + } +#endif // BUILDFLAG(ENABLE_SPELLCHECK) +} + void ContentBrowserClientQt::RunServiceInstance(const service_manager::Identity &identity, mojo::PendingReceiver<service_manager::mojom::Service> *receiver) { @@ -582,17 +612,6 @@ void ContentBrowserClientQt::RunServiceInstance(const service_manager::Identity content::ContentBrowserClient::RunServiceInstance(identity, receiver); } -void ContentBrowserClientQt::RunServiceInstanceOnIOThread(const service_manager::Identity &identity, - mojo::PendingReceiver<service_manager::mojom::Service> *receiver) -{ - if (identity.name() == "qtwebengine") { - ServiceQt::GetInstance()->CreateServiceQtRequestHandler().Run(std::move(*receiver)); - return; - } - - content::ContentBrowserClient::RunServiceInstance(identity, receiver); -} - base::Optional<service_manager::Manifest> ContentBrowserClientQt::GetServiceManifestOverlay(base::StringPiece name) { if (name == content::mojom::kBrowserServiceName) @@ -605,9 +624,7 @@ base::Optional<service_manager::Manifest> ContentBrowserClientQt::GetServiceMani std::vector<service_manager::Manifest> ContentBrowserClientQt::GetExtraServiceManifests() { - auto manifests = GetQtWebEnginePackagedServiceManifests(); - manifests.push_back(GetQtWebEngineRendererManifest()); - return manifests; + return { }; } bool ContentBrowserClientQt::CanCreateWindow( @@ -659,19 +676,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 @@ -679,29 +683,29 @@ bool ContentBrowserClientQt::ShouldEnableStrictSiteIsolation() return false; } -bool ContentBrowserClientQt::WillCreateRestrictedCookieManager(network::mojom::RestrictedCookieManagerRole role, - content::BrowserContext *browser_context, - const url::Origin &origin, - bool is_service_worker, - int process_id, - 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; -} - -bool ContentBrowserClientQt::AllowAppCacheOnIO(const GURL &manifest_url, - const GURL &first_party, - content::ResourceContext *context) +bool ContentBrowserClientQt::WillCreateRestrictedCookieManager( + network::mojom::RestrictedCookieManagerRole role, + content::BrowserContext *browser_context, + const url::Origin & /*origin*/, + const GURL & /*site_for_cookies*/, + const url::Origin & /*top_frame_origin*/, + bool is_service_worker, + int process_id, + int routing_id, + mojo::PendingReceiver<network::mojom::RestrictedCookieManager> *receiver) { - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - return ProfileIODataQt::FromResourceContext(context)->canGetCookies(toQt(first_party), toQt(manifest_url)); + mojo::PendingReceiver<network::mojom::RestrictedCookieManager> orig_receiver = std::move(*receiver); + + mojo::PendingRemote<network::mojom::RestrictedCookieManager> target_rcm_remote; + *receiver = target_rcm_remote.InitWithNewPipeAndPassReceiver(); + + ProxyingRestrictedCookieManagerQt::CreateAndBind( + ProfileIODataQt::FromBrowserContext(browser_context), + std::move(target_rcm_remote), + is_service_worker, process_id, routing_id, + std::move(orig_receiver)); + + return false; // only made a proxy, still need the actual impl to be made. } bool ContentBrowserClientQt::AllowAppCache(const GURL &manifest_url, @@ -712,45 +716,68 @@ bool ContentBrowserClientQt::AllowAppCache(const GURL &manifest_url, return static_cast<ProfileQt *>(context)->profileAdapter()->cookieStore()->d_func()->canAccessCookies(toQt(first_party), toQt(manifest_url)); } -bool ContentBrowserClientQt::AllowServiceWorker(const GURL &scope, - const GURL &first_party, - const GURL & /*script_url*/, - content::ResourceContext *context, - base::RepeatingCallback<content::WebContents*()> wc_getter) +bool ContentBrowserClientQt::AllowServiceWorkerOnIO(const GURL &scope, + const GURL &site_for_cookies, + const base::Optional<url::Origin> & /*top_frame_origin*/, + const GURL & /*script_url*/, + content::ResourceContext *context, + base::RepeatingCallback<content::WebContents*()> wc_getter) { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); // FIXME: Chrome also checks if javascript is enabled here to check if has been disabled since the service worker // was started. - return ProfileIODataQt::FromResourceContext(context)->canGetCookies(toQt(first_party), toQt(scope)); + return ProfileIODataQt::FromResourceContext(context)->canGetCookies(toQt(site_for_cookies), toQt(scope)); +} + +bool ContentBrowserClientQt::AllowServiceWorkerOnUI(const GURL &scope, + const GURL &site_for_cookies, + const base::Optional<url::Origin> & /*top_frame_origin*/, + const GURL & /*script_url*/, + content::BrowserContext *context, + base::RepeatingCallback<content::WebContents*()> wc_getter) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + // FIXME: Chrome also checks if javascript is enabled here to check if has been disabled since the service worker + // was started. + return static_cast<ProfileQt *>(context)->profileAdapter()->cookieStore()->d_func()->canAccessCookies(toQt(site_for_cookies), toQt(scope)); } // We control worker access to FS and indexed-db using cookie permissions, this is mirroring Chromium's logic. void ContentBrowserClientQt::AllowWorkerFileSystem(const GURL &url, - content::ResourceContext *context, + content::BrowserContext *context, const std::vector<content::GlobalFrameRoutingId> &/*render_frames*/, - base::Callback<void(bool)> callback) + base::OnceCallback<void(bool)> callback) { - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - callback.Run(ProfileIODataQt::FromResourceContext(context)->canSetCookie(toQt(url), QByteArray(), toQt(url))); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + std::move(callback).Run( + static_cast<ProfileQt *>(context)->profileAdapter()->cookieStore()->d_func()->canAccessCookies(toQt(url), toQt(url))); } bool ContentBrowserClientQt::AllowWorkerIndexedDB(const GURL &url, - content::ResourceContext *context, + content::BrowserContext *context, const std::vector<content::GlobalFrameRoutingId> &/*render_frames*/) { - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - return ProfileIODataQt::FromResourceContext(context)->canSetCookie(toQt(url), QByteArray(), toQt(url)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + return static_cast<ProfileQt *>(context)->profileAdapter()->cookieStore()->d_func()->canAccessCookies(toQt(url), toQt(url)); } static void LaunchURL(const GURL& url, - const content::ResourceRequestInfo::WebContentsGetter& web_contents_getter, + const content::WebContents::Getter& web_contents_getter, ui::PageTransition page_transition, bool is_main_frame, bool has_user_gesture) { Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 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); } @@ -758,36 +785,36 @@ static void LaunchURL(const GURL& url, bool ContentBrowserClientQt::HandleExternalProtocol( const GURL &url, - content::ResourceRequestInfo::WebContentsGetter web_contents_getter, + content::WebContents::Getter web_contents_getter, int child_id, content::NavigationUIData *navigation_data, bool is_main_frame, ui::PageTransition page_transition, bool has_user_gesture, + const base::Optional<url::Origin> &initiating_origin, 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); - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(&LaunchURL, - url, - web_contents_getter, - page_transition, - is_main_frame, - has_user_gesture)); + base::PostTask(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(&LaunchURL, + url, + web_contents_getter, + page_transition, + is_main_frame, + has_user_gesture)); return true; } namespace { // Copied from chrome/browser/chrome_content_browser_client.cc -template<class HandlerRegistry> -class ProtocolHandlerThrottle : public content::URLLoaderThrottle +class ProtocolHandlerThrottle : public blink::URLLoaderThrottle { public: - explicit ProtocolHandlerThrottle(const HandlerRegistry &protocol_handler_registry) + explicit ProtocolHandlerThrottle(ProtocolHandlerRegistry *protocol_handler_registry) : protocol_handler_registry_(protocol_handler_registry) { } @@ -816,34 +843,112 @@ private: *url = translated_url; } - HandlerRegistry protocol_handler_registry_; + ProtocolHandlerRegistry *protocol_handler_registry_; }; } // namespace -std::vector<std::unique_ptr<content::URLLoaderThrottle>> -ContentBrowserClientQt::CreateURLLoaderThrottlesOnIO( - const network::ResourceRequest & /*request*/, content::ResourceContext *resource_context, +std::vector<std::unique_ptr<blink::URLLoaderThrottle>> +ContentBrowserClientQt::CreateURLLoaderThrottles( + const network::ResourceRequest &request, content::BrowserContext *browser_context, const base::RepeatingCallback<content::WebContents *()> & /*wc_getter*/, - content::NavigationUIData * /*navigation_ui_data*/, int /*frame_tree_node_id*/) + content::NavigationUIData * /*navigation_ui_data*/, int frame_tree_node_id) { - 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())); + std::vector<std::unique_ptr<blink::URLLoaderThrottle>> result; + result.push_back(std::make_unique<ProtocolHandlerThrottle>( + 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; } -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) +WebContentsAdapterClient::NavigationType pageTransitionToNavigationType(ui::PageTransition transition) { - std::vector<std::unique_ptr<content::URLLoaderThrottle>> result; - result.push_back(std::make_unique<ProtocolHandlerThrottle<ProtocolHandlerRegistry *>>( - ProtocolHandlerRegistryFactory::GetForBrowserContext(browser_context))); - return result; + 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 ¶ms) +{ + // 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( @@ -875,6 +980,35 @@ bool ContentBrowserClientQt::ShouldUseProcessPerSite(content::BrowserContext* br return ContentBrowserClient::ShouldUseProcessPerSite(browser_context, effective_url); } +bool ContentBrowserClientQt::DoesSiteRequireDedicatedProcess(content::BrowserContext *browser_context, + const GURL &effective_site_url) +{ +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (effective_site_url.SchemeIs(extensions::kExtensionScheme)) + return true; +#endif + return ContentBrowserClient::DoesSiteRequireDedicatedProcess(browser_context, effective_site_url); +} + +bool ContentBrowserClientQt::ShouldUseSpareRenderProcessHost(content::BrowserContext *browser_context, + const GURL &site_url) +{ +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (site_url.SchemeIs(extensions::kExtensionScheme)) + return false; +#endif + return ContentBrowserClient::ShouldUseSpareRenderProcessHost(browser_context, site_url); +} + +bool ContentBrowserClientQt::ShouldTreatURLSchemeAsFirstPartyWhenTopLevel(base::StringPiece scheme) +{ +#if BUILDFLAG(ENABLE_EXTENSIONS) + return scheme == extensions::kExtensionScheme; +#else + return false; +#endif +} + 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 +1021,189 @@ 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); +} + +mojo::Remote<network::mojom::NetworkContext> ContentBrowserClientQt::CreateNetworkContext( + content::BrowserContext *context, + bool in_memory, + const base::FilePath &relative_partition_path) +{ + mojo::Remote<network::mojom::NetworkContext> 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( + network_context.BindNewPipeAndPassReceiver(), 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 +} + +void ContentBrowserClientQt::RegisterNonNetworkWorkerMainResourceURLLoaderFactories(content::BrowserContext *browser_context, + NonNetworkURLLoaderFactoryMap *factories) +{ + DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService)); + Profile *profile = Profile::FromBrowserContext(browser_context); + ProfileAdapter *profileAdapter = static_cast<ProfileQt *>(profile)->profileAdapter(); + + for (const QByteArray &scheme : profileAdapter->customUrlSchemes()) + factories->emplace(scheme.toStdString(), CreateCustomURLLoaderFactory(profileAdapter)); +} + +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)); + + content::RenderFrameHost *frame_host = content::RenderFrameHost::FromID(render_process_id, render_frame_id); + content::WebContents *web_contents = content::WebContents::FromRenderFrameHost(frame_host); + GURL url; + if (web_contents) + url = web_contents->GetVisibleURL(); + + // Install file scheme if necessary: + // FIXME: "extension -> file" will not be needed after switching to using transferable url loaders and guest views. + // FIXME: "qrc -> file" should be reconsidered for Qt6. + bool install_file_scheme = url.SchemeIs("qrc"); +#if BUILDFLAG(ENABLE_EXTENSIONS) + install_file_scheme = install_file_scheme || url.SchemeIs(extensions::kExtensionScheme); +#endif + if (!install_file_scheme && web_contents) { + const auto *settings = static_cast<WebContentsDelegateQt *>(web_contents->GetDelegate())->webEngineSettings(); + if (settings->testAttribute(WebEngineSettings::LocalContentCanAccessFileUrls)) { + for (const auto &local_scheme : url::GetLocalSchemes()) { + if (url.SchemeIs(local_scheme)) { + install_file_scheme = true; + break; + } + } + } + } + + if (install_file_scheme && factories->find(url::kFileScheme) == factories->end()) { + auto file_factory = content::CreateFileURLLoaderFactory(profile->GetPath(), + profile->GetSharedCorsOriginAccessList()); + factories->emplace(url::kFileScheme, std::move(file_factory)); + } + +#if BUILDFLAG(ENABLE_EXTENSIONS) + auto factory = extensions::CreateExtensionURLLoaderFactory(render_process_id, render_frame_id); + if (factory) + factories->emplace(extensions::kExtensionScheme, std::move(factory)); + + 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))); + } +#endif +} + +bool ContentBrowserClientQt::WillCreateURLLoaderFactory( + content::BrowserContext *browser_context, + content::RenderFrameHost *frame, + int render_process_id, + URLLoaderFactoryType type, + const url::Origin &request_initiator, + mojo::PendingReceiver<network::mojom::URLLoaderFactory> *factory_receiver, + mojo::PendingRemote<network::mojom::TrustedURLLoaderHeaderClient> *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 = (type == URLLoaderFactoryType::kNavigation) ? 0 : render_process_id; + + base::PostTask(FROM_HERE, { content::BrowserThread::IO }, + base::BindOnce(&ProxyingURLLoaderFactoryQt::CreateProxy, process_id, + browser_context->GetResourceContext(), + static_cast<content::RenderFrameHostImpl*>(frame), + 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..d828d44b6 100644 --- a/src/core/content_browser_client_qt.h +++ b/src/core/content_browser_client_qt.h @@ -78,21 +78,19 @@ class BrowserMainPartsQt; class ProfileQt; class ShareGroupQtQuick; -class ContentBrowserClientQt : public content::ContentBrowserClient { - +class ContentBrowserClientQt : public content::ContentBrowserClient +{ public: ContentBrowserClientQt(); ~ContentBrowserClientQt(); std::unique_ptr<content::BrowserMainParts> CreateBrowserMainParts(const content::MainFunctionParams&) override; - void RenderProcessWillLaunch(content::RenderProcessHost *host, - service_manager::mojom::ServiceRequest* service_request) override; - void ResourceDispatcherHostCreated() override; + void RenderProcessWillLaunch(content::RenderProcessHost *host) override; gl::GLShareGroup* GetInProcessGpuShareGroup() override; content::MediaObserver* GetMediaObserver() override; scoped_refptr<content::QuotaPermissionContext> CreateQuotaPermissionContext() override; void GetQuotaSettings(content::BrowserContext *context, - content::StoragePartition *partition, - storage::OptionalQuotaSettingsCallback callback) override; + content::StoragePartition *partition, + base::OnceCallback<void(base::Optional<storage::QuotaSettings>)> callback) override; void OverrideWebkitPrefs(content::RenderViewHost *, content::WebPreferences *) override; void AllowCertificateError(content::WebContents *web_contents, int cert_error, @@ -100,7 +98,6 @@ public: const GURL &request_url, bool is_main_frame_request, bool strict_enforcement, - bool expired_previous_decision, const base::Callback<void(content::CertificateRequestResultType)> &callback) override; base::OnceClosure SelectClientCertificate(content::WebContents* web_contents, net::SSLCertRequestInfo* cert_request_info, @@ -116,14 +113,15 @@ 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, mojo::ScopedMessagePipeHandle interface_pipe) override; + void BindHostReceiverForRenderer(content::RenderProcessHost *render_process_host, + mojo::GenericPendingReceiver receiver) override; void RunServiceInstance(const service_manager::Identity &identity, mojo::PendingReceiver<service_manager::mojom::Service> *receiver) override; - void RunServiceInstanceOnIOThread(const service_manager::Identity &identity, - mojo::PendingReceiver<service_manager::mojom::Service> *receiver) override; std::vector<service_manager::Manifest> GetExtraServiceManifests() override; base::Optional<service_manager::Manifest> GetServiceManifestOverlay(base::StringPiece name) override; @@ -142,41 +140,53 @@ public: bool *no_javascript_access) override; bool ShouldEnableStrictSiteIsolation() override; - bool WillCreateRestrictedCookieManager(network::mojom::RestrictedCookieManagerRole role, - content::BrowserContext *browser_context, - const url::Origin& origin, - bool is_service_worker, - int process_id, - int routing_id, - network::mojom::RestrictedCookieManagerRequest *request) override; - - bool AllowAppCacheOnIO(const GURL& manifest_url, - const GURL& first_party, - content::ResourceContext* context) override; - bool AllowAppCache(const GURL& manifest_url, - const GURL& first_party, - content::BrowserContext* context) override; - - bool AllowServiceWorker(const GURL& scope, - const GURL& first_party, - const GURL& script_url, - content::ResourceContext* context, - base::RepeatingCallback<content::WebContents*()> wc_getter) override; + bool WillCreateRestrictedCookieManager( + network::mojom::RestrictedCookieManagerRole role, + content::BrowserContext *browser_context, + const url::Origin &origin, + const GURL &site_for_cookies, + const url::Origin &top_frame_origin, + bool is_service_worker, + int process_id, + int routing_id, + mojo::PendingReceiver<network::mojom::RestrictedCookieManager> *receiver) override; + + bool AllowAppCache(const GURL &manifest_url, + const GURL &first_party, + content::BrowserContext *context) override; + + bool AllowServiceWorkerOnIO(const GURL &scope, + const GURL &site_for_cookies, + const base::Optional<url::Origin> &top_frame_origin, + const GURL &script_url, + content::ResourceContext *context, + base::RepeatingCallback<content::WebContents*()> wc_getter) override; + + bool AllowServiceWorkerOnUI(const GURL &scope, + const GURL &site_for_cookies, + const base::Optional<url::Origin> &top_frame_origin, + const GURL &script_url, + content::BrowserContext *context, + base::RepeatingCallback<content::WebContents*()> wc_getter) override; void AllowWorkerFileSystem(const GURL &url, - content::ResourceContext *context, + content::BrowserContext *context, const std::vector<content::GlobalFrameRoutingId> &render_frames, - base::Callback<void(bool)> callback) override; + base::OnceCallback<void(bool)> callback) override; bool AllowWorkerIndexedDB(const GURL &url, - content::ResourceContext *context, + content::BrowserContext *context, const std::vector<content::GlobalFrameRoutingId> &render_frames) override; #if QT_CONFIG(webengine_geolocation) std::unique_ptr<device::LocationProvider> OverrideSystemLocationProvider() override; #endif bool ShouldIsolateErrorPage(bool in_main_frame) override; - bool ShouldUseProcessPerSite(content::BrowserContext* browser_context, const GURL& effective_url) override; + bool ShouldUseProcessPerSite(content::BrowserContext *browser_context, const GURL &effective_url) override; + bool DoesSiteRequireDedicatedProcess(content::BrowserContext *browser_context, + const GURL &effective_site_url) override; + bool ShouldUseSpareRenderProcessHost(content::BrowserContext *browser_context, const GURL& site_url) override; + bool ShouldTreatURLSchemeAsFirstPartyWhenTopLevel(base::StringPiece scheme) override; #if defined(Q_OS_LINUX) void GetAdditionalMappedFilesForChildProcess(const base::CommandLine& command_line, int child_process_id, content::PosixFileDescriptorInfo* mappings) override; @@ -198,24 +208,46 @@ public: bool HandleExternalProtocol( const GURL &url, - content::ResourceRequestInfo::WebContentsGetter web_contents_getter, + base::Callback<content::WebContents*(void)> web_contents_getter, int child_id, content::NavigationUIData *navigation_data, bool is_main_frame, ui::PageTransition page_transition, bool has_user_gesture, + const base::Optional<url::Origin> &initiating_origin, network::mojom::URLLoaderFactoryPtr *out_factory) override; - std::vector<std::unique_ptr<content::URLLoaderThrottle>> CreateURLLoaderThrottlesOnIO( - 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) override; - - std::vector<std::unique_ptr<content::URLLoaderThrottle>> CreateURLLoaderThrottles( + std::vector<std::unique_ptr<blink::URLLoaderThrottle>> CreateURLLoaderThrottles( const network::ResourceRequest &request, content::BrowserContext *browser_context, const base::RepeatingCallback<content::WebContents *()> &wc_getter, content::NavigationUIData *navigation_ui_data, int frame_tree_node_id) override; + std::vector<std::unique_ptr<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, + URLLoaderFactoryType type, + const url::Origin &request_initiator, + mojo::PendingReceiver<network::mojom::URLLoaderFactory> *factory_receiver, + mojo::PendingRemote<network::mojom::TrustedURLLoaderHeaderClient> *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; + mojo::Remote<network::mojom::NetworkContext> 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; + void RegisterNonNetworkWorkerMainResourceURLLoaderFactories(content::BrowserContext* browser_context, + NonNetworkURLLoaderFactoryMap* factories) override; + static std::string getUserAgent(); std::string GetUserAgent() override { return getUserAgent(); } @@ -223,9 +255,7 @@ 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; std::unique_ptr<service_manager::BinderRegistry> m_frameInterfaces; std::unique_ptr<service_manager::BinderRegistryWithArgs<content::RenderFrameHost*>> m_frameInterfacesParameterized; diff --git a/src/core/content_client_qt.cpp b/src/core/content_client_qt.cpp index 22f3f548f..6f8fc04f9 100644 --- a/src/core/content_client_qt.cpp +++ b/src/core/content_client_qt.cpp @@ -435,11 +435,6 @@ gfx::Image &ContentClientQt::GetNativeImageNamed(int resource_id) return ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(resource_id); } -bool ContentClientQt::IsDataResourceGzipped(int resource_id) -{ - return ui::ResourceBundle::GetSharedInstance().IsGzipped(resource_id); -} - base::string16 ContentClientQt::GetLocalizedString(int message_id) { return l10n_util::GetStringUTF16(message_id); diff --git a/src/core/content_client_qt.h b/src/core/content_client_qt.h index 581805a51..a7fc7432a 100644 --- a/src/core/content_client_qt.h +++ b/src/core/content_client_qt.h @@ -59,7 +59,6 @@ public: base::StringPiece GetDataResource(int, ui::ScaleFactor) override; base::RefCountedMemory* GetDataResourceBytes(int resource_id) override; gfx::Image &GetNativeImageNamed(int resource_id) override; - bool IsDataResourceGzipped(int resource_id) override; base::string16 GetLocalizedString(int message_id) override; }; diff --git a/src/core/content_main_delegate_qt.cpp b/src/core/content_main_delegate_qt.cpp index cb23a5287..ff6bf4366 100644 --- a/src/core/content_main_delegate_qt.cpp +++ b/src/core/content_main_delegate_qt.cpp @@ -79,36 +79,52 @@ ContentClient *GetContentClient(); namespace QtWebEngineCore { +namespace { + // The logic of this function is based on chrome/common/net/net_resource_provider.cc // Copyright (c) 2012 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. -static std::string constructDirHeaderHTML() -{ - base::DictionaryValue dict; - dict.SetString("header", l10n_util::GetStringUTF16(IDS_DIRECTORY_LISTING_HEADER)); - dict.SetString("parentDirText", l10n_util::GetStringUTF16(IDS_DIRECTORY_LISTING_PARENT)); - dict.SetString("headerName", l10n_util::GetStringUTF16(IDS_DIRECTORY_LISTING_NAME)); - dict.SetString("headerSize", l10n_util::GetStringUTF16(IDS_DIRECTORY_LISTING_SIZE)); - dict.SetString("headerDateModified", - l10n_util::GetStringUTF16(IDS_DIRECTORY_LISTING_DATE_MODIFIED)); - dict.SetString("language", l10n_util::GetLanguage(base::i18n::GetConfiguredLocale())); - dict.SetString("listingParsingErrorBoxText", - l10n_util::GetStringFUTF16(IDS_DIRECTORY_LISTING_PARSING_ERROR_BOX_TEXT, - toString16(QCoreApplication::applicationName()))); - dict.SetString("textdirection", base::i18n::IsRTL() ? "rtl" : "ltr"); - std::string html = webui::GetI18nTemplateHtml( - ui::ResourceBundle::GetSharedInstance().GetRawDataResource(IDR_DIR_HEADER_HTML), - &dict); - return html; -} -static base::StringPiece PlatformResourceProvider(int key) { - if (key == IDR_DIR_HEADER_HTML) { - static std::string html_data = constructDirHeaderHTML(); - return base::StringPiece(html_data); +// The net module doesn't have access to this HTML or the strings that need to +// be localized. The Chrome locale will never change while we're running, so +// it's safe to have a static string that we always return a pointer into. +struct LazyDirectoryListerCacher +{ + LazyDirectoryListerCacher() + { + base::DictionaryValue dict; + dict.SetString("header", l10n_util::GetStringUTF16(IDS_DIRECTORY_LISTING_HEADER)); + dict.SetString("parentDirText", l10n_util::GetStringUTF16(IDS_DIRECTORY_LISTING_PARENT)); + dict.SetString("headerName", l10n_util::GetStringUTF16(IDS_DIRECTORY_LISTING_NAME)); + dict.SetString("headerSize", l10n_util::GetStringUTF16(IDS_DIRECTORY_LISTING_SIZE)); + dict.SetString("headerDateModified", + l10n_util::GetStringUTF16(IDS_DIRECTORY_LISTING_DATE_MODIFIED)); + dict.SetString("language", l10n_util::GetLanguage(base::i18n::GetConfiguredLocale())); + dict.SetString("listingParsingErrorBoxText", + l10n_util::GetStringFUTF16(IDS_DIRECTORY_LISTING_PARSING_ERROR_BOX_TEXT, + toString16(QCoreApplication::applicationName()))); + dict.SetString("textdirection", base::i18n::IsRTL() ? "rtl" : "ltr"); + std::string html = + webui::GetI18nTemplateHtml( + ui::ResourceBundle::GetSharedInstance().DecompressDataResource(IDR_DIR_HEADER_HTML), + &dict); + html_data = base::RefCountedString::TakeString(&html); } - return base::StringPiece(); + + scoped_refptr<base::RefCountedMemory> html_data; +}; + +} // namespace + +static scoped_refptr<base::RefCountedMemory> PlatformResourceProvider(int key) +{ + static base::NoDestructor<LazyDirectoryListerCacher> lazy_dir_lister; + + if (IDR_DIR_HEADER_HTML == key) + return lazy_dir_lister->html_data; + + return ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytes(key); } // Logging logic is based on chrome/common/logging_chrome.cc: diff --git a/src/core/content_utility_client_qt.cpp b/src/core/content_utility_client_qt.cpp index 12a538a07..3582e15a7 100644 --- a/src/core/content_utility_client_qt.cpp +++ b/src/core/content_utility_client_qt.cpp @@ -40,6 +40,7 @@ #include "content_utility_client_qt.h" #include "base/no_destructor.h" +#include "mojo/public/cpp/bindings/service_factory.h" #include "services/proxy_resolver/proxy_resolver_factory_impl.h" namespace QtWebEngineCore { @@ -50,12 +51,17 @@ ContentUtilityClientQt::ContentUtilityClientQt() ContentUtilityClientQt::~ContentUtilityClientQt() = default; -void ContentUtilityClientQt::RunIOThreadService(mojo::GenericPendingReceiver *receiver) +auto RunProxyResolver(mojo::PendingReceiver<proxy_resolver::mojom::ProxyResolverFactory> receiver) { - if (auto factory_receiver = receiver->As<proxy_resolver::mojom::ProxyResolverFactory>()) { - static base::NoDestructor<proxy_resolver::ProxyResolverFactoryImpl> factory(std::move(factory_receiver)); - return; - } + return std::make_unique<proxy_resolver::ProxyResolverFactoryImpl>(std::move(receiver)); +} + +mojo::ServiceFactory *ContentUtilityClientQt::GetIOThreadServiceFactory() +{ + static base::NoDestructor<mojo::ServiceFactory> factory { + RunProxyResolver, + }; + return factory.get(); } } // namespace diff --git a/src/core/content_utility_client_qt.h b/src/core/content_utility_client_qt.h index 2a9ecff93..fc1d1eb05 100644 --- a/src/core/content_utility_client_qt.h +++ b/src/core/content_utility_client_qt.h @@ -53,8 +53,7 @@ public: ~ContentUtilityClientQt() override; // content::ContentUtilityClient: - void RunIOThreadService(mojo::GenericPendingReceiver *receiver) override; - + mojo::ServiceFactory *GetIOThreadServiceFactory() override; }; } // namespace diff --git a/src/core/core_chromium.pri b/src/core/core_chromium.pri index 9eab15456..ac118101a 100644 --- a/src/core/core_chromium.pri +++ b/src/core/core_chromium.pri @@ -50,7 +50,6 @@ SOURCES = \ clipboard_qt.cpp \ color_chooser_qt.cpp \ color_chooser_controller.cpp \ - command_line_pref_store_qt.cpp \ common/qt_ipc_logging.cpp \ common/qt_messages.cpp \ common/user_script_data.cpp \ @@ -82,17 +81,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 \ @@ -120,11 +118,12 @@ SOURCES = \ renderer/render_view_observer_qt.cpp \ renderer/render_thread_observer_qt.cpp \ renderer/user_resource_controller.cpp \ + renderer/plugins/loadable_plugin_placeholder_qt.cpp \ + renderer/plugins/plugin_placeholder_qt.cpp \ renderer_host/render_view_observer_host_qt.cpp \ renderer_host/user_resource_controller_host.cpp \ resource_bundle_qt.cpp \ resource_context_qt.cpp \ - service/service_qt.cpp \ touch_handle_drawable_qt.cpp \ touch_selection_controller_client_qt.cpp \ touch_selection_menu_controller.cpp \ @@ -157,7 +156,6 @@ HEADERS = \ client_cert_select_controller.h \ clipboard_change_observer.h \ clipboard_qt.h \ - command_line_pref_store_qt.h \ color_chooser_qt.h \ color_chooser_controller_p.h \ color_chooser_controller.h \ @@ -193,16 +191,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 +215,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 \ @@ -234,11 +231,12 @@ HEADERS = \ renderer/render_view_observer_qt.h \ renderer/render_thread_observer_qt.h \ renderer/user_resource_controller.h \ + renderer/plugins/loadable_plugin_placeholder_qt.h \ + renderer/plugins/plugin_placeholder_qt.h \ renderer_host/render_view_observer_host_qt.h \ renderer_host/user_resource_controller_host.h \ request_controller.h \ resource_context_qt.h \ - service/service_qt.h \ touch_handle_drawable_client.h \ touch_handle_drawable_qt.h \ touch_selection_controller_client_qt.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/devtools_frontend_qt.cpp b/src/core/devtools_frontend_qt.cpp index 1dcbd1e9d..8d325eca6 100644 --- a/src/core/devtools_frontend_qt.cpp +++ b/src/core/devtools_frontend_qt.cpp @@ -130,7 +130,7 @@ public: private: void OnResponseStarted(const GURL &final_url, - const network::ResourceResponseHead &response_head) + const network::mojom::URLResponseHead &response_head) { response_headers_ = response_head.headers; } diff --git a/src/core/download_manager_delegate_qt.cpp b/src/core/download_manager_delegate_qt.cpp index 7049b8273..fe7f7c57b 100644 --- a/src/core/download_manager_delegate_qt.cpp +++ b/src/core/download_manager_delegate_qt.cpp @@ -229,14 +229,12 @@ bool DownloadManagerDelegateQt::DetermineDownloadTarget(download::DownloadItem* } void DownloadManagerDelegateQt::GetSaveDir(content::BrowserContext* browser_context, - base::FilePath* website_save_dir, - base::FilePath* download_save_dir, - bool* skip_dir_check) + base::FilePath* website_save_dir, + base::FilePath* download_save_dir) { static base::FilePath::StringType save_dir = toFilePathString(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); *website_save_dir = base::FilePath(save_dir); *download_save_dir = base::FilePath(save_dir); - *skip_dir_check = true; } void DownloadManagerDelegateQt::ChooseSavePath(content::WebContents *web_contents, diff --git a/src/core/download_manager_delegate_qt.h b/src/core/download_manager_delegate_qt.h index 6acfa42ce..4634790cc 100644 --- a/src/core/download_manager_delegate_qt.h +++ b/src/core/download_manager_delegate_qt.h @@ -77,8 +77,7 @@ public: void GetSaveDir(content::BrowserContext* browser_context, base::FilePath* website_save_dir, - base::FilePath* download_save_dir, - bool* skip_dir_check) override; + base::FilePath* download_save_dir) override; void ChooseSavePath(content::WebContents *web_contents, const base::FilePath &suggested_path, const base::FilePath::StringType &default_extension, diff --git a/src/core/extensions/extension_system_qt.cpp b/src/core/extensions/extension_system_qt.cpp index fbe98099c..a66051fbd 100644 --- a/src/core/extensions/extension_system_qt.cpp +++ b/src/core/extensions/extension_system_qt.cpp @@ -136,7 +136,8 @@ public: // This should return what verification mode is appropriate for the given // extension, if any. - bool ShouldBeVerified(const Extension &extension) override { return false; } + VerifierSourceType GetVerifierSourceType(const Extension &extension) override + { return VerifierSourceType::NONE; } // Should return the public key to use for validating signatures via the two // out parameters. @@ -174,7 +175,7 @@ void ExtensionSystemQt::LoadExtension(std::string extension_id, std::unique_ptr< if (!extension.get()) LOG(ERROR) << error; - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::PostTask(FROM_HERE, {content::BrowserThread::IO}, base::Bind(&InfoMap::AddExtension, base::Unretained(info_map()), base::RetainedRef(extension), @@ -411,7 +412,7 @@ void ExtensionSystemQt::RegisterExtensionWithRequestContexts(const Extension *ex bool incognito_enabled = false; bool notifications_disabled = false; - base::PostTaskWithTraitsAndReply( + base::PostTaskAndReply( FROM_HERE, {BrowserThread::IO}, base::Bind(&InfoMap::AddExtension, info_map(), base::RetainedRef(extension), install_time, incognito_enabled, @@ -422,7 +423,7 @@ void ExtensionSystemQt::RegisterExtensionWithRequestContexts(const Extension *ex void ExtensionSystemQt::UnregisterExtensionWithRequestContexts(const std::string &extension_id, const UnloadedExtensionReason reason) { - base::PostTaskWithTraits( + base::PostTask( FROM_HERE, {BrowserThread::IO}, base::Bind(&InfoMap::RemoveExtension, info_map(), extension_id, reason)); } 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..a91127192 100644 --- a/src/core/extensions/extensions_browser_client_qt.cpp +++ b/src/core/extensions/extensions_browser_client_qt.cpp @@ -66,10 +66,13 @@ #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/cpp/resource_response.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" @@ -102,67 +105,160 @@ void DetermineCharset(const std::string &mime_type, } } -// A request for an extension resource in a Chrome .pak file. These are used -// by component extensions. -class URLRequestResourceBundleJob : public net::URLRequestSimpleJob +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: - URLRequestResourceBundleJob(net::URLRequest *request, net::NetworkDelegate *network_delegate, - const base::FilePath &filename, int resource_id, - const std::string &content_security_policy, bool send_cors_header) - : net::URLRequestSimpleJob(request, network_delegate) - , filename_(filename) - , resource_id_(resource_id) - , weak_factory_(this) + 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 { - // Leave cache headers out of resource bundle requests. - response_info_.headers = extensions::BuildHttpHeaders(content_security_policy, send_cors_header, base::Time()); + NOTREACHED() << "No redirects for local file loads."; } - int GetRefCountedData(std::string *mime_type, std::string *charset, scoped_refptr<base::RefCountedMemory> *data, - net::CompletionOnceCallback callback) const override + // 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 {} + +private: + ResourceBundleFileLoader(const std::string &content_security_policy, bool send_cors_header) : binding_(this) { - const ui::ResourceBundle &rb = ui::ResourceBundle::GetSharedInstance(); - *data = rb.LoadDataResourceBytes(resource_id_); + response_headers_ = extensions::BuildHttpHeaders(content_security_policy, send_cors_header, base::Time()); + } + ~ResourceBundleFileLoader() override = default; - // Add the Content-Length header now that we know the resource length. - response_info_.headers->AddHeader(base::StringPrintf("%s: %s", net::HttpRequestHeaders::kContentLength, - base::NumberToString((*data)->size()).c_str())); + 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(&URLRequestResourceBundleJob::OnMimeTypeRead, weak_factory_.GetWeakPtr(), mime_type, - charset, *data, base::Owned(read_mime_type), std::move(callback))); - - return net::ERR_IO_PENDING; + base::PostTaskAndReplyWithResult( + FROM_HERE, { base::ThreadPool(), 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 GetResponseInfo(net::HttpResponseInfo *info) override { *info = response_info_; } + 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)); -private: - ~URLRequestResourceBundleJob() override {} + uint32_t write_size = data->size(); + MojoResult result = pipe.producer_handle->WriteData(data->front(), &write_size, MOJO_WRITE_DATA_FLAG_NONE); + OnFileWritten(result); + } - void OnMimeTypeRead(std::string *out_mime_type, std::string *charset, scoped_refptr<base::RefCountedMemory> data, - std::string *read_mime_type, net::CompletionOnceCallback callback, bool read_result) + void OnConnectionError() { - response_info_.headers->AddHeader( - base::StringPrintf("%s: %s", net::HttpRequestHeaders::kContentType, read_mime_type->c_str())); - *out_mime_type = *read_mime_type; - DetermineCharset(*read_mime_type, data.get(), charset); - int result = read_result ? net::OK : net::ERR_INVALID_URL; - std::move(callback).Run(result); + client_.reset(); + MaybeDeleteSelf(); } - // We need the filename of the resource to determine the mime type. - base::FilePath filename_; + void OnBindingError() + { + binding_.Close(); + MaybeDeleteSelf(); + } + + void MaybeDeleteSelf() + { + if (!binding_.is_bound() && !client_.is_bound()) + delete this; + } - // The resource to load. - int resource_id_; + 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(); + } - net::HttpResponseInfo response_info_; + mojo::Binding<network::mojom::URLLoader> binding_; + network::mojom::URLLoaderClientPtr client_; + scoped_refptr<net::HttpResponseHeaders> response_headers_; + base::WeakPtrFactory<ResourceBundleFileLoader> weak_factory_{ this }; - mutable base::WeakPtrFactory<URLRequestResourceBundleJob> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(ResourceBundleFileLoader); }; } // namespace @@ -235,38 +331,6 @@ bool ExtensionsBrowserClientQt::CanExtensionCrossIncognito(const Extension *exte return false; } -net::URLRequestJob *ExtensionsBrowserClientQt::MaybeCreateResourceBundleRequestJob(net::URLRequest *request, - net::NetworkDelegate *network_delegate, - const base::FilePath &directory_path, - const std::string &content_security_policy, - bool send_cors_header) -{ - base::FilePath resources_path; - base::FilePath relative_path; - // Try to load extension resources from chrome resource file if - // directory_path is a descendant of resources_path. resources_path - // corresponds to src/chrome/browser/resources in source tree. - if (base::PathService::Get(base::DIR_QT_LIBRARY_DATA, &resources_path) && - // Since component extension resources are included in - // component_extension_resources.pak file in resources_path, calculate - // extension relative path against resources_path. - resources_path.AppendRelativePath(directory_path, &relative_path)) { - base::FilePath request_path = extensions::file_util::ExtensionURLToRelativeFilePath(request->url()); - int resource_id = 0; - if (GetComponentExtensionResourceManager()->IsComponentExtensionResource(directory_path, request_path, &resource_id)) { - relative_path = relative_path.Append(request_path); - relative_path = relative_path.NormalizePathSeparators(); - return new URLRequestResourceBundleJob(request, - network_delegate, - relative_path, - resource_id, - content_security_policy, - send_cors_header); - } - } - return nullptr; -} - // Return the resource relative path and id for the given request. base::FilePath ExtensionsBrowserClientQt::GetBundleResourcePath(const network::ResourceRequest &request, const base::FilePath &extension_resources_path, @@ -307,7 +371,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); } @@ -409,7 +474,7 @@ const ComponentExtensionResourceManager *ExtensionsBrowserClientQt::GetComponent void ExtensionsBrowserClientQt::BroadcastEventToRenderers(events::HistogramValue histogram_value, const std::string &event_name, - std::unique_ptr<base::ListValue> args) + std::unique_ptr<base::ListValue> args, bool dispatch_to_off_the_record_profiles) { NOTIMPLEMENTED(); // TODO : do the event routing diff --git a/src/core/extensions/extensions_browser_client_qt.h b/src/core/extensions/extensions_browser_client_qt.h index 41cb2ce20..1a90bae95 100644 --- a/src/core/extensions/extensions_browser_client_qt.h +++ b/src/core/extensions/extensions_browser_client_qt.h @@ -73,11 +73,6 @@ public: bool IsGuestSession(content::BrowserContext *context) const override; bool IsExtensionIncognitoEnabled(const std::string &extension_id, content::BrowserContext *context) const override; bool CanExtensionCrossIncognito(const Extension *extension, content::BrowserContext *context) const override; - net::URLRequestJob *MaybeCreateResourceBundleRequestJob(net::URLRequest *request, - net::NetworkDelegate *network_delegate, - const base::FilePath &directory_path, - const std::string &content_security_policy, - bool send_cors_header) override; bool AllowCrossRendererResourceLoad(const GURL &url, content::ResourceType resource_type, ui::PageTransition page_transition, @@ -105,7 +100,8 @@ public: GetComponentExtensionResourceManager() override; void BroadcastEventToRenderers(events::HistogramValue histogram_value, const std::string &event_name, - std::unique_ptr<base::ListValue> args) override; + std::unique_ptr<base::ListValue> args, + bool dispatch_to_off_the_record_profiles) override; ExtensionCache *GetExtensionCache() override; bool IsBackgroundUpdateAllowed() override; bool IsMinBrowserVersionSupported(const std::string &min_version) override; diff --git a/src/core/file_picker_controller.cpp b/src/core/file_picker_controller.cpp index 62e02e126..3c81a977c 100644 --- a/src/core/file_picker_controller.cpp +++ b/src/core/file_picker_controller.cpp @@ -100,10 +100,19 @@ void FilePickerController::accepted(const QStringList &files) void FilePickerController::accepted(const QVariant &files) { - if (!files.canConvert(QVariant::StringList)) + QStringList stringList; + + if (files.canConvert(QVariant::StringList)) { + stringList = files.toStringList(); + } else if (files.canConvert<QList<QUrl> >()) { + const QList<QUrl> urls = files.value<QList<QUrl>>(); + for (const QUrl &url : urls) + stringList.append(url.toLocalFile()); + } else { qWarning("An unhandled type '%s' was provided in FilePickerController::accepted(QVariant)", files.typeName()); + } - accepted(files.toStringList()); + accepted(stringList); } void FilePickerController::rejected() diff --git a/src/core/login_delegate_qt.cpp b/src/core/login_delegate_qt.cpp index f63252112..1b9c22191 100644 --- a/src/core/login_delegate_qt.cpp +++ b/src/core/login_delegate_qt.cpp @@ -48,9 +48,9 @@ #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_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" @@ -82,7 +82,7 @@ LoginDelegateQt::LoginDelegateQt(const net::AuthChallengeInfo &authInfo, , m_auth_required_callback(std::move(auth_required_callback)) , m_weakFactory(this) { - base::PostTaskWithTraits( + base::PostTask( FROM_HERE, { content::BrowserThread::UI }, base::BindOnce(&LoginDelegateQt::triggerDialog, m_weakFactory.GetWeakPtr())); } @@ -153,7 +153,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/login_delegate_qt.h b/src/core/login_delegate_qt.h index 20f302988..116fe3c56 100644 --- a/src/core/login_delegate_qt.h +++ b/src/core/login_delegate_qt.h @@ -42,7 +42,6 @@ #include "content/public/browser/content_browser_client.h" #include "content/public/browser/login_delegate.h" -#include "content/public/browser/resource_request_info.h" #include "content/public/browser/web_contents_observer.h" #include "url/gurl.h" diff --git a/src/core/media_capture_devices_dispatcher.cpp b/src/core/media_capture_devices_dispatcher.cpp index 7bf499917..2293c424a 100644 --- a/src/core/media_capture_devices_dispatcher.cpp +++ b/src/core/media_capture_devices_dispatcher.cpp @@ -280,9 +280,9 @@ void MediaCaptureDevicesDispatcher::handleMediaAccessPermissionResponse(content: // Post a task to process next queued request. It has to be done // asynchronously to make sure that calling infobar is not destroyed until // after this function returns. - base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI}, - base::BindOnce(&MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest, - base::Unretained(this), webContents)); + base::PostTask(FROM_HERE, {BrowserThread::UI}, + base::BindOnce(&MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest, + base::Unretained(this), webContents)); } if (devices.empty()) @@ -368,7 +368,7 @@ void MediaCaptureDevicesDispatcher::processDesktopCaptureAccessRequest(content:: // Resolve DesktopMediaID for the specified device id. mediaId = content::DesktopStreamsRegistry::GetInstance()->RequestMediaForStreamId( request.requested_video_device_id, main_frame->GetProcess()->GetID(), - main_frame->GetRoutingID(), request.security_origin, + main_frame->GetRoutingID(), url::Origin::Create(request.security_origin), &originalExtensionName, content::kRegistryStreamTypeDesktop); } @@ -444,10 +444,10 @@ void MediaCaptureDevicesDispatcher::getDefaultDevices(const std::string &audioDe void MediaCaptureDevicesDispatcher::OnMediaRequestStateChanged(int render_process_id, int render_frame_id, int page_request_id, const GURL &security_origin, blink::mojom::MediaStreamType stream_type, content::MediaRequestState state) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI}, - base::BindOnce(&MediaCaptureDevicesDispatcher::updateMediaRequestStateOnUIThread, - base::Unretained(this), render_process_id, render_frame_id, - page_request_id, security_origin, stream_type, state)); + base::PostTask(FROM_HERE, {BrowserThread::UI}, + base::BindOnce(&MediaCaptureDevicesDispatcher::updateMediaRequestStateOnUIThread, + base::Unretained(this), render_process_id, render_frame_id, + page_request_id, security_origin, stream_type, state)); } void MediaCaptureDevicesDispatcher::updateMediaRequestStateOnUIThread(int render_process_id, diff --git a/src/core/net/client_cert_override.cpp b/src/core/net/client_cert_override.cpp index afb7ab5af..f33515f63 100644 --- a/src/core/net/client_cert_override.cpp +++ b/src/core/net/client_cert_override.cpp @@ -142,7 +142,7 @@ void ClientCertOverrideStore::GetClientCerts(const net::SSLCertRequestInfo &cert { #if QT_CONFIG(ssl) // Access the user-provided data from the UI thread, but return on whatever thread this is. - if (base::PostTaskWithTraitsAndReplyWithResult( + if (base::PostTaskAndReplyWithResult( FROM_HERE, { content::BrowserThread::UI }, base::BindOnce(&ClientCertOverrideStore::GetClientCertsOnUIThread, base::Unretained(this), std::cref(cert_request_info)), diff --git a/src/core/net/cookie_monster_delegate_qt.cpp b/src/core/net/cookie_monster_delegate_qt.cpp index dad9a8db5..cf114406b 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,24 @@ 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::CookieChangeInfo &change) override + { + m_delegate->OnCookieChanged(change); + } + +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()); @@ -59,44 +79,34 @@ static GURL sourceUrlForCookie(const QNetworkCookie &cookie) } CookieMonsterDelegateQt::CookieMonsterDelegateQt() - : m_client(0) - , m_cookieMonster(nullptr) + : m_client(nullptr) + , m_listener(new CookieChangeListener(this)) + , m_receiver(m_listener.get()) { } CookieMonsterDelegateQt::~CookieMonsterDelegateQt() { - } void CookieMonsterDelegateQt::AddStore(net::CookieStore *store) { std::unique_ptr<net::CookieChangeSubscription> sub = store->GetChangeDispatcher().AddCallbackForAllChanges( - base::Bind(&CookieMonsterDelegateQt::OnCookieChanged, - // this object's destruction will deregister the subscription. - base::Unretained(this))); + base::BindRepeating(&CookieMonsterDelegateQt::OnCookieChanged, + // this object's destruction will deregister the subscription. + base::Unretained(this))); m_subscriptions.push_back(std::move(sub)); } 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 +115,18 @@ 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::CanonicalCookie::CookieInclusionStatus inclusion; + auto canonCookie = net::CanonicalCookie::Create(gurl, cookie_line, base::Time::Now(), base::nullopt, &inclusion); net::CookieOptions options; - options.set_include_httponly(); - - if (m_cookieMonster) - m_cookieMonster->SetCookieWithOptionsAsync(url, cookie_line, options, std::move(callback)); + if (!inclusion.HasExclusionReason(net::CanonicalCookie::CookieInclusionStatus::EXCLUDE_HTTP_ONLY)) + options.set_include_httponly(); + 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())); -} - -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()); + 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::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,32 @@ 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))); -} - -void CookieMonsterDelegateQt::DeleteAllOnIOThread(net::CookieMonster::DeleteCallback callback) -{ - if (m_cookieMonster) - m_cookieMonster->DeleteAllAsync(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::setCookieMonster(net::CookieMonster *monster) +void CookieMonsterDelegateQt::setMojoCookieManager(network::mojom::CookieManagerPtrInfo cookie_manager_info) { - if (monster == m_cookieMonster) - return; + if (m_mojoCookieManager.is_bound()) + unsetMojoCookieManager(); - m_subscriptions.clear(); - if (monster) - AddStore(monster); + Q_ASSERT(!m_mojoCookieManager.is_bound()); + Q_ASSERT(!m_receiver.is_bound()); - m_cookieMonster = monster; + m_mojoCookieManager.Bind(std::move(cookie_manager_info)); - if (!m_client) - return; + m_mojoCookieManager->AddGlobalChangeListener(m_receiver.BindNewPipeAndPassRemote()); - if (monster) + if (m_client) m_client->d_func()->processPendingUserCookies(); - else - m_client->d_func()->rejectPendingUserCookies(); +} + +void CookieMonsterDelegateQt::unsetMojoCookieManager() +{ + m_receiver.reset(); + m_mojoCookieManager.reset(); } void CookieMonsterDelegateQt::setClient(QWebEngineCookieStore *client) @@ -246,39 +216,24 @@ bool CookieMonsterDelegateQt::canGetCookies(const QUrl &firstPartyUrl, const QUr return m_client->d_func()->canAccessCookies(firstPartyUrl, url); } -void CookieMonsterDelegateQt::OnCookieChanged(const net::CanonicalCookie &cookie, net::CookieChangeCause cause) +void CookieMonsterDelegateQt::OnCookieChanged(const net::CookieChangeInfo &change) { if (!m_client) return; - m_client->d_func()->onCookieChanged(toQt(cookie), cause != net::CookieChangeCause::INSERTED); + m_client->d_func()->onCookieChanged(toQt(change.cookie), change.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); @@ -287,8 +242,7 @@ void CookieMonsterDelegateQt::GetAllCookiesCallbackOnUIThread(qint64 callbackId, void CookieMonsterDelegateQt::SetCookieCallbackOnUIThread(qint64 callbackId, net::CanonicalCookie::CookieInclusionStatus status) { if (m_client) - m_client->d_func()->onSetCallbackResult(callbackId, - status == net::CanonicalCookie::CookieInclusionStatus::INCLUDE); + m_client->d_func()->onSetCallbackResult(callbackId, status.IsInclude()); } void CookieMonsterDelegateQt::DeleteCookiesCallbackOnUIThread(qint64 callbackId, uint numCookies) diff --git a/src/core/net/cookie_monster_delegate_qt.h b/src/core/net/cookie_monster_delegate_qt.h index 23b803790..6caaeea94 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/receiver.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::Receiver<network::mojom::CookieChangeListener> m_receiver; public: CookieMonsterDelegateQt(); ~CookieMonsterDelegateQt(); @@ -92,31 +104,19 @@ 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; void AddStore(net::CookieStore *store); - void OnCookieChanged(const net::CanonicalCookie &cookie, net::CookieChangeCause cause); + void OnCookieChanged(const net::CookieChangeInfo &change); 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_url_loader_factory.cpp b/src/core/net/custom_url_loader_factory.cpp new file mode 100644 index 000000000..a96b35696 --- /dev/null +++ b/src/core/net/custom_url_loader_factory.cpp @@ -0,0 +1,484 @@ +/**************************************************************************** +** +** 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.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/receiver_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/cpp/resource_response.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::PostTask(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 { } + +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 (ParseRange(m_request.headers)) + m_firstBytePosition = m_byteRange.first_byte_position(); + +// m_taskRunner->PostTask(FROM_HERE, + base::PostTask(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::PostTask(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_totalSize = size; + if (m_byteRange.IsValid()) { + if (!m_byteRange.ComputeBounds(size)) { + CompleteWithFailure(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE); + } else { + m_maxBytesToRead = m_byteRange.last_byte_position() - m_byteRange.first_byte_position() + 1; + m_head.content_length = m_maxBytesToRead; + } + } else { + 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 { + if (m_byteRange.IsValid() && m_totalSize > 0) { + headers += "HTTP/1.1 206 Partial Content\n"; + headers += net::HttpResponseHeaders::kContentRange; + headers += base::StringPrintf(": bytes %lld-%lld/%lld", + qlonglong{m_byteRange.first_byte_position()}, + qlonglong{m_byteRange.last_byte_position()}, + qlonglong{m_totalSize}); + headers += "\n"; + } else { + headers += "HTTP/1.1 200 OK\n"; + } + if (m_mimeType.size() > 0) { + headers += net::HttpRequestHeaders::kContentType; + headers += base::StringPrintf(": %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, + 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; + if (m_maxBytesToRead > 0 && m_maxBytesToRead <= int64_t{std::numeric_limits<uint32_t>::max()}) + bufferSize = std::min(bufferSize, uint32_t(m_maxBytesToRead)); + + 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() || (m_maxBytesToRead > 0 && m_totalBytesRead >= m_maxBytesToRead)) { + 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 + } + bool ParseRange(const net::HttpRequestHeaders &headers) + { + std::string range_header; + if (headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header)) { + std::vector<net::HttpByteRange> ranges; + if (net::HttpUtil::ParseRangeHeader(range_header, &ranges)) { + // Chromium doesn't support multirange requests. + if (ranges.size() == 1) { + m_byteRange = ranges[0]; + return true; + } + } + } + return false; + } + 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; + + net::HttpByteRange m_byteRange; + int64_t m_totalSize = 0; + int64_t m_maxBytesToRead = -1; + 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(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) override + { + m_receivers.Add(this, std::move(receiver)); + } + + const scoped_refptr<base::SequencedTaskRunner> m_taskRunner; + mojo::ReceiverSet<network::mojom::URLLoaderFactory> m_receivers; + 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..2b6158f4a --- /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::PostTask(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..1fc9e654f 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 "third_party/blink/public/common/loader/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 blink::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..a0aaf0c05 --- /dev/null +++ b/src/core/net/proxy_config_monitor.cpp @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** 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::CreateSingleThreadTaskRunner({ 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) +{ + mojo::PendingRemote<network::mojom::ProxyConfigClient> proxy_config_client; + network_context_params->proxy_config_client_receiver = + proxy_config_client.InitWithNewPipeAndPassReceiver(); + proxy_config_client_set_.Add(std::move(proxy_config_client)); + + poller_receiver_set_.Add(this, + network_context_params->proxy_config_poller_client.InitWithNewPipeAndPassReceiver()); + + 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)); + for (const auto &proxy_config_client : proxy_config_client_set_) { + 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..fda6a6fb9 --- /dev/null +++ b/src/core/net/proxy_config_monitor.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** 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/receiver_set.h" +#include "mojo/public/cpp/bindings/remote_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::ReceiverSet<network::mojom::ProxyConfigPollerClient> poller_receiver_set_; + mojo::RemoteSet<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..7ee6c2a15 --- /dev/null +++ b/src/core/net/proxying_restricted_cookie_manager_qt.cpp @@ -0,0 +1,241 @@ +/**************************************************************************** +** +** 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/self_owned_receiver.h" + +namespace QtWebEngineCore { + +class ProxyingRestrictedCookieManagerListenerQt : public network::mojom::CookieChangeListener { +public: + ProxyingRestrictedCookieManagerListenerQt(const GURL &url, + const GURL &site_for_cookies, + const url::Origin &top_frame_origin, + base::WeakPtr<ProxyingRestrictedCookieManagerQt> restricted_cookie_manager, + mojo::PendingRemote<network::mojom::CookieChangeListener> client_listener) + : url_(url) + , site_for_cookies_(site_for_cookies) + , top_frame_origin_(top_frame_origin) + , restricted_cookie_manager_(restricted_cookie_manager) + , client_listener_(std::move(client_listener)) + {} + + void OnCookieChange(const net::CookieChangeInfo &change) override + { + if (restricted_cookie_manager_ && restricted_cookie_manager_->allowCookies(url_, site_for_cookies_)) + client_listener_->OnCookieChange(change); + } + +private: + const GURL url_; + const GURL site_for_cookies_; + const url::Origin top_frame_origin_; + base::WeakPtr<ProxyingRestrictedCookieManagerQt> restricted_cookie_manager_; + mojo::Remote<network::mojom::CookieChangeListener> client_listener_; +}; + +// static +void ProxyingRestrictedCookieManagerQt::CreateAndBind(ProfileIODataQt *profileIoData, + mojo::PendingRemote<network::mojom::RestrictedCookieManager> underlying_rcm, + bool is_service_worker, + int process_id, + int frame_id, + mojo::PendingReceiver<network::mojom::RestrictedCookieManager> receiver) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + base::PostTask(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&ProxyingRestrictedCookieManagerQt::CreateAndBindOnIoThread, + profileIoData, + std::move(underlying_rcm), + is_service_worker, process_id, frame_id, + std::move(receiver))); +} + + +// static +void ProxyingRestrictedCookieManagerQt::CreateAndBindOnIoThread(ProfileIODataQt *profileIoData, + mojo::PendingRemote<network::mojom::RestrictedCookieManager> underlying_rcm, + bool is_service_worker, + int process_id, + int frame_id, + mojo::PendingReceiver<network::mojom::RestrictedCookieManager> receiver) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + auto wrapper = base::WrapUnique(new ProxyingRestrictedCookieManagerQt( + profileIoData->getWeakPtrOnIOThread(), + std::move(underlying_rcm), + is_service_worker, process_id, frame_id)); + mojo::MakeSelfOwnedReceiver(std::move(wrapper), std::move(receiver)); +} + +ProxyingRestrictedCookieManagerQt::ProxyingRestrictedCookieManagerQt( + base::WeakPtr<ProfileIODataQt> profileIoData, + mojo::PendingRemote<network::mojom::RestrictedCookieManager> 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, + const url::Origin &top_frame_origin, + 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, top_frame_origin, 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, const url::Origin &top_frame_origin, + SetCanonicalCookieCallback callback) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + if (allowCookies(url, site_for_cookies)) { + underlying_restricted_cookie_manager_->SetCanonicalCookie(cookie, url, site_for_cookies, top_frame_origin, std::move(callback)); + } else { + std::move(callback).Run(false); + } +} + +void ProxyingRestrictedCookieManagerQt::AddChangeListener(const GURL &url, + const GURL &site_for_cookies, + const url::Origin &top_frame_origin, + mojo::PendingRemote<network::mojom::CookieChangeListener> listener, + AddChangeListenerCallback callback) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + mojo::PendingRemote<network::mojom::CookieChangeListener> proxy_listener_remote; + auto proxy_listener = + std::make_unique<ProxyingRestrictedCookieManagerListenerQt>( + url, site_for_cookies, top_frame_origin, + weak_factory_.GetWeakPtr(), + std::move(listener)); + + mojo::MakeSelfOwnedReceiver(std::move(proxy_listener), + proxy_listener_remote.InitWithNewPipeAndPassReceiver()); + + underlying_restricted_cookie_manager_->AddChangeListener(url, site_for_cookies, top_frame_origin, std::move(proxy_listener_remote), std::move(callback)); +} + +void ProxyingRestrictedCookieManagerQt::SetCookieFromString(const GURL &url, + const GURL &site_for_cookies, + const url::Origin &top_frame_origin, + 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, top_frame_origin, cookie, std::move(callback)); + } else { + std::move(callback).Run(); + } +} + +void ProxyingRestrictedCookieManagerQt::GetCookiesString(const GURL &url, + const GURL &site_for_cookies, + const url::Origin &top_frame_origin, + GetCookiesStringCallback callback) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + if (allowCookies(url, site_for_cookies)) { + underlying_restricted_cookie_manager_->GetCookiesString(url, site_for_cookies, top_frame_origin, std::move(callback)); + } else { + std::move(callback).Run(""); + } +} + +void ProxyingRestrictedCookieManagerQt::CookiesEnabledFor(const GURL &url, + const GURL &site_for_cookies, + const url::Origin & /*top_frame_origin*/, + 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..b682075ed 100644 --- a/src/core/net/restricted_cookie_manager_qt.h +++ b/src/core/net/proxying_restricted_cookie_manager_qt.h @@ -37,64 +37,91 @@ ** ****************************************************************************/ -#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 "mojo/public/cpp/bindings/remote.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, + mojo::PendingRemote<network::mojom::RestrictedCookieManager> underlying_rcm, bool is_service_worker, - int32_t process_id, - int32_t frame_id); - ~RestrictedCookieManagerQt() override; + int process_id, + int frame_id, + mojo::PendingReceiver<network::mojom::RestrictedCookieManager> receiver); + + ~ProxyingRestrictedCookieManagerQt() override; // network::mojom::RestrictedCookieManager interface: void GetAllForUrl(const GURL &url, const GURL &site_for_cookies, + const url::Origin &top_frame_origin, network::mojom::CookieManagerGetOptionsPtr options, GetAllForUrlCallback callback) override; void SetCanonicalCookie(const net::CanonicalCookie& cookie, const GURL &url, const GURL &site_for_cookies, + const url::Origin &top_frame_origin, SetCanonicalCookieCallback callback) override; void AddChangeListener(const GURL &url, const GURL &site_for_cookies, - network::mojom::CookieChangeListenerPtr listener, + const url::Origin &top_frame_origin, + mojo::PendingRemote<network::mojom::CookieChangeListener> listener, AddChangeListenerCallback callback) override; - + void SetCookieFromString(const GURL &url, + const GURL &site_for_cookies, + const url::Origin &top_frame_origin, + const std::string &cookie, + SetCookieFromStringCallback callback) override; void GetCookiesString(const GURL &url, const GURL &site_for_cookies, + const url::Origin &top_frame_origin, GetCookiesStringCallback callback) override; - void CookiesEnabledFor(const GURL &url, const GURL &site_for_cookies, + const url::Origin &top_frame_origin, CookiesEnabledForCallback callback) override; // Internal: bool allowCookies(const GURL &url, const GURL &site_for_cookies) const; private: + ProxyingRestrictedCookieManagerQt(base::WeakPtr<ProfileIODataQt> profileIoData, + mojo::PendingRemote<network::mojom::RestrictedCookieManager> underlying_rcm, + bool is_service_worker, + int32_t process_id, + int32_t frame_id); + + static void CreateAndBindOnIoThread(ProfileIODataQt *profileIoData, + mojo::PendingRemote<network::mojom::RestrictedCookieManager> underlying_rcm, + bool is_service_worker, + int process_id, + int frame_id, + mojo::PendingReceiver<network::mojom::RestrictedCookieManager> receiver); + base::WeakPtr<ProfileIODataQt> m_profileIoData; - base::WeakPtrFactory<RestrictedCookieManagerQt> weak_factory_; + mojo::Remote<network::mojom::RestrictedCookieManager> 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..a2cb268b0 --- /dev/null +++ b/src/core/net/proxying_url_loader_factory_qt.cpp @@ -0,0 +1,558 @@ +/**************************************************************************** +** +** 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/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 GURL &top_document_url, + 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(network::mojom::URLResponseHeadPtr head) override; + void OnReceiveRedirect(const net::RedirectInfo &redirect_info, network::mojom::URLResponseHeadPtr 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 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; + QUrl m_originalUrl; + GURL m_topDocumentUrl; + + network::ResourceRequest request_; + network::ResourceResponseHead current_response_; + + const net::MutableNetworkTrafficAnnotationTag traffic_annotation_; + + QWebEngineUrlRequestInfo m_requestInfo; + 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 GURL &top_document_url, + 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) + , m_topDocumentUrl(top_document_url) + , 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() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + content::ResourceType resourceType = content::ResourceType(request_.resource_type); + WebContentsAdapterClient::NavigationType navigationType = + pageTransitionToNavigationType(ui::PageTransition(request_.transition_type)); + + m_originalUrl = toQt(request_.url); + + const QUrl initiator = request_.request_initiator.has_value() ? toQt(request_.request_initiator->GetURL()) : QUrl(); + + QUrl firstPartyUrl; + if (!m_topDocumentUrl.is_empty()) + firstPartyUrl = toQt(m_topDocumentUrl); + else + firstPartyUrl = toQt(request_.site_for_cookies); // m_topDocumentUrl can be empty for the main-frame. + + QWebEngineUrlRequestInfoPrivate *infoPrivate = + new QWebEngineUrlRequestInfoPrivate(toQt(resourceType), toQt(navigationType), + m_originalUrl, firstPartyUrl, initiator, + QByteArray::fromStdString(request_.method)); + m_requestInfo = QWebEngineUrlRequestInfo(infoPrivate); + + if (m_profileData && m_profileData->isInterceptorDeprecated()) { + QWebEngineUrlRequestInterceptor *interceptor = m_profileData->acquireInterceptor(); + if (interceptor && m_profileData->isInterceptorDeprecated()) + interceptor->interceptRequest(m_requestInfo); + m_profileData->releaseInterceptor(); + } + + if (m_requestInfo.changed()) { + ContinueAfterIntercept(); + } else { + // FIXME: unretained post? + base::PostTask(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(&InterceptedRequest::InterceptOnUIThread, base::Unretained(this))); + } +} + +void InterceptedRequest::InterceptOnUIThread() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + 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) { + if (m_profileData) { + QWebEngineUrlRequestInterceptor *interceptor = m_profileData->requestInterceptor(); + if (interceptor && !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); + } + base::PostTask(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&InterceptedRequest::ContinueAfterIntercept, m_weakPtr)); +} + +void InterceptedRequest::ContinueAfterIntercept() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + if (m_requestInfo.changed()) { + if (m_requestInfo.d_ptr->shouldBlockRequest) + return SendErrorAndCompleteImmediately(net::ERR_BLOCKED_BY_CLIENT); + if (m_requestInfo.requestUrl() != m_originalUrl) { + 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, + first_party_url_policy, request_.referrer_policy, + request_.referrer.spec(), net::HTTP_TEMPORARY_REDIRECT, + toGurl(m_requestInfo.requestUrl()), base::nullopt, + false /*insecure_scheme_was_upgraded*/); + + // FIXME: Should probably create a new header. + current_response_.encoded_data_length = 0; + 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; + target_client_->OnReceiveRedirect(redirectInfo, current_response_); + return; + } + + 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")) { + request_.referrer = GURL(header.value().toStdString()); + } else { + request_.headers.SetHeader(h, header.value().toStdString()); + } + } + } + } + + 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(network::mojom::URLResponseHeadPtr head) +{ + current_response_ = head; + + target_client_->OnReceiveResponse(std::move(head)); +} + +void InterceptedRequest::OnReceiveRedirect(const net::RedirectInfo &redirect_info, network::mojom::URLResponseHeadPtr head) +{ + // TODO(timvolodine): handle redirect override. + request_was_redirected_ = true; + current_response_ = head; + target_client_->OnReceiveRedirect(redirect_info, std::move(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::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, + content::RenderFrameHostImpl *host, + mojo::PendingReceiver<network::mojom::URLLoaderFactory> loader_receiver, + network::mojom::URLLoaderFactoryPtrInfo target_factory_info) + : m_processId(process_id), m_resourceContext(resourceContext), m_renderFrameHost(host), 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_proxyReceivers.Add(this, std::move(loader_receiver)); + m_proxyReceivers.set_disconnect_handler( + base::BindRepeating(&ProxyingURLLoaderFactoryQt::OnProxyBindingError, m_weakFactory.GetWeakPtr())); +} + +ProxyingURLLoaderFactoryQt::~ProxyingURLLoaderFactoryQt() +{ + m_weakFactory.InvalidateWeakPtrs(); +} + +// static +void ProxyingURLLoaderFactoryQt::CreateProxy(int process_id, + content::ResourceContext *resourceContext, + content::RenderFrameHostImpl *host, + mojo::PendingReceiver<network::mojom::URLLoaderFactory> loader_receiver, + network::mojom::URLLoaderFactoryPtrInfo target_factory_info) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + // Will manage its own lifetime + new ProxyingURLLoaderFactoryQt(process_id, resourceContext, host, std::move(loader_receiver), 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)); + + // Follows a similar path to the root as RenderFrameHostImpl::CalculateSiteForCookies() + GURL top_document_url; + if (m_renderFrameHost) + top_document_url = m_renderFrameHost->frame_tree_node()->frame_tree()->root()->current_frame_host()->GetLastCommittedURL(); + else + LOG(INFO) << "ProxyingURLLoaderFactoryQt::CreateLoaderAndStart() - null m_renderFrameHost, shouldn't happen"; + + // Will manage its own lifetime + InterceptedRequest *req = new InterceptedRequest(m_processId, request_id, routing_id, options, request, + top_document_url, + 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_proxyReceivers.empty()) + delete this; +} + +void ProxyingURLLoaderFactoryQt::Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + m_proxyReceivers.Add(this, std::move(receiver)); +} + +} // 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..4d913f545 --- /dev/null +++ b/src/core/net/proxying_url_loader_factory_qt.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** 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/receiver_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 RenderFrameHostImpl; +class ResourceContext; +} + +namespace QtWebEngineCore { + +class ProxyingURLLoaderFactoryQt : public network::mojom::URLLoaderFactory +{ +public: + ProxyingURLLoaderFactoryQt(int process_id, content::ResourceContext *resourceContext, + content::RenderFrameHostImpl *host, + mojo::PendingReceiver<network::mojom::URLLoaderFactory> loader_receiver, + network::mojom::URLLoaderFactoryPtrInfo target_factory_info); + + ~ProxyingURLLoaderFactoryQt() override; + + static void CreateProxy(int process_id, content::ResourceContext *resourceContext, + content::RenderFrameHostImpl *host, + mojo::PendingReceiver<network::mojom::URLLoaderFactory> loader_receiver, + 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(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) override; + +private: + void OnTargetFactoryError(); + void OnProxyBindingError(); + + const int m_processId; + mojo::ReceiverSet<network::mojom::URLLoaderFactory> m_proxyReceivers; + network::mojom::URLLoaderFactoryPtr m_targetFactory; + + content::ResourceContext *m_resourceContext; + content::RenderFrameHostImpl *m_renderFrameHost; + + 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/ssl_host_state_delegate_qt.cpp b/src/core/net/ssl_host_state_delegate_qt.cpp index 0885475be..f327717e9 100644 --- a/src/core/net/ssl_host_state_delegate_qt.cpp +++ b/src/core/net/ssl_host_state_delegate_qt.cpp @@ -105,8 +105,7 @@ void SSLHostStateDelegateQt::Clear(const base::Callback<bool(const std::string & // prior to this query, otherwise false. content::SSLHostStateDelegate::CertJudgment SSLHostStateDelegateQt::QueryPolicy(const std::string &host, const net::X509Certificate &cert, - int error, - bool * /*expired_previous_decision*/) + int error) { return m_certPolicyforHost[host].Check(cert, error) ? SSLHostStateDelegate::ALLOWED : SSLHostStateDelegate::DENIED; } diff --git a/src/core/net/ssl_host_state_delegate_qt.h b/src/core/net/ssl_host_state_delegate_qt.h index e361aa0be..b3b89f117 100644 --- a/src/core/net/ssl_host_state_delegate_qt.h +++ b/src/core/net/ssl_host_state_delegate_qt.h @@ -68,8 +68,7 @@ public: // content::SSLHostStateDelegate implementation: void AllowCert(const std::string &, const net::X509Certificate &cert, int error) override; void Clear(const base::Callback<bool(const std::string &)> &host_filter) override; - CertJudgment QueryPolicy(const std::string &host, const net::X509Certificate &cert, int error, - bool *expired_previous_decision) override; + CertJudgment QueryPolicy(const std::string &host, const net::X509Certificate &cert, int error) override; void HostRanInsecureContent(const std::string &host, int child_id, InsecureContentType content_type) override; bool DidHostRunInsecureContent(const std::string &host, int child_id, InsecureContentType content_type) override; void RevokeUserAllowExceptions(const std::string &host) override; 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..2c5f49615 --- /dev/null +++ b/src/core/net/system_network_context_manager.cpp @@ -0,0 +1,350 @@ +/**************************************************************************** +** +** 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/network/public/mojom/url_loader_factory.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_allowlist = command_line->GetSwitchValueASCII(switches::kAuthServerWhitelist); +// auth_dynamic_params->delegate_allowlist = 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(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) + { + if (!manager_) + return; + manager_->GetURLLoaderFactory()->Clone(std::move(receiver)); + } + + // 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)); + } + + 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 = false; + + 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_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..f734db645 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,73 @@ 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; - if (remainingBytes > 0) - m_job->set_expected_content_size(remainingBytes); + qint64 deviceSize = m_client->m_device ? m_client->m_device->size() : -1; + if (deviceSize > 0) + m_client->notifyExpectedContentSize(deviceSize); - 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/net/webui_controller_factory_qt.cpp b/src/core/net/webui_controller_factory_qt.cpp index 8c045bb7b..27e58d75f 100644 --- a/src/core/net/webui_controller_factory_qt.cpp +++ b/src/core/net/webui_controller_factory_qt.cpp @@ -65,7 +65,7 @@ #include "url/gurl.h" #if defined(OS_LINUX) || defined(OS_ANDROID) -#include "chrome/browser/ui/webui/sandbox_internals_ui.h" +#include "chrome/browser/ui/webui/sandbox/sandbox_internals_ui.h" #endif // The Following WebUIs are disabled because they currently doesn't build diff --git a/src/core/ozone/gl_surface_qt.cpp b/src/core/ozone/gl_surface_qt.cpp index e9da5e6a5..c54a8f12e 100644 --- a/src/core/ozone/gl_surface_qt.cpp +++ b/src/core/ozone/gl_surface_qt.cpp @@ -238,6 +238,11 @@ bool DirectCompositionSurfaceWin::IsHDRSupported() { return false; } + +bool DirectCompositionSurfaceWin::IsSwapChainTearingSupported() +{ + return false; +} } // namespace gl #endif #endif // !defined(OS_MACOSX) diff --git a/src/core/ozone/ozone_platform_qt.cpp b/src/core/ozone/ozone_platform_qt.cpp index 2ab274b8f..887bc167e 100644 --- a/src/core/ozone/ozone_platform_qt.cpp +++ b/src/core/ozone/ozone_platform_qt.cpp @@ -51,7 +51,6 @@ #include "ui/ozone/public/system_input_injector.h" #include "ui/platform_window/platform_window_delegate.h" #include "ui/platform_window/platform_window_init_properties.h" -#include "ui/platform_window/platform_window.h" #include "surface_factory_qt.h" #include "platform_window_qt.h" @@ -68,7 +67,7 @@ public: ui::SurfaceFactoryOzone* GetSurfaceFactoryOzone() override; ui::CursorFactoryOzone* GetCursorFactoryOzone() override; GpuPlatformSupportHost* GetGpuPlatformSupportHost() override; - std::unique_ptr<PlatformWindow> CreatePlatformWindow(PlatformWindowDelegate* delegate, PlatformWindowInitProperties properties) override; + std::unique_ptr<PlatformWindowBase> CreatePlatformWindow(PlatformWindowDelegate* delegate, PlatformWindowInitProperties properties) override; std::unique_ptr<display::NativeDisplayDelegate> CreateNativeDisplayDelegate() override; ui::InputController* GetInputController() override; std::unique_ptr<ui::SystemInputInjector> CreateSystemInputInjector() override; @@ -109,7 +108,7 @@ GpuPlatformSupportHost* OzonePlatformQt::GetGpuPlatformSupportHost() return gpu_platform_support_host_.get(); } -std::unique_ptr<PlatformWindow> OzonePlatformQt::CreatePlatformWindow(PlatformWindowDelegate* delegate, PlatformWindowInitProperties properties) +std::unique_ptr<PlatformWindowBase> OzonePlatformQt::CreatePlatformWindow(PlatformWindowDelegate* delegate, PlatformWindowInitProperties properties) { return base::WrapUnique(new PlatformWindowQt(delegate, properties.bounds)); } diff --git a/src/core/ozone/platform_window_qt.h b/src/core/ozone/platform_window_qt.h index ca4a00313..344e9b115 100644 --- a/src/core/ozone/platform_window_qt.h +++ b/src/core/ozone/platform_window_qt.h @@ -45,13 +45,12 @@ #include "ui/events/platform/platform_event_dispatcher.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" -#include "ui/platform_window/platform_window.h" +#include "ui/platform_window/platform_window_base.h" +#include "ui/platform_window/platform_window_delegate.h" namespace ui { -class PlatformWindowDelegate; - -class PlatformWindowQt : public PlatformWindow, public PlatformEventDispatcher +class PlatformWindowQt : public PlatformWindowBase, public PlatformEventDispatcher { public: PlatformWindowQt(PlatformWindowDelegate* delegate, const gfx::Rect& bounds); @@ -59,9 +58,10 @@ public: // PlatformWindow: gfx::Rect GetBounds() override; void SetBounds(const gfx::Rect& bounds) override; - void Show() override { } + void Show(bool inactive = false) override { } void Hide() override { } void Close() override { } + bool IsVisible() const { return true; } void SetTitle(const base::string16&) override { } void SetCapture() override { } void ReleaseCapture() override { } @@ -78,6 +78,11 @@ public: gfx::Rect GetRestoredBoundsInPixels() const override { return gfx::Rect(); } void Activate() override { } void Deactivate() override { } + void SetUseNativeFrame(bool use_native_frame) override { } + bool ShouldUseNativeFrame() const override { return false; } + void SetWindowIcons(const gfx::ImageSkia& window_icon, + const gfx::ImageSkia& app_icon) override { } + void SizeConstraintsChanged() override { } // PlatformEventDispatcher: bool CanDispatchEvent(const PlatformEvent& event) override; diff --git a/src/core/platform_notification_service_qt.cpp b/src/core/platform_notification_service_qt.cpp index d8abec17f..5f3017dcf 100644 --- a/src/core/platform_notification_service_qt.cpp +++ b/src/core/platform_notification_service_qt.cpp @@ -201,12 +201,11 @@ int64_t PlatformNotificationServiceQt::ReadNextPersistentNotificationId() void PlatformNotificationServiceQt::ScheduleTrigger(base::Time /*timestamp*/) { - Q_UNIMPLEMENTED(); + QT_NOT_YET_IMPLEMENTED } base::Time PlatformNotificationServiceQt::ReadNextTriggerTimestamp() { - Q_UNIMPLEMENTED(); return base::Time::Max(); } diff --git a/src/core/pref_service_adapter.cpp b/src/core/pref_service_adapter.cpp index ca4be87df..4ded70d07 100644 --- a/src/core/pref_service_adapter.cpp +++ b/src/core/pref_service_adapter.cpp @@ -39,12 +39,13 @@ #include "pref_service_adapter.h" -#include "command_line_pref_store_qt.h" #include "profile_adapter.h" #include "type_conversion.h" #include "web_engine_context.h" +#include "chrome/browser/prefs/chrome_command_line_pref_store.h" #include "content/public/browser/browser_thread.h" +#include "components/language/core/browser/pref_names.h" #include "components/prefs/pref_member.h" #include "components/prefs/in_memory_pref_store.h" #include "components/prefs/json_pref_store.h" @@ -80,7 +81,7 @@ void PrefServiceAdapter::setup(const ProfileAdapter &profileAdapter) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); PrefServiceFactory factory; - factory.set_command_line_prefs(base::MakeRefCounted<CommandLinePrefStoreQt>( + factory.set_command_line_prefs(base::MakeRefCounted<ChromeCommandLinePrefStore>( WebEngineContext::commandLine())); QString userPrefStorePath = profileAdapter.dataPath(); @@ -92,12 +93,12 @@ void PrefServiceAdapter::setup(const ProfileAdapter &profileAdapter) factory.set_user_prefs(base::MakeRefCounted<JsonPrefStore>(toFilePath(userPrefStorePath))); } - PrefRegistrySimple *registry = new PrefRegistrySimple(); - PrefProxyConfigTrackerImpl::RegisterPrefs(registry); + auto registry = base::MakeRefCounted<PrefRegistrySimple>(); + PrefProxyConfigTrackerImpl::RegisterPrefs(registry.get()); #if QT_CONFIG(webengine_spellchecker) // Initial spellcheck settings - registry->RegisterStringPref(prefs::kAcceptLanguages, std::string()); + registry->RegisterStringPref(language::prefs::kAcceptLanguages, std::string()); registry->RegisterListPref(spellcheck::prefs::kSpellCheckDictionaries); registry->RegisterListPref(spellcheck::prefs::kSpellCheckForcedDictionaries); registry->RegisterListPref(spellcheck::prefs::kSpellCheckBlacklistedDictionaries); @@ -122,7 +123,6 @@ void PrefServiceAdapter::setup(const ProfileAdapter &profileAdapter) registry->RegisterListPref(extensions::pref_names::kNativeMessagingBlacklist); registry->RegisterListPref(extensions::pref_names::kNativeMessagingWhitelist); registry->RegisterBooleanPref(extensions::pref_names::kNativeMessagingUserLevelHosts, true); - registry->RegisterBooleanPref(extensions::pref_names::kInsecureExtensionUpdatesEnabled, false); #endif // BUILDFLAG(ENABLE_EXTENSIONS) // Media device salt id key diff --git a/src/core/printing/print_view_manager_base_qt.cpp b/src/core/printing/print_view_manager_base_qt.cpp index 4516f10b2..0cc334776 100644 --- a/src/core/printing/print_view_manager_base_qt.cpp +++ b/src/core/printing/print_view_manager_base_qt.cpp @@ -50,6 +50,7 @@ #include "base/memory/ref_counted_memory.h" #include "base/memory/shared_memory.h" #include "base/message_loop/message_loop.h" +#include "base/message_loop/message_loop_current.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" #include "base/task/post_task.h" @@ -150,7 +151,8 @@ printing::PrintedDocument *PrintViewManagerBaseQt::GetDocument(int cookie) // IPC handlers void PrintViewManagerBaseQt::OnDidPrintDocument(content::RenderFrameHost* /*render_frame_host*/, - const PrintHostMsg_DidPrintDocument_Params ¶ms) + const PrintHostMsg_DidPrintDocument_Params ¶ms, + std::unique_ptr<DelayedFrameDispatchHelper> helper) { printing::PrintedDocument *document = GetDocument(params.document_cookie); if (!document) @@ -172,6 +174,21 @@ void PrintViewManagerBaseQt::OnDidPrintDocument(content::RenderFrameHost* /*rend PrintDocument(document, data, params.page_size, params.content_area, params.physical_offsets); + if (helper) + helper->SendCompleted(); +} + +void PrintViewManagerBaseQt::OnGetDefaultPrintSettings(content::RenderFrameHost *render_frame_host, + IPC::Message *reply_msg) +{ + NOTREACHED() << "should be handled by printing::PrintingMessageFilter"; +} + +void PrintViewManagerBaseQt::OnScriptedPrint(content::RenderFrameHost *render_frame_host, + const PrintHostMsg_ScriptedPrint_Params ¶ms, + IPC::Message *reply_msg) +{ + NOTREACHED() << "should be handled by printing::PrintingMessageFilter"; } void PrintViewManagerBaseQt::OnShowInvalidPrinterSettingsError() @@ -212,13 +229,6 @@ bool PrintViewManagerBaseQt::OnMessageReceived(const IPC::Message& message, content::RenderFrameHost* render_frame_host) { bool handled = true; - IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(PrintViewManagerBaseQt, message, render_frame_host) - IPC_MESSAGE_HANDLER(PrintHostMsg_DidPrintDocument, OnDidPrintDocument) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - if (handled) - return true; - handled = true; IPC_BEGIN_MESSAGE_MAP(PrintViewManagerBaseQt, message) IPC_MESSAGE_HANDLER(PrintHostMsg_ShowInvalidPrinterSettingsError, OnShowInvalidPrinterSettingsError); @@ -514,20 +524,21 @@ void PrintViewManagerBaseQt::ReleasePrinterQuery() printerQuery = m_printerQueriesQueue->PopPrinterQuery(cookie); if (!printerQuery) return; - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, - base::BindOnce(&printing::PrinterQuery::StopWorker, std::move(printerQuery))); + base::PostTask(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&printing::PrinterQuery::StopWorker, std::move(printerQuery))); } // Originally from print_preview_message_handler.cc: -void PrintViewManagerBaseQt::StopWorker(int documentCookie) { - if (documentCookie <= 0) - return; - std::unique_ptr<printing::PrinterQuery> printer_query = - m_printerQueriesQueue->PopPrinterQuery(documentCookie); - if (printer_query.get()) { - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, - base::BindOnce(&printing::PrinterQuery::StopWorker, std::move(printer_query))); - } +void PrintViewManagerBaseQt::StopWorker(int documentCookie) +{ + if (documentCookie <= 0) + return; + std::unique_ptr<printing::PrinterQuery> printer_query = + m_printerQueriesQueue->PopPrinterQuery(documentCookie); + if (printer_query.get()) { + base::PostTask(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&printing::PrinterQuery::StopWorker, std::move(printer_query))); + } } void PrintViewManagerBaseQt::SendPrintingEnabled(bool enabled, content::RenderFrameHost* rfh) diff --git a/src/core/printing/print_view_manager_base_qt.h b/src/core/printing/print_view_manager_base_qt.h index 31e0a1778..1217e8c11 100644 --- a/src/core/printing/print_view_manager_base_qt.h +++ b/src/core/printing/print_view_manager_base_qt.h @@ -98,9 +98,16 @@ protected: bool OnMessageReceived(const IPC::Message& message, content::RenderFrameHost* render_frame_host) override; - // IPC Message handlers. - void OnDidPrintDocument(content::RenderFrameHost* render_frame_host, - const PrintHostMsg_DidPrintDocument_Params& params); + // printing::PrintManager implementation: + void OnDidPrintDocument(content::RenderFrameHost *render_frame_host, + const PrintHostMsg_DidPrintDocument_Params ¶ms, + std::unique_ptr<DelayedFrameDispatchHelper> helper) override; + void OnGetDefaultPrintSettings(content::RenderFrameHost* render_frame_host, + IPC::Message* reply_msg) override; + void OnScriptedPrint(content::RenderFrameHost* render_frame_host, + const PrintHostMsg_ScriptedPrint_Params& params, + IPC::Message* reply_msg) override; + void OnShowInvalidPrinterSettingsError(); // Processes a NOTIFY_PRINT_JOB_EVENT notification. diff --git a/src/core/printing/print_view_manager_qt.cpp b/src/core/printing/print_view_manager_qt.cpp index 7d8039100..ebaad9a80 100644 --- a/src/core/printing/print_view_manager_qt.cpp +++ b/src/core/printing/print_view_manager_qt.cpp @@ -66,6 +66,7 @@ #include "printing/metafile_skia.h" #include "printing/print_job_constants.h" #include "printing/units.h" +#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" namespace { @@ -106,8 +107,8 @@ static void SavePdfFile(scoped_refptr<base::RefCountedBytes> data, base::File file(path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); bool success = file.IsValid() && metafile.SaveTo(&file); - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(saveCallback, success)); + base::PostTask(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(saveCallback, success)); } static base::DictionaryValue *createPrintSettings() @@ -120,10 +121,7 @@ static base::DictionaryValue *createPrintSettings() printSettings->SetInteger(printing::kPreviewRequestID, internalRequestId); // The following are standard settings that Chromium expects to be set. - printSettings->SetBoolean(printing::kSettingPrintToPDF, true); - printSettings->SetBoolean(printing::kSettingCloudPrintDialog, false); - printSettings->SetBoolean(printing::kSettingPrintWithPrivet, false); - printSettings->SetBoolean(printing::kSettingPrintWithExtension, false); + printSettings->SetInteger(printing::kSettingPrinterType, printing::kPdfPrinter); printSettings->SetInteger(printing::kSettingDpiHorizontal, printing::kPointsPerInch); printSettings->SetInteger(printing::kSettingDpiVertical, printing::kPointsPerInch); @@ -216,16 +214,16 @@ void PrintViewManagerQt::PrintToPDFFileWithCallback(const QPageLayout &pageLayou return; if (m_printSettings || !filePath.length()) { - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(callback, false)); + base::PostTask(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(callback, false)); return; } m_pdfOutputPath = toFilePath(filePath); m_pdfSaveCallback = callback; if (!PrintToPDFInternal(pageLayout, printInColor)) { - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(callback, false)); + base::PostTask(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(callback, false)); resetPdfState(); } } @@ -240,15 +238,15 @@ void PrintViewManagerQt::PrintToPDFWithCallback(const QPageLayout &pageLayout, // If there already is a pending print in progress, don't try starting another one. if (m_printSettings) { - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(callback, QSharedPointer<QByteArray>())); + base::PostTask(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(callback, QSharedPointer<QByteArray>())); return; } m_pdfPrintCallback = callback; if (!PrintToPDFInternal(pageLayout, printInColor, useCustomMargins)) { - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(callback, QSharedPointer<QByteArray>())); + base::PostTask(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(callback, QSharedPointer<QByteArray>())); resetPdfState(); } @@ -272,15 +270,9 @@ bool PrintViewManagerQt::PrintToPDFInternal(const QPageLayout &pageLayout, return false; content::RenderFrameHost* rfh = web_contents()->GetMainFrame(); - auto message = std::make_unique<PrintMsg_InitiatePrintPreview>( - rfh->GetRoutingID(), false); + GetPrintRenderFrame(rfh)->InitiatePrintPreview(nullptr, false); DCHECK(!m_printPreviewRfh); - - if (!rfh->Send(message.release())) { - return false; - } - m_printPreviewRfh = rfh; return true; } @@ -324,6 +316,24 @@ void PrintViewManagerQt::RenderFrameDeleted(content::RenderFrameHost *render_fra if (render_frame_host == m_printPreviewRfh) PrintPreviewDone(); PrintViewManagerBaseQt::RenderFrameDeleted(render_frame_host); + m_printRenderFrames.erase(render_frame_host); +} + +const mojo::AssociatedRemote<printing::mojom::PrintRenderFrame> &PrintViewManagerQt::GetPrintRenderFrame(content::RenderFrameHost *rfh) +{ + auto it = m_printRenderFrames.find(rfh); + if (it == m_printRenderFrames.end()) { + mojo::AssociatedRemote<printing::mojom::PrintRenderFrame> remote; + rfh->GetRemoteAssociatedInterfaces()->GetInterface(&remote); + it = m_printRenderFrames.insert(std::make_pair(rfh, std::move(remote))).first; + } else if (it->second.is_bound() && !it->second.is_connected()) { + // When print preview is closed, the remote is disconnected from the + // receiver. Reset and bind the remote before using it again. + it->second.reset(); + rfh->GetRemoteAssociatedInterfaces()->GetInterface(&it->second); + } + + return it->second; } void PrintViewManagerQt::resetPdfState() @@ -359,12 +369,12 @@ void PrintViewManagerQt::OnMetafileReadyForPrinting(content::RenderFrameHost* rf if (!pdf_print_callback.is_null()) { QSharedPointer<QByteArray> data_array = GetStdVectorFromHandle(params.content.metafile_data_region); - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(pdf_print_callback, data_array)); + base::PostTask(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(pdf_print_callback, data_array)); } else { scoped_refptr<base::RefCountedBytes> data_bytes = GetBytesFromHandle(params.content.metafile_data_region); - base::PostTaskWithTraits(FROM_HERE, {base::MayBlock()}, - base::BindOnce(&SavePdfFile, data_bytes, pdfOutputPath, pdf_save_callback)); + base::PostTask(FROM_HERE, {base::ThreadPool(), base::MayBlock()}, + base::BindOnce(&SavePdfFile, data_bytes, pdfOutputPath, pdf_save_callback)); } } @@ -382,8 +392,8 @@ void PrintViewManagerQt::DidStartLoading() void PrintViewManagerQt::NavigationStopped() { if (!m_pdfPrintCallback.is_null()) { - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(m_pdfPrintCallback, QSharedPointer<QByteArray>())); + base::PostTask(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(m_pdfPrintCallback, QSharedPointer<QByteArray>())); } resetPdfState(); PrintViewManagerBaseQt::NavigationStopped(); @@ -393,8 +403,8 @@ void PrintViewManagerQt::RenderProcessGone(base::TerminationStatus status) { PrintViewManagerBaseQt::RenderProcessGone(status); if (!m_pdfPrintCallback.is_null()) { - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(m_pdfPrintCallback, QSharedPointer<QByteArray>())); + base::PostTask(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(m_pdfPrintCallback, QSharedPointer<QByteArray>())); } resetPdfState(); } @@ -419,7 +429,7 @@ void PrintViewManagerQt::OnSetupScriptedPrintPreview(content::RenderFrameHost* r return; // close preview - rfh->Send(new PrintMsg_ClosePrintPreviewDialog(rfh->GetRoutingID())); + GetPrintRenderFrame(rfh)->OnPrintPreviewDialogClosed(); client->printRequested(); } @@ -431,8 +441,7 @@ void PrintViewManagerQt::OnShowScriptedPrintPreview(content::RenderFrameHost* rf } void PrintViewManagerQt::PrintPreviewDone() { - m_printPreviewRfh->Send(new PrintMsg_ClosePrintPreviewDialog( - m_printPreviewRfh->GetRoutingID())); + GetPrintRenderFrame(m_printPreviewRfh)->OnPrintPreviewDialogClosed(); m_printPreviewRfh = nullptr; } diff --git a/src/core/printing/print_view_manager_qt.h b/src/core/printing/print_view_manager_qt.h index 14f2688dd..06c2f47ea 100644 --- a/src/core/printing/print_view_manager_qt.h +++ b/src/core/printing/print_view_manager_qt.h @@ -47,14 +47,17 @@ #include "print_view_manager_base_qt.h" #include "qtwebenginecoreglobal_p.h" + #include "base/memory/ref_counted.h" #include "base/strings/string16.h" #include "components/prefs/pref_member.h" #include "components/printing/browser/print_manager.h" +#include "components/printing/common/print.mojom.h" #include "components/printing/common/print_messages.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" #include "content/public/browser/web_contents_user_data.h" +#include "mojo/public/cpp/bindings/associated_remote.h" #include <QSharedPointer> @@ -132,6 +135,11 @@ protected: private: void resetPdfState(); + + // Helper method to fetch the PrintRenderFrame associated remote interface + // pointer. + const mojo::AssociatedRemote<printing::mojom::PrintRenderFrame> &GetPrintRenderFrame(content::RenderFrameHost *rfh); + // content::WebContentsObserver implementation. void DidStartLoading() override; void PrintPreviewDone(); @@ -143,6 +151,9 @@ private: PrintToPDFCallback m_pdfPrintCallback; PrintToPDFFileCallback m_pdfSaveCallback; std::unique_ptr<base::DictionaryValue> m_printSettings; + + std::map<content::RenderFrameHost*,mojo::AssociatedRemote<printing::mojom::PrintRenderFrame>> m_printRenderFrames; + friend class content::WebContentsUserData<PrintViewManagerQt>; DISALLOW_COPY_AND_ASSIGN(PrintViewManagerQt); struct FrameDispatchHelper; diff --git a/src/core/profile_adapter.cpp b/src/core/profile_adapter.cpp index bccdf1ada..e9e2a2c5d 100644 --- a/src/core/profile_adapter.cpp +++ b/src/core/profile_adapter.cpp @@ -43,13 +43,17 @@ #include "content/public/browser/browser_thread.h" #include "content/public/browser/browsing_data_remover.h" #include "content/public/browser/download_manager.h" +#include "content/public/browser/shared_cors_origin_access_list.h" +#include "content/public/browser/storage_partition.h" +#include "services/network/public/cpp/cors/origin_access_list.h" +#include "url/url_util.h" #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" @@ -104,6 +108,17 @@ ProfileAdapter::ProfileAdapter(const QString &storageName): if (!storageName.isEmpty()) extensions::ExtensionSystem::Get(m_profile.data())->InitForRegularProfile(true); #endif + + // Allow XMLHttpRequests from qrc to file. + // ### consider removing for Qt6 + url::Origin qrc = url::Origin::Create(GURL("qrc://")); + auto pattern = network::mojom::CorsOriginPattern::New("file", "", 0, + network::mojom::CorsDomainMatchMode::kAllowSubdomains, + network::mojom::CorsPortMatchMode::kAllowAnyPort, + network::mojom::CorsOriginAccessMatchPriority::kDefaultPriority); + std::vector<network::mojom::CorsOriginPatternPtr> list; + list.push_back(std::move(pattern)); + m_profile->GetSharedCorsOriginAccessList()->SetForOrigin(qrc, std::move(list), {}, base::BindOnce([]{})); } ProfileAdapter::~ProfileAdapter() @@ -129,8 +144,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 +156,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 +206,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 +222,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 +287,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 +314,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 @@ -343,17 +352,21 @@ QString ProfileAdapter::httpUserAgent() const void ProfileAdapter::setHttpUserAgent(const QString &userAgent) { - if (m_httpUserAgent == userAgent) + const QString httpUserAgent = userAgent.simplified(); + if (m_httpUserAgent == httpUserAgent) return; - m_httpUserAgent = userAgent.simplified(); + m_httpUserAgent = httpUserAgent; + const std::string stdUserAgent = httpUserAgent.toStdString(); std::vector<content::WebContentsImpl *> list = content::WebContentsImpl::GetAllWebContents(); for (content::WebContentsImpl *web_contents : list) if (web_contents->GetBrowserContext() == m_profile.data()) - web_contents->SetUserAgentOverride(m_httpUserAgent.toStdString(), true); + web_contents->SetUserAgentOverride(stdUserAgent, true); - if (m_profile->m_urlRequestContextGetter.get()) - m_profile->m_profileIOData->updateUserAgent(); + content::BrowserContext::ForEachStoragePartition( + m_profile.get(), base::BindRepeating([](const std::string &user_agent, content::StoragePartition *storage_partition) { + storage_partition->GetNetworkContext()->SetUserAgent(user_agent); + }, stdUserAgent)); } ProfileAdapter::HttpCacheType ProfileAdapter::httpCacheType() const @@ -371,8 +384,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 +404,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 +459,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 }; @@ -489,8 +505,10 @@ const QList<QByteArray> ProfileAdapter::customUrlSchemes() const void ProfileAdapter::updateCustomUrlSchemeHandlers() { - if (m_profile->m_urlRequestContextGetter.get()) - m_profile->m_profileIOData->updateJobFactory(); + content::BrowserContext::ForEachStoragePartition( + m_profile.get(), base::BindRepeating([](content::StoragePartition *storage_partition) { + storage_partition->ResetURLLoaderFactories(); + })); } void ProfileAdapter::removeUrlSchemeHandler(QWebEngineUrlSchemeHandler *handler) @@ -551,9 +569,11 @@ void ProfileAdapter::installUrlSchemeHandler(const QByteArray &scheme, QWebEngin void ProfileAdapter::removeAllUrlSchemeHandlers() { - m_customUrlSchemeHandlers.clear(); - m_customUrlSchemeHandlers.insert(QByteArrayLiteral("qrc"), &m_qrcHandler); - updateCustomUrlSchemeHandlers(); + if (m_customUrlSchemeHandlers.size() > 1) { + m_customUrlSchemeHandlers.clear(); + m_customUrlSchemeHandlers.insert(QByteArrayLiteral("qrc"), &m_qrcHandler); + updateCustomUrlSchemeHandlers(); + } } UserResourceControllerHost *ProfileAdapter::userResourceController() @@ -596,17 +616,21 @@ void ProfileAdapter::setHttpAcceptLanguage(const QString &httpAcceptLanguage) return; m_httpAcceptLanguage = httpAcceptLanguage; + std::string http_accept_language = httpAcceptLanguageWithoutQualities().toStdString(); + std::vector<content::WebContentsImpl *> list = content::WebContentsImpl::GetAllWebContents(); for (content::WebContentsImpl *web_contents : list) { if (web_contents->GetBrowserContext() == m_profile.data()) { blink::mojom::RendererPreferences *rendererPrefs = web_contents->GetMutableRendererPrefs(); - rendererPrefs->accept_languages = httpAcceptLanguageWithoutQualities().toStdString(); - web_contents->GetRenderViewHost()->SyncRendererPrefs(); + rendererPrefs->accept_languages = http_accept_language; + web_contents->SyncRendererPrefs(); } } - if (m_profile->m_urlRequestContextGetter.get()) - m_profile->m_profileIOData->updateUserAgent(); + content::BrowserContext::ForEachStoragePartition( + m_profile.get(), base::BindRepeating([](std::string accept_language, content::StoragePartition *storage_partition) { + storage_partition->GetNetworkContext()->SetAcceptLanguage(accept_language); + }, http_accept_language)); } void ProfileAdapter::clearHttpCache() @@ -675,6 +699,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 +710,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 @@ -700,7 +724,7 @@ QString ProfileAdapter::determineDownloadPath(const QString &downloadDirectory, QString suggestedFilePath = suggestedFile.absoluteFilePath(); base::FilePath tmpFilePath(toFilePath(suggestedFilePath).NormalizePathSeparatorsTo('/')); - int uniquifier = base::GetUniquePathNumber(tmpFilePath, base::FilePath::StringType()); + int uniquifier = base::GetUniquePathNumber(tmpFilePath); if (uniquifier > 0) suggestedFilePath = toQt(tmpFilePath.InsertBeforeExtensionASCII(base::StringPrintf(" (%d)", uniquifier)).AsUTF8Unsafe()); else if (uniquifier == -1) { diff --git a/src/core/profile_io_data_qt.cpp b/src/core/profile_io_data_qt.cpp index 68be09ad2..91af8d544 100644 --- a/src/core/profile_io_data_qt.cpp +++ b/src/core/profile_io_data_qt.cpp @@ -40,131 +40,30 @@ #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 "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/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 <QDebug> +#include <QVariant> #include <mutex> 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,28 +81,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); } QPointer<ProfileAdapter> ProfileIODataQt::profileAdapter() @@ -218,6 +96,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 +111,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 +129,22 @@ 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(); setGlobalCertificateVerification(); m_initialized = true; } @@ -273,300 +154,21 @@ void ProfileIODataQt::initializeOnUIThread() m_profileAdapter = m_profile->profileAdapter(); DCHECK_CURRENTLY_ON(content::BrowserThread::UI); m_resourceContext.reset(new ResourceContextQt(this)); - ProtocolHandlerRegistry* protocolHandlerRegistry = - ProtocolHandlerRegistryFactory::GetForBrowserContext(m_profile); - DCHECK(protocolHandlerRegistry); - 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(); - } + m_proxyConfigMonitor.reset(new ProxyConfigMonitor(m_profile->GetPrefs())); } 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 +176,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,38 +209,28 @@ 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(); - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, - base::BindOnce(&ProfileIODataQt::generateAllStorage, m_weakPtr)); + base::PostTask(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&ProfileIODataQt::generateAllStorage, m_weakPtr)); } } -// TODO(miklocek): mojofy ProxyConfigServiceQt -void ProfileIODataQt::createProxyConfig() -{ - 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); - //pass interface to io thread - m_proxyResolverFactoryInterface = ChromeMojoProxyResolverFactory::CreateWithSelfOwnedReceiver(); -} - void ProfileIODataQt::updateStorageSettings() { Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); @@ -664,11 +238,6 @@ void ProfileIODataQt::updateStorageSettings() const std::lock_guard<QRecursiveMutex> lock(m_mutex); setFullConfiguration(); - base::Token groupId = content::BrowserContext::GetServiceInstanceGroupFor(m_profile); - if (file::GetUserDirForInstanceGroup(groupId) != toFilePath(m_profileAdapter->dataPath())) { - file::ForgetServiceInstanceGroupUserDirAssociation(groupId); - file::AssociateServiceInstanceGroupWithUserDir(groupId, toFilePath(m_profileAdapter->dataPath())); - } if (!m_pendingStorageRequestGeneration) requestStorageGeneration(); } @@ -726,8 +295,8 @@ void ProfileIODataQt::updateJobFactory() if (m_initialized && !m_updateJobFactory) { m_updateJobFactory = true; - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, - base::BindOnce(&ProfileIODataQt::regenerateJobFactory, m_weakPtr)); + base::PostTask(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&ProfileIODataQt::regenerateJobFactory, m_weakPtr)); } } @@ -751,6 +320,7 @@ bool ProfileIODataQt::isInterceptorDeprecated() const QWebEngineUrlRequestInterceptor *ProfileIODataQt::acquireInterceptor() { + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); m_mutex.lock(); return m_requestInterceptor; } @@ -769,6 +339,7 @@ bool ProfileIODataQt::hasPageInterceptors() void ProfileIODataQt::releaseInterceptor() { + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); m_mutex.unlock(); } @@ -791,8 +362,8 @@ void ProfileIODataQt::updateUsedForGlobalCertificateVerification() m_useForGlobalCertificateVerification = m_profileAdapter->isUsedForGlobalCertificateVerification(); if (m_useForGlobalCertificateVerification) - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, - base::BindOnce(&ProfileIODataQt::setGlobalCertificateVerification, m_weakPtr)); + base::PostTask(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&ProfileIODataQt::setGlobalCertificateVerification, m_weakPtr)); } #if QT_CONFIG(ssl) @@ -811,21 +382,55 @@ 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->user_agent = m_httpUserAgent.toStdString(); + network_context_params->accept_language = m_httpAcceptLanguage.toStdString(); + + network_context_params->enable_referrers = true; + // Encrypted cookies requires os_crypt, which currently has issues for us on Linux. + network_context_params->enable_encrypted_cookies = false; + + network_context_params->http_cache_enabled = m_httpCacheType != ProfileAdapter::NoCache; + network_context_params->http_cache_max_size = m_httpCacheMaxSize; + if (m_httpCacheType == ProfileAdapter::DiskHttpCache && !m_httpCachePath.isEmpty()) + network_context_params->http_cache_path = toFilePath(m_httpCachePath); + + if (m_persistentCookiesPolicy != ProfileAdapter::NoPersistentCookies && !m_dataPath.isEmpty()) { + 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 = m_persistentCookiesPolicy == ProfileAdapter::ForcePersistentCookies; + network_context_params->persist_session_cookies = m_persistentCookiesPolicy != ProfileAdapter::NoPersistentCookies; + } + 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; + + // 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..fcd209bf8 100644 --- a/src/core/profile_io_data_qt.h +++ b/src/core/profile_io_data_qt.h @@ -40,16 +40,13 @@ #ifndef PROFILE_IO_DATA_QT_H #define PROFILE_IO_DATA_QT_H -#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 "services/network/cookie_settings.h" -#include "services/network/public/mojom/restricted_cookie_manager.mojom.h" -#include "services/proxy_resolver/public/mojom/proxy_resolver.mojom.h" + +#include "net/proxy_config_monitor.h" +#include "profile_adapter.h" #include <QtCore/QString> #include <QtCore/QPointer> @@ -57,16 +54,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 { @@ -108,22 +96,12 @@ public: extensions::ExtensionSystemQt* GetExtensionSystem(); #endif // BUILDFLAG(ENABLE_EXTENSIONS) - ProtocolHandlerRegistry::IOThreadDelegate *protocolHandlerRegistryIOThreadDelegate() - { - return m_protocolHandlerRegistryIOThreadDelegate.get(); - } - void initializeOnIOThread(); void initializeOnUIThread(); // runs on ui thread void shutdownOnUIThread(); // runs on ui thread 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 +116,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 @@ -145,16 +124,10 @@ public: void updateJobFactory(); // runs on ui thread void updateRequestInterceptor(); // runs on ui thread void requestStorageGeneration(); //runs on ui thread - void createProxyConfig(); //runs on ui thread 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 +136,23 @@ 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..748104312 100644 --- a/src/core/profile_qt.cpp +++ b/src/core/profile_qt.cpp @@ -41,10 +41,8 @@ #include "profile_adapter.h" #include "browsing_data_remover_delegate_qt.h" -#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" @@ -52,8 +50,10 @@ #include "web_engine_library_info.h" #include "web_engine_context.h" +#include "base/barrier_closure.h" #include "base/time/time.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/cors_origin_pattern_setter.h" #include "content/public/browser/shared_cors_origin_access_list.h" #include "content/public/browser/storage_partition.h" @@ -76,7 +76,6 @@ #if BUILDFLAG(ENABLE_EXTENSIONS) #include "components/guest_view/browser/guest_view_manager.h" -#include "extensions/browser/extension_protocols.h" #include "extensions/browser/pref_names.h" #include "extensions/browser/process_manager.h" #include "extensions/common/constants.h" @@ -145,16 +144,6 @@ bool ProfileQt::IsOffTheRecord() return m_profileAdapter->isOffTheRecord(); } -net::URLRequestContextGetter *ProfileQt::GetRequestContext() -{ - return m_urlRequestContextGetter.get(); -} - -net::URLRequestContextGetter *ProfileQt::CreateMediaRequestContext() -{ - return m_urlRequestContextGetter.get(); -} - content::ResourceContext *ProfileQt::GetResourceContext() { return m_profileIOData->resourceContext(); @@ -176,7 +165,7 @@ content::BrowserPluginGuestManager *ProfileQt::GetGuestManager() storage::SpecialStoragePolicy *ProfileQt::GetSpecialStoragePolicy() { - QT_NOT_YET_IMPLEMENTED + // matches android_webview and chromecast return nullptr; } @@ -221,26 +210,12 @@ content::PermissionControllerDelegate *ProfileQt::GetPermissionControllerDelegat return m_permissionManager.get(); } -net::URLRequestContextGetter *ProfileQt::CreateRequestContext( - content::ProtocolHandlerMap *protocol_handlers, - content::URLRequestInterceptorScopedVector request_interceptors) +content::ClientHintsControllerDelegate *ProfileQt::GetClientHintsControllerDelegate() { - 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() +content::StorageNotificationService *ProfileQt::GetStorageNotificationService() { return nullptr; } @@ -250,10 +225,22 @@ void ProfileQt::SetCorsOriginAccessListForOrigin(const url::Origin &source_origi std::vector<network::mojom::CorsOriginPatternPtr> block_patterns, base::OnceClosure closure) { + auto barrier_closure = base::BarrierClosure(2, std::move(closure)); + + // Keep profile storage partitions' NetworkContexts synchronized. + auto profile_setter = base::MakeRefCounted<content::CorsOriginPatternSetter>( + source_origin, + content::CorsOriginPatternSetter::ClonePatterns(allow_patterns), + content::CorsOriginPatternSetter::ClonePatterns(block_patterns), + barrier_closure); + ForEachStoragePartition(this, + base::BindRepeating(&content::CorsOriginPatternSetter::SetLists, + base::RetainedRef(profile_setter.get()))); + m_sharedCorsOriginAccessList->SetForOrigin(source_origin, std::move(allow_patterns), std::move(block_patterns), - std::move(closure)); + barrier_closure); } content::SharedCorsOriginAccessList *ProfileQt::GetSharedCorsOriginAccessList() diff --git a/src/core/profile_qt.h b/src/core/profile_qt.h index f5dc59717..59f5a8c21 100644 --- a/src/core/profile_qt.h +++ b/src/core/profile_qt.h @@ -44,7 +44,6 @@ #include "content/public/browser/content_browser_client.h" #include "content/public/browser/resource_context.h" #include "extensions/buildflags/buildflags.h" -#include "net/url_request/url_request_context.h" #include "pref_service_adapter.h" #include "profile_io_data_qt.h" #include <QtGlobal> @@ -79,16 +78,12 @@ public: base::FilePath GetPath() override; bool IsOffTheRecord() override; - net::URLRequestContextGetter *CreateMediaRequestContext() override; content::ResourceContext *GetResourceContext() override; content::DownloadManagerDelegate *GetDownloadManagerDelegate() override; content::BrowserPluginGuestManager *GetGuestManager() override; storage::SpecialStoragePolicy *GetSpecialStoragePolicy() override; content::PushMessagingService *GetPushMessagingService() override; content::SSLHostStateDelegate *GetSSLHostStateDelegate() override; - net::URLRequestContextGetter *CreateRequestContext( - content::ProtocolHandlerMap *protocol_handlers, - content::URLRequestInterceptorScopedVector request_interceptors) override; std::unique_ptr<content::ZoomLevelDelegate> CreateZoomLevelDelegate( const base::FilePath &partition_path) override; content::PermissionControllerDelegate * GetPermissionControllerDelegate() override; @@ -96,6 +91,7 @@ public: content::BackgroundSyncController *GetBackgroundSyncController() override; content::BrowsingDataRemoverDelegate *GetBrowsingDataRemoverDelegate() override; content::ClientHintsControllerDelegate *GetClientHintsControllerDelegate() override; + content::StorageNotificationService *GetStorageNotificationService() override; void SetCorsOriginAccessListForOrigin(const url::Origin &source_origin, std::vector<network::mojom::CorsOriginPatternPtr> allow_patterns, std::vector<network::mojom::CorsOriginPatternPtr> block_patterns, @@ -106,9 +102,8 @@ public: // Profile implementation: PrefService *GetPrefs() override; const PrefService *GetPrefs() const override; - net::URLRequestContextGetter *GetRequestContext() override; - void Initialize(); + void Initialize(); ProfileAdapter *profileAdapter() { return m_profileAdapter; } content::PlatformNotificationService *platformNotificationService(); @@ -132,7 +127,6 @@ private: friend class ContentBrowserClientQt; friend class ProfileIODataQt; friend class WebContentsAdapter; - scoped_refptr<net::URLRequestContextGetter> m_urlRequestContextGetter; std::unique_ptr<BrowsingDataRemoverDelegateQt> m_removerDelegate; std::unique_ptr<PermissionManagerQt> m_permissionManager; std::unique_ptr<SSLHostStateDelegateQt> m_sslHostStateDelegate; 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/qtwebengine_sources.gni b/src/core/qtwebengine_sources.gni index 011a143b5..9c65fa690 100644 --- a/src/core/qtwebengine_sources.gni +++ b/src/core/qtwebengine_sources.gni @@ -49,7 +49,9 @@ source_set("qtwebengine_sources") { "//build:branding_buildflags", "//chrome/common:buildflags", "//components/nacl/common:buildflags", + "//components/plugins/renderer/", "//extensions/buildflags:buildflags", + "//rlz/buildflags:buildflags", "//third_party/blink/public/mojom:mojom_platform", ] @@ -63,10 +65,14 @@ source_set("qtwebengine_sources") { "//chrome/browser/media/webrtc/desktop_media_list.h", "//chrome/browser/net/chrome_mojo_proxy_resolver_factory.cc", "//chrome/browser/net/chrome_mojo_proxy_resolver_factory.h", + "//chrome/browser/prefs/chrome_command_line_pref_store.cc", + "//chrome/browser/prefs/chrome_command_line_pref_store.h", "//chrome/browser/profiles/profile.cc", "//chrome/browser/profiles/profile.h", "//chrome/browser/ui/webui/devtools_ui.cc", "//chrome/browser/ui/webui/devtools_ui.h", + "//chrome/browser/ui/webui/devtools_ui_data_source.cc", + "//chrome/browser/ui/webui/devtools_ui_data_source.h", "//chrome/browser/ui/webui/quota_internals/quota_internals_handler.cc", "//chrome/browser/ui/webui/quota_internals/quota_internals_handler.h", "//chrome/browser/ui/webui/quota_internals/quota_internals_proxy.cc", @@ -128,8 +134,8 @@ source_set("qtwebengine_sources") { if (is_linux) { sources += [ - "//chrome/browser/ui/webui/sandbox_internals_ui.cc", - "//chrome/browser/ui/webui/sandbox_internals_ui.h", + "//chrome/browser/ui/webui/sandbox/sandbox_internals_ui.cc", + "//chrome/browser/ui/webui/sandbox/sandbox_internals_ui.h", ] } diff --git a/src/core/quota_permission_context_qt.cpp b/src/core/quota_permission_context_qt.cpp index a502e7fc8..94645b237 100644 --- a/src/core/quota_permission_context_qt.cpp +++ b/src/core/quota_permission_context_qt.cpp @@ -66,7 +66,7 @@ void QuotaPermissionContextQt::RequestQuotaPermission(const StorageQuotaParams & } if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) { - base::PostTaskWithTraits( + base::PostTask( FROM_HERE, {content::BrowserThread::UI}, base::BindOnce(&QuotaPermissionContextQt::RequestQuotaPermission, this, params, render_process_id, callback)); @@ -97,7 +97,7 @@ void QuotaPermissionContextQt::dispatchCallbackOnIOThread(const PermissionCallba return; if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)) { - base::PostTaskWithTraits( + base::PostTask( FROM_HERE, {content::BrowserThread::IO}, base::BindOnce(&QuotaPermissionContextQt::dispatchCallbackOnIOThread, this, callback, response)); diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp index 7a5118837..c1b1791cf 100644 --- a/src/core/render_widget_host_view_qt.cpp +++ b/src/core/render_widget_host_view_qt.cpp @@ -362,6 +362,10 @@ RenderWidgetHostViewQt::~RenderWidgetHostViewQt() void RenderWidgetHostViewQt::setDelegate(RenderWidgetHostViewQtDelegate* delegate) { m_delegate.reset(delegate); + if (m_deferredShow) { + m_deferredShow = false; + Show(); + } visualPropertiesChanged(); } @@ -468,16 +472,21 @@ void RenderWidgetHostViewQt::CopyFromSurface(const gfx::Rect &src_rect, void RenderWidgetHostViewQt::Show() { - m_delegate->show(); + if (m_delegate) + m_delegate->show(); + else + m_deferredShow = true; } void RenderWidgetHostViewQt::Hide() { + Q_ASSERT(m_delegate); m_delegate->hide(); } bool RenderWidgetHostViewQt::IsShowing() { + Q_ASSERT(m_delegate); return m_delegate->isVisible(); } @@ -506,7 +515,7 @@ void RenderWidgetHostViewQt::UpdateBackgroundColor() } // Return value indicates whether the mouse is locked successfully or not. -bool RenderWidgetHostViewQt::LockMouse() +bool RenderWidgetHostViewQt::LockMouse(bool) { m_previousMousePosition = QCursor::pos(); m_delegate->lockMouse(); @@ -751,10 +760,6 @@ gfx::Rect RenderWidgetHostViewQt::GetBoundsInRootWindow() return m_windowRectInDips; } -void RenderWidgetHostViewQt::ClearCompositorFrame() -{ -} - void RenderWidgetHostViewQt::OnUpdateTextInputStateCalled(content::TextInputManager *text_input_manager, RenderWidgetHostViewBase *updated_view, bool did_update_state) { Q_UNUSED(text_input_manager); @@ -827,7 +832,7 @@ void RenderWidgetHostViewQt::OnTextSelectionChanged(content::TextInputManager *t #if defined(USE_OZONE) if (!selection->selected_text().empty() && selection->user_initiated()) { // Set the CLIPBOARD_TYPE_SELECTION to the ui::Clipboard. - ui::ScopedClipboardWriter clipboard_writer(ui::ClipboardType::kSelection); + ui::ScopedClipboardWriter clipboard_writer(ui::ClipboardBuffer::kSelection); clipboard_writer.WriteText(selection->selected_text()); } #endif // defined(USE_OZONE) @@ -1706,6 +1711,7 @@ void RenderWidgetHostViewQt::handlePointerEvent(T *event) if ((webEvent.GetType() == blink::WebInputEvent::kMouseDown || webEvent.GetType() == blink::WebInputEvent::kMouseUp) && webEvent.button == blink::WebMouseEvent::Button::kNoButton) { // Blink can only handle the 3 main mouse-buttons and may assert when processing mouse-down for no button. + LOG(INFO) << "Unhandled mouse button"; return; } @@ -1874,9 +1880,9 @@ void RenderWidgetHostViewQt::OnRenderFrameMetadataChangedAfterActivation() gfx::SizeF contentsSize = metadata.root_layer_size; std::swap(m_lastScrollOffset, scrollOffset); std::swap(m_lastContentsSize, contentsSize); - if (scrollOffset != m_lastScrollOffset) + if (m_adapterClient && scrollOffset != m_lastScrollOffset) m_adapterClient->updateScrollPosition(toQt(m_lastScrollOffset)); - if (contentsSize != m_lastContentsSize) + if (m_adapterClient && contentsSize != m_lastContentsSize) m_adapterClient->updateContentsSize(toQt(m_lastContentsSize)); } diff --git a/src/core/render_widget_host_view_qt.h b/src/core/render_widget_host_view_qt.h index dc6ccc8df..41ce50b34 100644 --- a/src/core/render_widget_host_view_qt.h +++ b/src/core/render_widget_host_view_qt.h @@ -141,7 +141,7 @@ public: bool IsShowing() override; gfx::Rect GetViewBounds() override; void UpdateBackgroundColor() override; - bool LockMouse() override; + bool LockMouse(bool) override; void UnlockMouse() override; void UpdateCursor(const content::WebCursor&) override; void DisplayCursor(const content::WebCursor&) override; @@ -162,7 +162,6 @@ public: void GetScreenInfo(content::ScreenInfo *results) override; gfx::Rect GetBoundsInRootWindow() override; void ProcessAckedTouchEvent(const content::TouchEventWithLatencyInfo &touch, content::InputEventAckState ack_result) override; - void ClearCompositorFrame() override; void SetNeedsBeginFrames(bool needs_begin_frames) override; void SetWantsAnimateOnlyBeginFrames() override; viz::SurfaceId GetCurrentSurfaceId() const override; @@ -273,6 +272,7 @@ private: const bool m_enableViz; bool m_visible; bool m_needsBeginFrames; + bool m_deferredShow = false; DelegatedFrameHostClientQt m_delegatedFrameHostClient{this}; std::unique_ptr<content::DelegatedFrameHost> m_delegatedFrameHost; std::unique_ptr<ui::Layer> m_rootLayer; diff --git a/src/core/renderer/content_renderer_client_qt.cpp b/src/core/renderer/content_renderer_client_qt.cpp index b9b199087..f7c8a497b 100644 --- a/src/core/renderer/content_renderer_client_qt.cpp +++ b/src/core/renderer/content_renderer_client_qt.cpp @@ -95,8 +95,15 @@ #include "extensions/extensions_renderer_client_qt.h" #endif //ENABLE_EXTENSIONS +#if BUILDFLAG(ENABLE_PLUGINS) +#include "plugins/loadable_plugin_placeholder_qt.h" +#include "plugins/plugin_placeholder_qt.h" +#include "content/common/frame_messages.h" +#endif // ENABLE_PLUGINS + #include "services/service_manager/public/cpp/binder_registry.h" #include "services/service_manager/public/cpp/connector.h" +#include "services/service_manager/public/cpp/service_binding.h" #include "components/grit/components_resources.h" @@ -114,7 +121,6 @@ namespace QtWebEngineCore { static const char kHttpErrorDomain[] = "http"; ContentRendererClientQt::ContentRendererClientQt() - : m_serviceBinding(this) { #if BUILDFLAG(ENABLE_EXTENSIONS) extensions::ExtensionsClient::Set(extensions::ExtensionsClientQt::GetInstance()); @@ -122,14 +128,11 @@ ContentRendererClientQt::ContentRendererClientQt() #endif } -ContentRendererClientQt::~ContentRendererClientQt() -{ -} +ContentRendererClientQt::~ContentRendererClientQt() {} void ContentRendererClientQt::RenderThreadStarted() { content::RenderThread *renderThread = content::RenderThread::Get(); - (void)GetConnector(); m_renderThreadObserver.reset(new RenderThreadObserverQt()); m_visitedLinkSlave.reset(new visitedlink::VisitedLinkSlave); m_webCacheImpl.reset(new web_cache::WebCacheImpl()); @@ -137,10 +140,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()); @@ -151,34 +153,35 @@ void ContentRendererClientQt::RenderThreadStarted() #endif // Allow XMLHttpRequests from qrc to file. + // ### consider removing for Qt6 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 +198,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>(); @@ -255,29 +256,28 @@ bool ContentRendererClientQt::ShouldSuppressErrorPage(content::RenderFrame *fram void ContentRendererClientQt::PrepareErrorPage(content::RenderFrame *renderFrame, const blink::WebURLError &web_error, const std::string &httpMethod, - bool ignoring_cache, 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, const GURL &unreachable_url, const std::string &httpMethod, - bool ignoring_cache, int http_status, std::string *errorHtml) { - Q_UNUSED(ignoring_cache); GetNavigationErrorStringsInternal(renderFrame, httpMethod, error_page::Error::HttpError(unreachable_url, http_status), 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,42 +322,67 @@ 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 ¶ms, + blink::WebPlugin **plugin) { #if BUILDFLAG(ENABLE_EXTENSIONS) if (!ExtensionsRendererClientQt::GetInstance()->OverrideCreatePlugin(render_frame, params)) return false; #endif //ENABLE_EXTENSIONS + +#if BUILDFLAG(ENABLE_PLUGINS) + chrome::mojom::PluginInfoPtr plugin_info = chrome::mojom::PluginInfo::New(); + content::WebPluginInfo info; + std::string mime_type; + bool found = false; + + render_frame->Send(new FrameHostMsg_GetPluginInfo(render_frame->GetRoutingID(), params.url, + render_frame->GetWebFrame()->Top()->GetSecurityOrigin(), + params.mime_type.Utf8(), &found, &info, &mime_type)); + if (!found) { + *plugin = CreatePlugin(render_frame, params, *plugin_info); + return true; + } +#endif // BUILDFLAG(ENABLE_PLUGINS) return content::ContentRendererClient::OverrideCreatePlugin(render_frame, params, plugin); } -content::BrowserPluginDelegate* ContentRendererClientQt::CreateBrowserPluginDelegate(content::RenderFrame *render_frame, +#if BUILDFLAG(ENABLE_PLUGINS) +// static +blink::WebPlugin* ContentRendererClientQt::CreatePlugin(content::RenderFrame* render_frame, + const blink::WebPluginParams& original_params, + const chrome::mojom::PluginInfo& plugin_info) +{ + // If the browser plugin is to be enabled, this should be handled by the + // renderer, so the code won't reach here due to the early exit in OverrideCreatePlugin. + return LoadablePluginPlaceholderQt::CreateLoadableMissingPlugin(render_frame, original_params)->plugin(); +} +#endif //BUILDFLAG(ENABLE_PLUGINS) + +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, - mojo::ScopedMessagePipeHandle handle) +void ContentRendererClientQt::BindReceiverOnMainThread(mojo::GenericPendingReceiver receiver) { - Q_UNUSED(remote_info); - m_registry.TryBindInterface(name, &handle); + std::string interface_name = *receiver.interface_name(); + auto pipe = receiver.PassPipe(); + m_registry.TryBindInterface(interface_name, &pipe); } 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)); + content::RenderThread::Get()->BindHostReceiver(mojo::GenericPendingReceiver(interface_name, std::move(interface_pipe))); } // The following is based on chrome/renderer/media/chrome_key_systems.cc: @@ -367,7 +392,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 +428,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 +495,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 +512,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 +531,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,22 +598,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); -} - -void ContentRendererClientQt::CreateRendererService(service_manager::mojom::ServiceRequest service_request) -{ - DCHECK(!m_serviceBinding.is_bound()); - m_serviceBinding.Bind(std::move(service_request)); -} - -service_manager::Connector* ContentRendererClientQt::GetConnector() -{ - return m_serviceBinding.GetConnector(); + content::ContentRendererClient::WillSendRequest(frame, transition_type, url, initiator_origin, new_url, + attach_same_site_cookies); } -} // namespace +} // namespace QtWebEngineCore diff --git a/src/core/renderer/content_renderer_client_qt.h b/src/core/renderer/content_renderer_client_qt.h index a13d16b5c..da70d29a8 100644 --- a/src/core/renderer/content_renderer_client_qt.h +++ b/src/core/renderer/content_renderer_client_qt.h @@ -43,10 +43,13 @@ #include "content/public/renderer/content_renderer_client.h" #include "components/spellcheck/spellcheck_buildflags.h" #include "services/service_manager/public/cpp/binder_registry.h" -#include "services/service_manager/public/cpp/connector.h" #include "services/service_manager/public/cpp/local_interface_provider.h" -#include "services/service_manager/public/cpp/service.h" -#include "services/service_manager/public/cpp/service_binding.h" +#include "ppapi/buildflags/buildflags.h" + +#if BUILDFLAG(ENABLE_PLUGINS) +#include "qtwebengine/browser/plugin.mojom.h" +#include "third_party/blink/public/web/web_plugin_params.h" +#endif #include <QScopedPointer> @@ -70,13 +73,17 @@ class WebCacheImpl; class SpellCheck; #endif +namespace content { +struct WebPluginInfo; +} + 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::LocalInterfaceProvider { public: ContentRendererClientQt(); @@ -85,34 +92,35 @@ 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; void PrepareErrorPage(content::RenderFrame *render_frame, const blink::WebURLError &error, const std::string &http_method, - bool ignoring_cache, std::string *error_html) override; void PrepareErrorPageForHttpStatusError(content::RenderFrame *render_frame, const GURL &unreachable_url, const std::string &http_method, - bool ignoring_cache, int http_status, std::string *error_html) override; 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 ¶ms, + 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, @@ -121,24 +129,23 @@ public: GURL *new_url, bool *attach_same_site_cookies) override; - void CreateRendererService(service_manager::mojom::ServiceRequest service_request) override; + void BindReceiverOnMainThread(mojo::GenericPendingReceiver receiver) override; + +#if BUILDFLAG(ENABLE_PLUGINS) + static blink::WebPlugin* CreatePlugin(content::RenderFrame* render_frame, + const blink::WebPluginParams& params, + const chrome::mojom::PluginInfo& plugin_info); +#endif private: #if BUILDFLAG(ENABLE_SPELLCHECK) void InitSpellCheck(); #endif - service_manager::Connector *GetConnector(); - - // service_manager::Service: - void OnBindInterface(const service_manager::BindSourceInfo &remote_info, - const std::string &name, - 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; @@ -147,8 +154,6 @@ private: QScopedPointer<SpellCheck> m_spellCheck; #endif - service_manager::mojom::ConnectorRequest m_connectorRequest; - service_manager::ServiceBinding m_serviceBinding; service_manager::BinderRegistry m_registry; std::unique_ptr<network_hints::PrescientNetworkingDispatcher> m_prescientNetworkingDispatcher; 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/plugins/loadable_plugin_placeholder_qt.cpp b/src/core/renderer/plugins/loadable_plugin_placeholder_qt.cpp new file mode 100644 index 000000000..b43d9a24b --- /dev/null +++ b/src/core/renderer/plugins/loadable_plugin_placeholder_qt.cpp @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE: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 2015 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 "loadable_plugin_placeholder_qt.h" +#include "qtwebenginecoreglobal_p.h" + +#include "content/public/renderer/render_frame.h" +#include "components/strings/grit/components_strings.h" +#include "chrome/grit/renderer_resources.h" +#include "gin/handle.h" +#include "gin/wrappable.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/base/webui/jstemplate_builder.h" + +namespace QtWebEngineCore { + +// static +gin::WrapperInfo LoadablePluginPlaceholderQt::kWrapperInfo = {gin::kEmbedderNativeGin}; + +LoadablePluginPlaceholderQt::LoadablePluginPlaceholderQt(content::RenderFrame* render_frame, + const blink::WebPluginParams& params, + const std::string& html_data, + const base::string16& title) + : plugins::LoadablePluginPlaceholder(render_frame, params, html_data) + , context_menu_request_id_(0) +{} + +LoadablePluginPlaceholderQt::~LoadablePluginPlaceholderQt() +{ + if (context_menu_request_id_ && render_frame()) + render_frame()->CancelContextMenu(context_menu_request_id_); +} + +// TODO(bauerb): Move this method to NonLoadablePluginPlaceholder? +// static +LoadablePluginPlaceholderQt* LoadablePluginPlaceholderQt::CreateLoadableMissingPlugin(content::RenderFrame* render_frame, + const blink::WebPluginParams& params) +{ + const base::StringPiece template_html(ui::ResourceBundle::GetSharedInstance().GetRawDataResource(IDR_BLOCKED_PLUGIN_HTML)); + + base::DictionaryValue values; + values.SetString("name", ""); + values.SetString("message", l10n_util::GetStringUTF8(IDS_PLUGIN_NOT_SUPPORTED)); + + const std::string html_data = webui::GetI18nTemplateHtml(template_html, &values); + + // Will destroy itself when its WebViewPlugin is going away. + return new LoadablePluginPlaceholderQt(render_frame, params, html_data, params.mime_type.Utf16()); +} + +blink::WebPlugin* LoadablePluginPlaceholderQt::CreatePlugin() +{ + QT_NOT_YET_IMPLEMENTED + return nullptr; +} + +v8::Local<v8::Value> LoadablePluginPlaceholderQt::GetV8Handle(v8::Isolate* isolate) +{ + return gin::CreateHandle(isolate, this).ToV8(); +} + +} // namespace QtWebEngineCore diff --git a/src/core/net/custom_protocol_handler.h b/src/core/renderer/plugins/loadable_plugin_placeholder_qt.h index 625afc1d5..7bcad2d96 100644 --- a/src/core/net/custom_protocol_handler.h +++ b/src/core/renderer/plugins/loadable_plugin_placeholder_qt.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2020 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtWebEngine module of the Qt Toolkit. @@ -37,53 +37,48 @@ ** ****************************************************************************/ -// -// 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. -// +// Copyright 2015 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 CUSTOM_PROTOCOL_HANDLER_H_ -#define CUSTOM_PROTOCOL_HANDLER_H_ +#ifndef LOADALBLE_PLUGIN_PLACEHOLDER_QT_H +#define LOADALBLE_PLUGIN_PLACEHOLDER_QT_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 +#include "base/macros.h" +#include "components/plugins/renderer/loadable_plugin_placeholder.h" 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 +class LoadablePluginPlaceholderQt final : public plugins::LoadablePluginPlaceholder + , public gin::Wrappable<LoadablePluginPlaceholderQt> { - public: - CustomProtocolHandler(QPointer<ProfileAdapter> profileAdapter); + static gin::WrapperInfo kWrapperInfo; - net::URLRequestJob *MaybeCreateJob(net::URLRequest *request, net::NetworkDelegate *networkDelegate) const override; + // Creates a new WebViewPlugin with a MissingPlugin as a delegate. + static LoadablePluginPlaceholderQt* CreateLoadableMissingPlugin(content::RenderFrame* render_frame, + const blink::WebPluginParams& params); private: - DISALLOW_COPY_AND_ASSIGN(CustomProtocolHandler); - QPointer<ProfileAdapter> m_profileAdapter; + LoadablePluginPlaceholderQt(content::RenderFrame* render_frame, + const blink::WebPluginParams& params, + const std::string& html_data, + const base::string16& title); + ~LoadablePluginPlaceholderQt() override; + + // content::LoadablePluginPlaceholder overrides. + blink::WebPlugin* CreatePlugin() override; + void OnBlockedContent(content::RenderFrame::PeripheralContentStatus status, + bool is_same_origin) override {} + + // WebViewPlugin::Delegate (via PluginPlaceholder) methods: + v8::Local<v8::Value> GetV8Handle(v8::Isolate* isolate) override; + + int context_menu_request_id_; // Nonzero when request pending. + + DISALLOW_COPY_AND_ASSIGN(LoadablePluginPlaceholderQt); }; -} // namespace +} // namespace QtWebEngineCore -#endif // CUSTOM_PROTOCOL_HANDLER_H_ +#endif // LOADALBLE_PLUGIN_PLACEHOLDER_QT_H diff --git a/src/core/net/custom_protocol_handler.cpp b/src/core/renderer/plugins/plugin_placeholder_qt.cpp index 7e8ee47ab..a72c29873 100644 --- a/src/core/net/custom_protocol_handler.cpp +++ b/src/core/renderer/plugins/plugin_placeholder_qt.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2020 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtWebEngine module of the Qt Toolkit. @@ -37,26 +37,39 @@ ** ****************************************************************************/ -#include "custom_protocol_handler.h" -#include "url_request_custom_job.h" +// 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 file. -#include "net/base/net_errors.h" -#include "net/url_request/url_request.h" -#include "net/url_request/url_request_error_job.h" +#include "plugin_placeholder_qt.h" + +#include "content/public/renderer/render_frame.h" +#include "content/public/renderer/v8_value_converter.h" +#include "gin/object_template_builder.h" namespace QtWebEngineCore { -CustomProtocolHandler::CustomProtocolHandler(QPointer<ProfileAdapter> profileAdapter) - : m_profileAdapter(profileAdapter) +// static +gin::WrapperInfo PluginPlaceholderQt::kWrapperInfo = {gin::kEmbedderNativeGin}; + +PluginPlaceholderQt::PluginPlaceholderQt(content::RenderFrame* render_frame, + const blink::WebPluginParams& params, + const std::string& html_data) + : PluginPlaceholderBase(render_frame, params, html_data) +{} + +PluginPlaceholderQt::~PluginPlaceholderQt() {} + +v8::Local<v8::Value> PluginPlaceholderQt::GetV8Handle(v8::Isolate* isolate) { + return gin::CreateHandle(isolate, this).ToV8(); } -net::URLRequestJob *CustomProtocolHandler::MaybeCreateJob(net::URLRequest *request, net::NetworkDelegate *networkDelegate) const +gin::ObjectTemplateBuilder PluginPlaceholderQt::GetObjectTemplateBuilder(v8::Isolate* isolate) { - if (!networkDelegate) - return new net::URLRequestErrorJob(request, nullptr, net::ERR_ACCESS_DENIED); - - return new URLRequestCustomJob(request, networkDelegate, request->url().scheme(), m_profileAdapter); + return gin::Wrappable<PluginPlaceholderQt>::GetObjectTemplateBuilder(isolate) + .SetMethod<void (QtWebEngineCore::PluginPlaceholderQt::*)()>( + "hide", &PluginPlaceholderQt::HideCallback); } -} // namespace +} // namespace QtWebEngineCore diff --git a/src/core/net/url_request_context_getter_qt.cpp b/src/core/renderer/plugins/plugin_placeholder_qt.h index 6081a5e9f..a99c0d045 100644 --- a/src/core/net/url_request_context_getter_qt.cpp +++ b/src/core/renderer/plugins/plugin_placeholder_qt.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2020 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtWebEngine module of the Qt Toolkit. @@ -37,33 +37,42 @@ ** ****************************************************************************/ -#include "url_request_context_getter_qt.h" -#include "profile_io_data_qt.h" +// 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 file. -#include "base/task/post_task.h" -#include "content/public/browser/browser_task_traits.h" -#include "content/public/browser/browser_thread.h" +#ifndef PLUGIN_PLACEHOLDER_QT_H +#define PLUGIN_PLACEHOLDER_QT_H + +#include "base/macros.h" +#include "components/plugins/renderer/plugin_placeholder.h" +#include "gin/handle.h" +#include "gin/wrappable.h" +#include "third_party/blink/public/web/web_plugin_params.h" namespace QtWebEngineCore { -URLRequestContextGetterQt::URLRequestContextGetterQt(ProfileIODataQt *data) - : m_profileIOData(data) +// A basic placeholder that supports only hiding. +class PluginPlaceholderQt final : public plugins::PluginPlaceholderBase + , public gin::Wrappable<PluginPlaceholderQt> { -} +public: + static gin::WrapperInfo kWrapperInfo; -URLRequestContextGetterQt::~URLRequestContextGetterQt() -{ -} + PluginPlaceholderQt(content::RenderFrame* render_frame, + const blink::WebPluginParams& params, + const std::string& html_data); + ~PluginPlaceholderQt() override; -net::URLRequestContext *URLRequestContextGetterQt::GetURLRequestContext() -{ - Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); - return m_profileIOData->urlRequestContext(); -} +private: + // WebViewPlugin::Delegate methods: + v8::Local<v8::Value> GetV8Handle(v8::Isolate* isolate) final; -scoped_refptr<base::SingleThreadTaskRunner> URLRequestContextGetterQt::GetNetworkTaskRunner() const -{ - return base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::IO}); -} + // gin::Wrappable method: + gin::ObjectTemplateBuilder GetObjectTemplateBuilder( + v8::Isolate* isolate) override; +}; + +} // namespace QtWebEngineCore -} // namespace QtWebEngineCore +#endif // PLUGIN_PLACEHOLDER_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 ®istry_; } + service_manager::BinderRegistry *registry() { return ®istry_; } 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..4912ebfc2 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,9 +66,10 @@ void RenderThreadObserverQt::SetInitialConfiguration(bool is_incognito_process) m_isIncognitoProcess = is_incognito_process; } -void RenderThreadObserverQt::OnRendererConfigurationAssociatedRequest(qtwebengine::mojom::RendererConfigurationAssociatedRequest request) +void RenderThreadObserverQt::OnRendererConfigurationAssociatedRequest( + mojo::PendingAssociatedReceiver<qtwebengine::mojom::RendererConfiguration> receiver) { - m_rendererConfigurationBindings.AddBinding(this, std::move(request)); + m_rendererConfigurationReceivers.Add(this, std::move(receiver)); } } // namespace diff --git a/src/core/renderer/render_thread_observer_qt.h b/src/core/renderer/render_thread_observer_qt.h index 29b842ab4..05372049b 100644 --- a/src/core/renderer/render_thread_observer_qt.h +++ b/src/core/renderer/render_thread_observer_qt.h @@ -41,15 +41,17 @@ #define RENDER_THREAD_OBSERVER_QT_H #include "content/public/renderer/render_thread_observer.h" -#include "mojo/public/cpp/bindings/associated_binding_set.h" +#include "mojo/public/cpp/bindings/associated_receiver_set.h" +#include "mojo/public/cpp/bindings/pending_associated_receiver.h" #include "qtwebengine/common/renderer_configuration.mojom.h" 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; @@ -63,11 +65,12 @@ private: // qtwebengine::mojom::RendererConfiguration: void SetInitialConfiguration(bool is_incognito_process) override; - void OnRendererConfigurationAssociatedRequest(qtwebengine::mojom::RendererConfigurationAssociatedRequest request); + void OnRendererConfigurationAssociatedRequest( + mojo::PendingAssociatedReceiver<qtwebengine::mojom::RendererConfiguration> receiver); static bool m_isIncognitoProcess; - mojo::AssociatedBindingSet<qtwebengine::mojom::RendererConfiguration> m_rendererConfigurationBindings; + mojo::AssociatedReceiverSet<qtwebengine::mojom::RendererConfiguration> m_rendererConfigurationReceivers; DISALLOW_COPY_AND_ASSIGN(RenderThreadObserverQt); }; 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..1fb3bc678 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); @@ -165,30 +165,28 @@ void WebChannelTransport::NativeQtSendMessage(gin::Arguments *args) int size = 0; const char *rawData = doc.rawData(&size); - qtwebchannel::mojom::WebChannelTransportHostAssociatedPtr webChannelTransport; + mojo::AssociatedRemote<qtwebchannel::mojom::WebChannelTransportHost> webChannelTransport; renderFrame->GetRemoteAssociatedInterfaces()->GetInterface(&webChannelTransport); webChannelTransport->DispatchWebChannelMessage(std::vector<uint8_t>(rawData, rawData + size)); } 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::BindRepeating(&WebChannelIPCTransport::BindReceiver, base::Unretained(this))); } -void WebChannelIPCTransport::BindRequest( - qtwebchannel::mojom::WebChannelTransportRenderAssociatedRequest request) { - - m_binding.AddBinding(this, std::move(request)); +void WebChannelIPCTransport::BindReceiver( + mojo::PendingAssociatedReceiver<qtwebchannel::mojom::WebChannelTransportRender> receiver) +{ + m_receivers.Add(this, std::move(receiver)); } void WebChannelIPCTransport::SetWorldId(uint32_t worldId) @@ -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..15778a7bc 100644 --- a/src/core/renderer/web_channel_ipc_transport.h +++ b/src/core/renderer/web_channel_ipc_transport.h @@ -42,15 +42,18 @@ #include "content/public/renderer/render_frame_observer.h" #include "services/service_manager/public/cpp/binder_registry.h" -#include "mojo/public/cpp/bindings/associated_binding_set.h" +#include "mojo/public/cpp/bindings/associated_receiver_set.h" +#include "mojo/public/cpp/bindings/pending_associated_receiver.h" #include "qtwebengine/browser/qtwebchannel.mojom.h" #include <QtCore/qglobal.h> 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,13 +61,13 @@ 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; void DidClearWindowObject() override; void OnDestruct() override; - void BindRequest(qtwebchannel::mojom::WebChannelTransportRenderAssociatedRequest request); + void BindReceiver(mojo::PendingAssociatedReceiver<qtwebchannel::mojom::WebChannelTransportRender> receiver); private: // The worldId from our WebChannelIPCTransportHost or empty when there is no @@ -73,7 +76,7 @@ private: bool m_worldInitialized; // True means it's currently OK to manipulate the frame's script context. bool m_canUseContext = false; - mojo::AssociatedBindingSet<qtwebchannel::mojom::WebChannelTransportRender> m_binding; + mojo::AssociatedReceiverSet<qtwebchannel::mojom::WebChannelTransportRender> m_receivers; }; } // namespace diff --git a/src/core/renderer_host/pepper/pepper_isolated_file_system_message_filter.cpp b/src/core/renderer_host/pepper/pepper_isolated_file_system_message_filter.cpp index 5d7c3973f..bd93f0dd3 100644 --- a/src/core/renderer_host/pepper/pepper_isolated_file_system_message_filter.cpp +++ b/src/core/renderer_host/pepper/pepper_isolated_file_system_message_filter.cpp @@ -85,7 +85,7 @@ scoped_refptr<base::TaskRunner> PepperIsolatedFileSystemMessageFilter::OverrideT { // In order to reach ExtensionSystem, we need to get ProfileManager first. // ProfileManager lives in UI thread, so we need to do this in UI thread. - return base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::UI}); + return base::CreateSingleThreadTaskRunner({content::BrowserThread::UI}); } int32_t PepperIsolatedFileSystemMessageFilter::OnResourceMessageReceived(const IPC::Message& msg, ppapi::host::HostMessageContext *context) 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..c071a566a 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) @@ -101,7 +96,7 @@ void WebChannelIPCTransportHost::sendMessage(const QJsonObject &message) int size = 0; const char *rawData = doc.rawData(&size); content::RenderFrameHost *frame = web_contents()->GetMainFrame(); - qtwebchannel::mojom::WebChannelTransportRenderAssociatedPtr webChannelTransport; + mojo::AssociatedRemote<qtwebchannel::mojom::WebChannelTransportRender> webChannelTransport; frame->GetRemoteAssociatedInterfaces()->GetInterface(&webChannelTransport); qCDebug(log).nospace() << "sending webchannel message to " << frame << ": " << doc; webChannelTransport->DispatchWebChannelMessage(std::vector<uint8_t>(rawData, rawData + size), m_worldId); @@ -121,7 +116,7 @@ void WebChannelIPCTransportHost::setWorldId(content::RenderFrameHost *frame, uin if (!frame->IsRenderFrameLive()) return; qCDebug(log).nospace() << "sending setWorldId(" << worldId << ") message to " << frame; - qtwebchannel::mojom::WebChannelTransportRenderAssociatedPtr webChannelTransport; + mojo::AssociatedRemote<qtwebchannel::mojom::WebChannelTransportRender> webChannelTransport; frame->GetRemoteAssociatedInterfaces()->GetInterface(&webChannelTransport); webChannelTransport->SetWorldId(worldId); } @@ -131,7 +126,7 @@ void WebChannelIPCTransportHost::resetWorldId() for (content::RenderFrameHost *frame : web_contents()->GetAllFrames()) { if (!frame->IsRenderFrameLive()) return; - qtwebchannel::mojom::WebChannelTransportRenderAssociatedPtr webChannelTransport; + mojo::AssociatedRemote<qtwebchannel::mojom::WebChannelTransportRender> webChannelTransport; frame->GetRemoteAssociatedInterfaces()->GetInterface(&webChannelTransport); webChannelTransport->ResetWorldId(); } @@ -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/service/service_qt.cpp b/src/core/service/service_qt.cpp deleted file mode 100644 index 83948e396..000000000 --- a/src/core/service/service_qt.cpp +++ /dev/null @@ -1,169 +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$ -** -****************************************************************************/ - -// based on chrome/browser/chrome_service.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 "service_qt.h" - -#include "base/no_destructor.h" -#include "base/task/post_task.h" -#include "components/spellcheck/spellcheck_buildflags.h" -#include "content/public/browser/browser_task_traits.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/content_browser_client.h" -#include "services/service_manager/public/cpp/binder_registry.h" -#include "services/service_manager/public/cpp/connector.h" -#include "services/service_manager/public/cpp/service.h" -#include "services/service_manager/public/cpp/service_binding.h" - -#if BUILDFLAG(ENABLE_SPELLCHECK) -#include "chrome/browser/spellchecker/spell_check_host_chrome_impl.h" -#endif - -class ServiceQt::IOThreadContext : public service_manager::Service { -public: - IOThreadContext(); - ~IOThreadContext() override = default; - - void BindServiceRequest(service_manager::mojom::ServiceRequest request); - void BindConnector(service_manager::mojom::ConnectorRequest connector_request); - -private: - void BindConnectorOnIOThread(service_manager::mojom::ConnectorRequest connector_request); - - // service_manager::Service: - void OnStart() override; - void OnBindInterface(const service_manager::BindSourceInfo &remote_info, - const std::string &name, - mojo::ScopedMessagePipeHandle handle) override; - - service_manager::mojom::ConnectorRequest m_connectorRequest; - service_manager::ServiceBinding m_serviceBinding{this}; - service_manager::BinderRegistry m_registry; - service_manager::BinderRegistryWithArgs<const service_manager::BindSourceInfo&> m_registry_with_source_info; - - DISALLOW_COPY_AND_ASSIGN(IOThreadContext); -}; - -ServiceQt::IOThreadContext::IOThreadContext() -{ -#if BUILDFLAG(ENABLE_SPELLCHECK) - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner = - base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::UI}); - m_registry_with_source_info.AddInterface(base::BindRepeating(&SpellCheckHostChromeImpl::Create), ui_task_runner); -#endif -} - -void ServiceQt::IOThreadContext::BindServiceRequest(service_manager::mojom::ServiceRequest request) -{ - m_serviceBinding.Bind(std::move(request)); -} - -void ServiceQt::IOThreadContext::BindConnector(service_manager::mojom::ConnectorRequest connector_request) -{ - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - - // NOTE: It's not safe to modify |connector_request_| here since it's read - // on the IO thread. Post a task instead. As long as this task is posted - // before any code attempts to connect to the chrome service, there's no - // race. - base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::IO})->PostTask( - FROM_HERE, - base::BindOnce(&IOThreadContext::BindConnectorOnIOThread, - base::Unretained(this), - std::move(connector_request))); -} - -void ServiceQt::IOThreadContext::BindConnectorOnIOThread(service_manager::mojom::ConnectorRequest connector_request) -{ - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - m_connectorRequest = std::move(connector_request); -} - -void ServiceQt::IOThreadContext::OnStart() -{ - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - DCHECK(m_connectorRequest.is_pending()); - m_serviceBinding.GetConnector()->BindConnectorRequest(std::move(m_connectorRequest)); -} - -void ServiceQt::IOThreadContext::OnBindInterface(const service_manager::BindSourceInfo &remote_info, - const std::string &name, - mojo::ScopedMessagePipeHandle handle) -{ - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - content::OverrideOnBindInterface(remote_info, name, &handle); - if (!handle.is_valid()) - return; - - if (!m_registry.TryBindInterface(name, &handle)) - m_registry_with_source_info.TryBindInterface(name, &handle, remote_info); -} - -ServiceQt *ServiceQt::GetInstance() -{ - static base::NoDestructor<ServiceQt> service; - return service.get(); -} - -content::ServiceManagerConnection::ServiceRequestHandler ServiceQt::CreateServiceQtRequestHandler() -{ - return base::BindRepeating(&ServiceQt::BindServiceQtRequest, base::Unretained(this)); -} - -ServiceQt::ServiceQt() : m_ioThreadContext(std::make_unique<IOThreadContext>()) -{} - -ServiceQt::~ServiceQt() = default; - -void ServiceQt::InitConnector() -{ - service_manager::mojom::ConnectorRequest request; - m_connector = service_manager::Connector::Create(&request); - m_ioThreadContext->BindConnector(std::move(request)); -} - -void ServiceQt::BindServiceQtRequest(service_manager::mojom::ServiceRequest request) -{ - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - m_ioThreadContext->BindServiceRequest(std::move(request)); -} diff --git a/src/core/service/service_qt.h b/src/core/service/service_qt.h deleted file mode 100644 index d4c89065c..000000000 --- a/src/core/service/service_qt.h +++ /dev/null @@ -1,75 +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 SERVICE_QT_H -#define SERVICE_QT_H - -#include "base/no_destructor.h" -#include "content/public/common/service_manager_connection.h" - -namespace service_manager { -class Connector; -class Service; -} // namespace service_manager - -class ServiceQt { -public: - static ServiceQt *GetInstance(); - - void InitConnector(); - content::ServiceManagerConnection::ServiceRequestHandler CreateServiceQtRequestHandler(); - service_manager::Connector *connector() { return m_connector.get(); } - -private: - friend class base::NoDestructor<ServiceQt>; - class IOThreadContext; - - ServiceQt(); - ~ServiceQt(); - - void BindServiceQtRequest(service_manager::mojom::ServiceRequest request); - - const std::unique_ptr<IOThreadContext> m_ioThreadContext; - - std::unique_ptr<service_manager::Connector> m_connector; - - DISALLOW_COPY_AND_ASSIGN(ServiceQt); -}; - -#endif // SERVICE_QT_H diff --git a/src/core/type_conversion.cpp b/src/core/type_conversion.cpp index aea924dbd..403125784 100644 --- a/src/core/type_conversion.cpp +++ b/src/core/type_conversion.cpp @@ -59,6 +59,11 @@ QImage toQImage(const SkBitmap &bitmap) case kRGBA_F16_SkColorType: case kRGBA_F32_SkColorType: case kRGBA_F16Norm_SkColorType: + case kR8G8_unorm_SkColorType: + case kA16_float_SkColorType: + case kA16_unorm_SkColorType: + case kR16G16_float_SkColorType: + case kR16G16_unorm_SkColorType: qWarning("Unknown or unsupported skia image format"); break; case kAlpha_8_SkColorType: @@ -131,6 +136,21 @@ QImage toQImage(const SkBitmap &bitmap) case kGray_8_SkColorType: image = toQImage(bitmap, QImage::Format_Grayscale8); break; + case kR16G16B16A16_unorm_SkColorType: + switch (bitmap.alphaType()) { + case kUnknown_SkAlphaType: + break; + case kUnpremul_SkAlphaType: + image = toQImage(bitmap, QImage::Format_RGBA64); + break; + case kOpaque_SkAlphaType: + image = toQImage(bitmap, QImage::Format_RGBX64); + break; + case kPremul_SkAlphaType: + image = toQImage(bitmap, QImage::Format_RGBA64_Premultiplied); + break; + } + break; } return image; } diff --git a/src/core/user_notification_controller.cpp b/src/core/user_notification_controller.cpp index 50d12e8fd..f94605a55 100644 --- a/src/core/user_notification_controller.cpp +++ b/src/core/user_notification_controller.cpp @@ -43,6 +43,7 @@ #include "base/callback.h" #include "content/public/browser/notification_event_dispatcher.h" +#include "third_party/blink/public/mojom/notifications/notification.mojom-shared.h" #include "third_party/blink/public/common/notifications/notification_resources.h" #include "third_party/blink/public/common/notifications/platform_notification_data.h" #include "ui/message_center/public/cpp/notification_delegate.h" diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp index 31731fe99..138ea7039 100644 --- a/src/core/web_contents_adapter.cpp +++ b/src/core/web_contents_adapter.cpp @@ -85,8 +85,9 @@ #include "content/public/common/page_zoom.h" #include "content/public/common/url_constants.h" #include "content/public/common/web_preferences.h" -#include "content/public/common/webrtc_ip_handling_policy.h" #include "extensions/buildflags/buildflags.h" +#include "third_party/blink/public/common/page/page_zoom.h" +#include "third_party/blink/public/common/peerconnection/webrtc_ip_handling_policy.h" #include "third_party/blink/public/web/web_media_player_action.h" #include "printing/buildflags/buildflags.h" #include "ui/base/clipboard/clipboard.h" @@ -240,7 +241,6 @@ static std::unique_ptr<content::WebContents> createBlankWebContents(WebContentsA { content::WebContents::CreateParams create_params(browserContext, NULL); create_params.routing_id = MSG_ROUTING_NONE; - create_params.initial_size = gfx::Size(kTestWindowWidth, kTestWindowHeight); create_params.initially_hidden = true; std::unique_ptr<content::WebContents> webContents = content::WebContents::Create(create_params); @@ -502,7 +502,6 @@ void WebContentsAdapter::initialize(content::SiteInstance *site) // Create our own if a WebContents wasn't provided at construction. if (!m_webContents) { content::WebContents::CreateParams create_params(m_profileAdapter->profile(), site); - create_params.initial_size = gfx::Size(kTestWindowWidth, kTestWindowHeight); create_params.initially_hidden = true; m_webContents = content::WebContents::Create(create_params); } @@ -563,8 +562,8 @@ void WebContentsAdapter::initializeRenderPrefs() else rendererPrefs->webrtc_ip_handling_policy = m_adapterClient->webEngineSettings()->testAttribute(WebEngineSettings::WebRTCPublicInterfacesOnly) - ? content::kWebRTCIPHandlingDefaultPublicInterfaceOnly - : content::kWebRTCIPHandlingDefault; + ? blink::kWebRTCIPHandlingDefaultPublicInterfaceOnly + : blink::kWebRTCIPHandlingDefault; #endif // Set web-contents font settings to the default font settings as Chromium constantly overrides // the global font defaults with the font settings of the latest web-contents created. @@ -575,7 +574,7 @@ void WebContentsAdapter::initializeRenderPrefs() rendererPrefs->use_autohinter = params.autohinter; rendererPrefs->use_bitmaps = params.use_bitmaps; rendererPrefs->subpixel_rendering = params.subpixel_rendering; - m_webContents->GetRenderViewHost()->SyncRendererPrefs(); + m_webContents->SyncRendererPrefs(); } bool WebContentsAdapter::canGoBack() const @@ -725,8 +724,8 @@ void WebContentsAdapter::load(const QWebEngineHttpRequest &request) if (resizeNeeded) { // Schedule navigation on the event loop. - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(&NavigateTask, sharedFromThis().toWeakRef(), std::move(params))); + base::PostTask(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(&NavigateTask, sharedFromThis().toWeakRef(), std::move(params))); } else { Navigate(this, params); } @@ -974,10 +973,10 @@ void WebContentsAdapter::serializeNavigationHistory(QDataStream &output) void WebContentsAdapter::setZoomFactor(qreal factor) { CHECK_INITIALIZED(); - if (factor < content::kMinimumZoomFactor || factor > content::kMaximumZoomFactor) + if (factor < blink::kMinimumPageZoomFactor || factor > blink::kMaximumPageZoomFactor) return; - double zoomLevel = content::ZoomFactorToZoomLevel(static_cast<double>(factor)); + double zoomLevel = blink::PageZoomFactorToZoomLevel(static_cast<double>(factor)); content::HostZoomMap *zoomMap = content::HostZoomMap::GetForWebContents(m_webContents.get()); if (zoomMap) { @@ -990,7 +989,7 @@ void WebContentsAdapter::setZoomFactor(qreal factor) qreal WebContentsAdapter::currentZoomFactor() const { CHECK_INITIALIZED(1); - return content::ZoomLevelToZoomFactor(content::HostZoomMap::GetZoomLevel(m_webContents.get())); + return blink::PageZoomLevelToZoomFactor(content::HostZoomMap::GetZoomLevel(m_webContents.get())); } ProfileQt* WebContentsAdapter::profile() @@ -1149,6 +1148,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..e97536593 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()); + } } } @@ -476,11 +483,12 @@ void WebContentsDelegateQt::DidFinishLoad(content::RenderFrameHost* render_frame Q_ASSERT(validated_url.is_valid()); if (validated_url.spec() == content::kUnreachableWebDataURL) { m_loadingErrorFrameList.removeOne(render_frame_host->GetRoutingID()); - m_viewClient->iconChanged(QUrl()); // Trigger LoadFinished signal for main frame's error page only. - if (!render_frame_host->GetParent()) + if (!render_frame_host->GetParent()) { + m_viewClient->iconChanged(QUrl()); EmitLoadFinished(true /* success */, toQt(validated_url), true /* isErrorPage */); + } return; } @@ -538,7 +546,7 @@ content::JavaScriptDialogManager *WebContentsDelegateQt::GetJavaScriptDialogMana return JavaScriptDialogManagerQt::GetInstance(); } -void WebContentsDelegateQt::EnterFullscreenModeForTab(content::WebContents *web_contents, const GURL& origin, const blink::WebFullscreenOptions &) +void WebContentsDelegateQt::EnterFullscreenModeForTab(content::WebContents *web_contents, const GURL& origin, const blink::mojom::FullscreenOptions &) { Q_UNUSED(web_contents); if (!m_viewClient->isFullScreenMode()) diff --git a/src/core/web_contents_delegate_qt.h b/src/core/web_contents_delegate_qt.h index 33fd49b3d..6818fa551 100644 --- a/src/core/web_contents_delegate_qt.h +++ b/src/core/web_contents_delegate_qt.h @@ -128,7 +128,7 @@ public: void WebContentsCreated(content::WebContents *source_contents, int opener_render_process_id, int opener_render_frame_id, const std::string &frame_name, const GURL &target_url, content::WebContents *new_contents) override; content::JavaScriptDialogManager *GetJavaScriptDialogManager(content::WebContents *source) override; - void EnterFullscreenModeForTab(content::WebContents *web_contents, const GURL &origin, const blink::WebFullscreenOptions &) override; + void EnterFullscreenModeForTab(content::WebContents *web_contents, const GURL &origin, const blink::mojom::FullscreenOptions &options) override; void ExitFullscreenModeForTab(content::WebContents*) override; bool IsFullscreenForTabOrPending(const content::WebContents* web_contents) override; void RunFileChooser(content::RenderFrameHost* render_frame_host, diff --git a/src/core/web_contents_view_qt.cpp b/src/core/web_contents_view_qt.cpp index e50835f74..139450cd8 100644 --- a/src/core/web_contents_view_qt.cpp +++ b/src/core/web_contents_view_qt.cpp @@ -111,7 +111,7 @@ content::RenderWidgetHostViewBase* WebContentsViewQt::CreateViewForChildWidget(c return view; } -void WebContentsViewQt::CreateView(const gfx::Size& initial_size, gfx::NativeView context) +void WebContentsViewQt::CreateView(gfx::NativeView context) { } diff --git a/src/core/web_contents_view_qt.h b/src/core/web_contents_view_qt.h index 978a2ce2e..6f5ffdb86 100644 --- a/src/core/web_contents_view_qt.h +++ b/src/core/web_contents_view_qt.h @@ -72,7 +72,7 @@ public: // content::WebContentsView overrides: content::RenderWidgetHostViewBase *CreateViewForWidget(content::RenderWidgetHost* render_widget_host, bool is_guest_view_hack) override; - void CreateView(const gfx::Size& initial_size, gfx::NativeView context) override; + void CreateView(gfx::NativeView context) override; content::RenderWidgetHostViewBase *CreateViewForChildWidget(content::RenderWidgetHost* render_widget_host) override; diff --git a/src/core/web_engine_context.cpp b/src/core/web_engine_context.cpp index 286842a20..5937d3b21 100644 --- a/src/core/web_engine_context.cpp +++ b/src/core/web_engine_context.cpp @@ -47,8 +47,10 @@ #include "base/run_loop.h" #include "base/task/post_task.h" #include "base/task/sequence_manager/thread_controller_with_message_pump_impl.h" +#include "base/task/thread_pool/thread_pool_instance.h" #include "base/threading/thread_restrictions.h" #include "cc/base/switches.h" +#include "chrome/common/chrome_switches.h" #include "content/gpu/gpu_child_thread.h" #include "content/browser/compositor/surface_utils.h" #include "components/viz/host/host_frame_sink_manager.h" @@ -76,6 +78,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" @@ -85,8 +88,9 @@ #include "ppapi/buildflags/buildflags.h" #include "services/network/public/cpp/features.h" #include "services/network/public/cpp/network_switches.h" -#include "services/resource_coordinator/public/cpp/resource_coordinator_features.h" +#include "services/network/public/mojom/network_context.mojom.h" #include "services/service_manager/sandbox/switches.h" +#include "services/tracing/public/cpp/trace_startup.h" #include "services/tracing/public/cpp/tracing_features.h" #include "third_party/blink/public/common/features.h" #include "ui/events/event_switches.h" @@ -214,6 +218,26 @@ 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() || + !pac_url.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive))) { + QFile file; + if (pac_url.isLocalFile()) + file.setFileName(pac_url.toLocalFile()); + else + file.setFileName(pac_url.path().prepend(QChar(':'))); + 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()); + } + } + } +} + static bool waitForViz = false; static void completeVizCleanup() { @@ -491,7 +515,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. @@ -506,20 +530,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 @@ -547,6 +557,11 @@ WebEngineContext::WebEngineContext() bool threadedGpu = false; #ifndef QT_NO_OPENGL threadedGpu = QOpenGLContext::supportsThreadedOpenGL(); +#if defined(Q_OS_MACOS) + // QtBase disabled it when building on 10.14+, unfortunately we still need it + // until we have fixed single-threaded viz-display-compositor. + threadedGpu = true; +#endif #endif threadedGpu = threadedGpu && !qEnvironmentVariableIsSet(kDisableInProcGpuThread); @@ -568,10 +583,10 @@ 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); - // BlinkGenPropertyTrees is enabled by default in 75, but causes regressions. - appendToFeatureList(disableFeatures, blink::features::kBlinkGenPropertyTrees.name); +#if defined(Q_OS_LINUX) + // broken and crashy (even upstream): + appendToFeatureList(disableFeatures, features::kFontSrcLocalMatching.name); +#endif #if QT_CONFIG(webengine_printing_and_pdf) appendToFeatureList(disableFeatures, printing::features::kUsePdfCompositorServiceForPrint.name); @@ -579,12 +594,12 @@ WebEngineContext::WebEngineContext() // Explicitly tell Chromium about default-on features we do not support appendToFeatureList(disableFeatures, features::kBackgroundFetch.name); - appendToFeatureList(disableFeatures, features::kOriginTrials.name); appendToFeatureList(disableFeatures, features::kSmsReceiver.name); appendToFeatureList(disableFeatures, features::kWebAuth.name); appendToFeatureList(disableFeatures, features::kWebAuthCable.name); appendToFeatureList(disableFeatures, features::kWebPayments.name); appendToFeatureList(disableFeatures, features::kWebUsb.name); + appendToFeatureList(disableFeatures, media::kPictureInPicture.name); if (useEmbeddedSwitches) { // embedded switches are based on the switches for Android, see content/browser/android/content_startup_flags.cc @@ -595,6 +610,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. @@ -715,6 +745,7 @@ WebEngineContext::WebEngineContext() m_mainDelegate->PostEarlyInitialization(false); content::StartBrowserThreadPool(); content::BrowserTaskExecutor::PostFeatureListSetup(); + tracing::InitTracingPostThreadPoolStartAndFeatureList(); m_discardableSharedMemoryManager = std::make_unique<discardable_memory::DiscardableSharedMemoryManager>(); m_serviceManagerEnvironment = std::make_unique<content::ServiceManagerEnvironment>(content::BrowserTaskExecutor::CreateIOThread()); m_startupData = m_serviceManagerEnvironment->CreateBrowserStartupData(); diff --git a/src/core/web_engine_context_threads.cpp b/src/core/web_engine_context_threads.cpp index c7440dd3f..ba3e18499 100644 --- a/src/core/web_engine_context_threads.cpp +++ b/src/core/web_engine_context_threads.cpp @@ -65,13 +65,13 @@ struct GpuThreadControllerQt : content::GpuThreadController { GpuThreadControllerQt(const content::InProcessChildThreadParams ¶ms, const gpu::GpuPreferences &gpuPreferences) { - base::PostTaskWithTraits( + base::PostTask( FROM_HERE, { content::BrowserThread::UI }, base::BindOnce(&GpuThreadControllerQt::createGpuProcess, params, gpuPreferences)); } ~GpuThreadControllerQt() override { - base::PostTaskWithTraits( + base::PostTask( FROM_HERE, { content::BrowserThread::UI }, base::BindOnce(&GpuThreadControllerQt::destroyGpuProcess)); } diff --git a/src/core/web_engine_settings.cpp b/src/core/web_engine_settings.cpp index 49006ec20..eb6db9793 100644 --- a/src/core/web_engine_settings.cpp +++ b/src/core/web_engine_settings.cpp @@ -50,8 +50,8 @@ #include "content/public/browser/web_contents.h" #include "content/public/common/content_switches.h" #include "content/public/common/web_preferences.h" -#include "content/public/common/webrtc_ip_handling_policy.h" #include "media/base/media_switches.h" +#include "third_party/blink/public/common/peerconnection/webrtc_ip_handling_policy.h" #include "third_party/blink/public/mojom/renderer_preferences.mojom.h" #include "ui/base/ui_base_switches.h" #include "ui/events/event_switches.h" @@ -128,9 +128,8 @@ void WebEngineSettings::overrideWebPreferences(content::WebContents *webContents webPreferences.reset(new content::WebPreferences(*prefs)); if (webContents - && webContents->GetRenderViewHost() && applySettingsToRendererPreferences(webContents->GetMutableRendererPrefs())) { - webContents->GetRenderViewHost()->SyncRendererPrefs(); + webContents->SyncRendererPrefs(); } } @@ -248,7 +247,7 @@ void WebEngineSettings::initDefaults() s_defaultAttributes.insert(LinksIncludedInFocusChain, true); s_defaultAttributes.insert(LocalStorageEnabled, true); s_defaultAttributes.insert(LocalContentCanAccessRemoteUrls, false); - s_defaultAttributes.insert(XSSAuditingEnabled, true); + s_defaultAttributes.insert(XSSAuditingEnabled, false); s_defaultAttributes.insert(SpatialNavigationEnabled, false); s_defaultAttributes.insert(LocalContentCanAccessFileUrls, true); s_defaultAttributes.insert(HyperlinkAuditingEnabled, false); @@ -340,7 +339,7 @@ void WebEngineSettings::doApply() m_adapter->updateWebPreferences(*webPreferences.data()); if (applySettingsToRendererPreferences(m_adapter->webContents()->GetMutableRendererPrefs())) - m_adapter->webContents()->GetRenderViewHost()->SyncRendererPrefs(); + m_adapter->webContents()->SyncRendererPrefs(); } void WebEngineSettings::applySettingsToWebPreferences(content::WebPreferences *prefs) @@ -366,7 +365,6 @@ void WebEngineSettings::applySettingsToWebPreferences(content::WebPreferences *p prefs->local_storage_enabled = testAttribute(LocalStorageEnabled); prefs->databases_enabled = testAttribute(LocalStorageEnabled); prefs->allow_universal_access_from_file_urls = testAttribute(LocalContentCanAccessRemoteUrls); - prefs->xss_auditor_enabled = testAttribute(XSSAuditingEnabled); prefs->spatial_navigation_enabled = testAttribute(SpatialNavigationEnabled); prefs->allow_file_access_from_file_urls = testAttribute(LocalContentCanAccessFileUrls); prefs->hyperlink_auditing_enabled = testAttribute(HyperlinkAuditingEnabled); @@ -405,12 +403,6 @@ void WebEngineSettings::applySettingsToWebPreferences(content::WebPreferences *p // Set the theme colors. Based on chrome_content_browser_client.cc: const ui::NativeTheme *webTheme = ui::NativeTheme::GetInstanceForWeb(); if (webTheme) { -#if !defined(OS_MACOSX) - // Mac has a concept of high contrast that does not relate to forced colors. - prefs->forced_colors = webTheme->UsesHighContrastColors() - ? blink::ForcedColors::kActive - : blink::ForcedColors::kNone; -#endif // !defined(OS_MACOSX) switch (webTheme->GetPreferredColorScheme()) { case ui::NativeTheme::PreferredColorScheme::kDark: prefs->preferred_color_scheme = blink::PreferredColorScheme::kDark; @@ -453,8 +445,8 @@ bool WebEngineSettings::applySettingsToRendererPreferences(blink::mojom::Rendere #if QT_CONFIG(webengine_webrtc) if (!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kForceWebRtcIPHandlingPolicy)) { std::string webrtc_ip_handling_policy = testAttribute(WebEngineSettings::WebRTCPublicInterfacesOnly) - ? content::kWebRTCIPHandlingDefaultPublicInterfaceOnly - : content::kWebRTCIPHandlingDefault; + ? blink::kWebRTCIPHandlingDefaultPublicInterfaceOnly + : blink::kWebRTCIPHandlingDefault; if (prefs->webrtc_ip_handling_policy != webrtc_ip_handling_policy) { prefs->webrtc_ip_handling_policy = webrtc_ip_handling_policy; changed = true; diff --git a/src/core/web_event_factory.cpp b/src/core/web_event_factory.cpp index f4940f305..f37cce6c7 100644 --- a/src/core/web_event_factory.cpp +++ b/src/core/web_event_factory.cpp @@ -1510,8 +1510,9 @@ blink::WebMouseWheelEvent WebEventFactory::toWebWheelEvent(QWheelEvent *ev) webEvent.wheel_ticks_y = static_cast<float>(ev->angleDelta().y()) / QWheelEvent::DefaultDeltasPerStep; webEvent.phase = toBlinkPhase(ev); #if defined(Q_OS_DARWIN) - // has_precise_scrolling_deltas is a macOS term meaning it is a system scroll gesture, see qnsview_mouse.mm - webEvent.has_precise_scrolling_deltas = (ev->source() == Qt::MouseEventSynthesizedBySystem); + // PrecisePixel is a macOS term meaning it is a system scroll gesture, see qnsview_mouse.mm + if (ev->source() == Qt::MouseEventSynthesizedBySystem) + webEvent.delta_units = ui::input_types::ScrollGranularity::kScrollByPrecisePixel; #endif setBlinkWheelEventDelta(webEvent); @@ -1528,7 +1529,8 @@ bool WebEventFactory::coalesceWebWheelEvent(blink::WebMouseWheelEvent &webEvent, if (toBlinkPhase(ev) != webEvent.phase) return false; #if defined(Q_OS_DARWIN) - if (webEvent.has_precise_scrolling_deltas != (ev->source() == Qt::MouseEventSynthesizedBySystem)) + if ((webEvent.delta_units == ui::input_types::ScrollGranularity::kScrollByPrecisePixel) + != (ev->source() == Qt::MouseEventSynthesizedBySystem)) return false; #endif diff --git a/src/pdf/api/qpdfbookmarkmodel.h b/src/pdf/api/qpdfbookmarkmodel.h new file mode 100644 index 000000000..8e06a1547 --- /dev/null +++ b/src/pdf/api/qpdfbookmarkmodel.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** 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 QPDFBOOKMARKMODEL_H +#define QPDFBOOKMARKMODEL_H + +#include <QtPdf/qtpdfglobal.h> +#include <QtCore/qabstractitemmodel.h> + +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..f9c186ff6 --- /dev/null +++ b/src/pdf/api/qpdfdestination.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** 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(); + QPdfDestination(const QPdfDestination &other); + QPdfDestination &operator=(const QPdfDestination &other); + QPdfDestination(QPdfDestination &&other) noexcept; + 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..f80a7832b --- /dev/null +++ b/src/pdf/api/qpdfdocument.h @@ -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$ +** +****************************************************************************/ + +#ifndef QPDFDOCUMENT_H +#define QPDFDOCUMENT_H + +#include <QtPdf/qtpdfglobal.h> + +#include <QtCore/qobject.h> +#include <QtGui/qimage.h> +#include <QtPdf/qpdfdocumentrenderoptions.h> +#include <QtPdf/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_INVOKABLE QPdfSelection getAllText(int page); + +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..b69b6f19e --- /dev/null +++ b/src/pdf/api/qpdfdocument_p.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** 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 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 <QtCore/qbuffer.h> +#include <QtCore/qmutex.h> +#include <QtCore/qpointer.h> +#include <QtNetwork/qnetworkreply.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(); + QString getText(FPDF_TEXTPAGE textPage, int startIndex, int count); +}; + +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..cafb4716f --- /dev/null +++ b/src/pdf/api/qpdfdocumentrenderoptions.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> +** 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 QPDFDOCUMENTRENDEROPTIONS_H +#define QPDFDOCUMENTRENDEROPTIONS_H + +#include <QtPdf/qpdfnamespace.h> +#include <QtCore/qobject.h> +#include <QtCore/qrect.h> + +QT_BEGIN_NAMESPACE + +class QPdfDocumentRenderOptions +{ +public: + Q_DECL_CONSTEXPR QPdfDocumentRenderOptions() noexcept : m_renderFlags(0), m_rotation(0), m_reserved(0) {} + + Q_DECL_CONSTEXPR QPdf::Rotation rotation() const noexcept { return static_cast<QPdf::Rotation>(m_rotation); } + Q_DECL_RELAXED_CONSTEXPR void setRotation(QPdf::Rotation r) noexcept { m_rotation = r; } + + Q_DECL_CONSTEXPR QPdf::RenderFlags renderFlags() const noexcept { return static_cast<QPdf::RenderFlags>(m_renderFlags); } + Q_DECL_RELAXED_CONSTEXPR void setRenderFlags(QPdf::RenderFlags r) noexcept { m_renderFlags = r; } + + Q_DECL_CONSTEXPR QRect scaledClipRect() const noexcept { return m_clipRect; } + Q_DECL_RELAXED_CONSTEXPR void setScaledClipRect(const QRect &r) noexcept { m_clipRect = r; } + + Q_DECL_CONSTEXPR QSize scaledSize() const noexcept { return m_scaledSize; } + Q_DECL_RELAXED_CONSTEXPR void setScaledSize(const QSize &s) noexcept { m_scaledSize = s; } + +private: + friend Q_DECL_CONSTEXPR inline bool operator==(QPdfDocumentRenderOptions lhs, QPdfDocumentRenderOptions rhs) noexcept; + + QRect m_clipRect; + QSize m_scaledSize; + + quint32 m_renderFlags : 8; + quint32 m_rotation : 3; + quint32 m_reserved : 21; + quint32 m_reserved2 = 0; +}; + +Q_DECLARE_TYPEINFO(QPdfDocumentRenderOptions, Q_PRIMITIVE_TYPE); + +Q_DECL_CONSTEXPR inline bool operator==(QPdfDocumentRenderOptions lhs, QPdfDocumentRenderOptions rhs) noexcept +{ + return lhs.m_clipRect == rhs.m_clipRect && lhs.m_scaledSize == rhs.m_scaledSize && + lhs.m_renderFlags == rhs.m_renderFlags && lhs.m_rotation == rhs.m_rotation && + lhs.m_reserved == rhs.m_reserved && lhs.m_reserved2 == rhs.m_reserved2; // fix -Wunused-private-field +} + +Q_DECL_CONSTEXPR inline bool operator!=(QPdfDocumentRenderOptions lhs, QPdfDocumentRenderOptions rhs) noexcept +{ + 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..e76d0abd9 --- /dev/null +++ b/src/pdf/api/qpdfnamespace.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> +** 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 QPDFNAMESPACE_H +#define QPDFNAMESPACE_H + +#include <QtCore/qobject.h> + +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..0f416bf77 --- /dev/null +++ b/src/pdf/api/qpdfpagenavigation.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> +** 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 QPDFPAGENAVIGATION_H +#define QPDFPAGENAVIGATION_H + +#include <QtPdf/qtpdfglobal.h> +#include <QtCore/qobject.h> + +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..c7b8de0df --- /dev/null +++ b/src/pdf/api/qpdfpagerenderer.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> +** 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 QPDFPAGERENDERER_H +#define QPDFPAGERENDERER_H + +#include <QtPdf/qtpdfglobal.h> + +#include <QtCore/qobject.h> +#include <QtCore/qsize.h> +#include <QtPdf/qpdfdocumentrenderoptions.h> + +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 class RenderMode + { + MultiThreaded, + SingleThreaded + }; + Q_ENUM(RenderMode) + + explicit QPdfPageRenderer(QObject *parent = nullptr); + ~QPdfPageRenderer() override; + + 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..eb0fb831f --- /dev/null +++ b/src/pdf/api/qpdfsearchmodel.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** 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 <QtPdf/qtpdfglobal.h> + +#include <QtCore/qabstractitemmodel.h> +#include <QtPdf/qpdfdocument.h> +#include <QtPdf/qpdfsearchresult.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, + ContextBefore, + ContextAfter, + _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..2dfca2dc4 --- /dev/null +++ b/src/pdf/api/qpdfsearchresult.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** 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 <QtCore/qdebug.h> +#include <QtCore/qrect.h> +#include <QtCore/qvector.h> +#include <QtPdf/qpdfdestination.h> + +QT_BEGIN_NAMESPACE + +class QPdfSearchResultPrivate; + +class Q_PDF_EXPORT QPdfSearchResult : public QPdfDestination +{ + Q_GADGET + Q_PROPERTY(QString contextBefore READ contextBefore) + Q_PROPERTY(QString contextAfter READ contextAfter) + Q_PROPERTY(QVector<QRectF> rectangles READ rectangles) + +public: + QPdfSearchResult(); + ~QPdfSearchResult() {} + + QString contextBefore() const; + QString contextAfter() const; + QVector<QRectF> rectangles() const; + +private: + QPdfSearchResult(int page, QVector<QRectF> rects, QString contextBefore, QString contextAfter); + 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..615dce4e0 --- /dev/null +++ b/src/pdf/api/qpdfsearchresult_p.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** 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 contextBefore, QString contextAfter) : + QPdfDestinationPrivate(page, rects.first().topLeft(), 0), + contextBefore(contextBefore), + contextAfter(contextAfter), + rects(rects) {} + + QString contextBefore; + QString contextAfter; + 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..5a6a1cddc --- /dev/null +++ b/src/pdf/api/qpdfselection.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** 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 <QtCore/qobject.h> +#include <QtCore/qshareddata.h> +#include <QtGui/qclipboard.h> +#include <QtGui/qpolygon.h> + +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(); + QPdfSelection(const QPdfSelection &other); + QPdfSelection &operator=(const QPdfSelection &other); + QPdfSelection(QPdfSelection &&other) noexcept; + 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..223ec4bcb --- /dev/null +++ b/src/pdf/api/qtpdfglobal.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 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..ddc0e99dc --- /dev/null +++ b/src/pdf/configure.json @@ -0,0 +1,64 @@ +{ + "module": "pdf", + "depends" : [ "buildtools-private" ], + "testDir": "../../config.tests", + "condition": "features.build-qtpdf && features.webengine-qtpdf-support", + "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..33d3b2465 --- /dev/null +++ b/src/pdf/jsbridge.cpp @@ -0,0 +1,38 @@ +/**************************************************************************** +** +** 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 "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..c9c365568 --- /dev/null +++ b/src/pdf/qpdfbookmarkmodel.cpp @@ -0,0 +1,363 @@ +/**************************************************************************** +** +** 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 "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..b70e031ca --- /dev/null +++ b/src/pdf/qpdfdestination.cpp @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** 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 &&other) noexcept + : d(std::move(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..89b27da8b --- /dev/null +++ b/src/pdf/qpdfdocument.cpp @@ -0,0 +1,803 @@ +/**************************************************************************** +** +** 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 "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); +} + +QString QPdfDocumentPrivate::getText(FPDF_TEXTPAGE textPage, int startIndex, int count) +{ + 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 + return QString::fromUtf16(buf.constData(), len - 1); +} + +/*! + \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) { + if (startIndex > endIndex) + qSwap(startIndex, endIndex); + int count = endIndex - startIndex + 1; + QString text = d->getText(textPage, startIndex, count); + 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(); +} + +QPdfSelection QPdfDocument::getAllText(int page) +{ + const QPdfMutexLocker lock; + FPDF_PAGE pdfPage = FPDF_LoadPage(d->doc, page); + double pageHeight = FPDF_GetPageHeight(pdfPage); + FPDF_TEXTPAGE textPage = FPDFText_LoadPage(pdfPage); + int count = FPDFText_CountChars(textPage); + if (count < 1) + return QPdfSelection(); + QString text = d->getText(textPage, 0, count); + QVector<QPolygonF> bounds; + int rectCount = FPDFText_CountRects(textPage, 0, count); + for (int i = 0; i < rectCount; ++i) { + double l, r, b, t; + FPDFText_GetRect(textPage, i, &l, &t, &r, &b); + QPolygonF poly; + poly << QPointF(l, pageHeight - t); + poly << QPointF(r, pageHeight - t); + poly << QPointF(r, pageHeight - b); + poly << QPointF(l, pageHeight - b); + poly << QPointF(l, pageHeight - t); + bounds << poly; + } + qCDebug(qLcDoc) << "on page" << page << "got" << count << "chars" << rectCount << "rects"; + return QPdfSelection(text, bounds); +} + +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..31d9f4e1e --- /dev/null +++ b/src/pdf/qpdfpagerenderer.cpp @@ -0,0 +1,361 @@ +/**************************************************************************** +** +** 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::RenderMode::SingleThreaded; + 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) +{ + Q_UNUSED(image); + Q_UNUSED(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 (\c RenderMode::SingleThreaded) or in a separate worker thread + (\c RenderMode::MultiThreaded) 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 MultiThreaded All pages are rendered in a separate worker thread. + \value SingleThreaded 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 RenderMode::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 == RenderMode::MultiThreaded) { + 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..27b7833fc --- /dev/null +++ b/src/pdf/qpdfsearchmodel.cpp @@ -0,0 +1,324 @@ +/**************************************************************************** +** +** 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 = 64; +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::ContextBefore: + return d->searchResults[pi.page][pi.index].contextBefore(); + case Role::ContextAfter: + return d->searchResults[pi.page][pi.index].contextAfter(); + case Role::_Count: + break; + } + if (role == Qt::DisplayRole) { + const QString ret = d->searchResults[pi.page][pi.index].contextBefore() + + QLatin1String("<b>") + d->searchString + QLatin1String("</b>") + + d->searchResults[pi.page][pi.index].contextAfter(); + return ret; + } + 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; + beginResetModel(); + d->clearResults(); + emit searchStringChanged(); + 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; + d->clearResults(); + emit documentChanged(); +} + +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() : QAbstractItemModelPrivate() +{ +} + +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 contextBefore, contextAfter; + 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 + QString context = QString::fromUtf16(buf.constData(), len - 1); + context = context.replace(QLatin1Char('\n'), QStringLiteral("\u23CE")); + context = context.remove(QLatin1Char('\r')); + // try to find the search string near the middle of the context if possible + int si = context.indexOf(searchString, ContextChars - 5, Qt::CaseInsensitive); + if (si < 0) + si = context.indexOf(searchString, Qt::CaseInsensitive); + if (si < 0) + qWarning() << "search string" << searchString << "not found in context" << context; + contextBefore = context.mid(0, si); + contextAfter = context.mid(si + searchString.length()); + } + } + if (!rects.isEmpty()) + newSearchResults << QPdfSearchResult(page, rects, contextBefore, contextAfter); + } + 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..53da1c165 --- /dev/null +++ b/src/pdf/qpdfsearchresult.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** 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 contextBefore, QString contextAfter) : + QPdfSearchResult(new QPdfSearchResultPrivate(page, rects, contextBefore, contextAfter)) { } + +QPdfSearchResult::QPdfSearchResult(QPdfSearchResultPrivate *d) : + QPdfDestination(static_cast<QPdfDestinationPrivate *>(d)) { } + +QString QPdfSearchResult::contextBefore() const +{ + return static_cast<QPdfSearchResultPrivate *>(d.data())->contextBefore; +} + +QString QPdfSearchResult::contextAfter() const +{ + return static_cast<QPdfSearchResultPrivate *>(d.data())->contextAfter; +} + +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() + << " contextBefore=" << searchResult.contextBefore() + << " contextAfter=" << searchResult.contextAfter() + << " 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..e334f0fb6 --- /dev/null +++ b/src/pdf/qpdfselection.cpp @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** 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(QPdfSelection &&other) noexcept + : d(std::move(other.d)) +{ +} + +QPdfSelection::~QPdfSelection() +{ +} + +QPdfSelection &QPdfSelection::operator=(const QPdfSelection &other) +{ + d = other.d; + return *this; +} + +/*! + \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..670fe0bf9 --- /dev/null +++ b/src/pdf/quick/plugin.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** 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); +#ifdef QT_STATIC + Q_UNUSED(engine); +#else + 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/+material/PdfStyle.qml b/src/pdf/quick/qml/+material/PdfStyle.qml new file mode 100644 index 000000000..12df30466 --- /dev/null +++ b/src/pdf/quick/qml/+material/PdfStyle.qml @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** 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 QtQml 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Controls.Material 2.14 +import QtQuick.Shapes 1.14 + +QtObject { + property Control prototypeControl: Control { } + function withAlpha(color, alpha) { + return Qt.hsla(color.hslHue, color.hslSaturation, color.hslLightness, alpha) + } + property color selectionColor: withAlpha(prototypeControl.palette.highlight, 0.5) + property color pageSearchResultsColor: withAlpha(Qt.lighter(Material.accentColor, 1.5), 0.5) + property color currentSearchResultStrokeColor: Material.accentColor + property real currentSearchResultStrokeWidth: 2 + property color linkUnderscoreColor: prototypeControl.palette.link + property real linkUnderscoreStrokeWidth: 1 + property var linkUnderscoreStrokeStyle: ShapePath.DashLine + property var linkUnderscoreDashPattern: [ 1, 4 ] +} diff --git a/src/pdf/quick/qml/+universal/PdfStyle.qml b/src/pdf/quick/qml/+universal/PdfStyle.qml new file mode 100644 index 000000000..e92f2a080 --- /dev/null +++ b/src/pdf/quick/qml/+universal/PdfStyle.qml @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** 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 QtQml 2.14 +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Controls.Universal 2.14 +import QtQuick.Shapes 1.14 + +QtObject { + property Control prototypeControl: Control { } + function withAlpha(color, alpha) { + return Qt.hsla(color.hslHue, color.hslSaturation, color.hslLightness, alpha) + } + property color selectionColor: withAlpha(prototypeControl.palette.highlight, 0.5) + property color pageSearchResultsColor: withAlpha(Qt.lighter(Universal.accent, 1.5), 0.5) + property color currentSearchResultStrokeColor: Universal.accent + property real currentSearchResultStrokeWidth: 2 + property color linkUnderscoreColor: prototypeControl.palette.link + property real linkUnderscoreStrokeWidth: 1 + property var linkUnderscoreStrokeStyle: ShapePath.DashLine + property var linkUnderscoreDashPattern: [ 1, 4 ] +} diff --git a/src/pdf/quick/qml/PdfMultiPageView.qml b/src/pdf/quick/qml/PdfMultiPageView.qml new file mode 100644 index 000000000..70bb5454f --- /dev/null +++ b/src/pdf/quick/qml/PdfMultiPageView.qml @@ -0,0 +1,379 @@ +/**************************************************************************** +** +** 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.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 selectAll() { + var currentItem = tableView.itemAtPos(0, tableView.contentY + root.height / 2) + if (currentItem !== null) + currentItem.selection.selectAll() + } + 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) + searchModel.currentPage = page + } + + // 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 + PdfStyle { id: style } + TableView { + id: tableView + anchors.fill: parent + anchors.leftMargin: 2 + model: root.document === undefined ? 0 : root.document.pageCount + rowSpacing: 6 + property real rotationNorm: Math.round((360 + (root.pageRotation % 360)) % 360) + property bool rot90: rotationNorm == 90 || rotationNorm == 270 + onRot90Changed: forceLayout() + property size firstPagePointSize: document === undefined ? Qt.size(0, 0) : document.pagePointSize(0) + contentWidth: document === undefined ? 0 : (rot90 ? document.maxPageHeight : document.maxPageWidth) * root.renderScale + vscroll.width + 2 + // workaround for missing function (see https://codereview.qt-project.org/c/qt/qtdeclarative/+/248464) + function itemAtPos(x, y, includeSpacing) { + // we don't care about x (assume col 0), and assume includeSpacing is true + var ret = null + for (var i = 0; i < contentItem.children.length; ++i) { + var child = contentItem.children[i]; + if (root.debug) + console.log(child, "@y", child.y) + if (child.y < y && (!ret || child.y > ret.y)) + ret = child + } + if (root.debug && ret !== null) + console.log("given y", y, "found", ret, "@", ret.y) + return ret // the delegate with the largest y that is less than the given y + } + 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: pinch.active ? undefined : 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 + searchHighlights.update() + } + } + Shape { + anchors.fill: parent + visible: image.status === Image.Ready + onVisibleChanged: searchHighlights.update() + ShapePath { + strokeWidth: -1 + fillColor: style.pageSearchResultsColor + scale: Qt.size(paper.pageScale, paper.pageScale) + PathMultiline { + id: searchHighlights + function update() { + // paths could be a binding, but we need to be able to "kick" it sometimes + paths = searchModel.boundingPolygonsOnPage(index) + } + } + } + Connections { + target: searchModel + // whenever the highlights on the _current_ page change, they actually need to change on _all_ pages + // (usually because the search string has changed) + function onCurrentPageBoundingPolygonsChanged() { searchHighlights.update() } + } + ShapePath { + strokeWidth: -1 + fillColor: style.selectionColor + scale: Qt.size(paper.pageScale, paper.pageScale) + PathMultiline { + paths: selection.geometry + } + } + } + Shape { + anchors.fill: parent + visible: image.status === Image.Ready && searchModel.currentPage === index + ShapePath { + strokeWidth: style.currentSearchResultStrokeWidth + strokeColor: style.currentSearchResultStrokeColor + fillColor: "transparent" + scale: Qt.size(paper.pageScale, paper.pageScale) + PathMultiline { + paths: searchModel.currentResultBoundingPolygons + } + } + } + PinchHandler { + id: pinch + minimumScale: 0.1 + maximumScale: root.renderScale < 4 ? 2 : 1 + minimumRotation: root.pageRotation + maximumRotation: root.pageRotation + enabled: image.sourceSize.width < 5000 + onActiveChanged: + if (active) { + paper.z = 10 + } else { + paper.z = 0 + var centroidInPoints = Qt.point(pinch.centroid.position.x / root.renderScale, + pinch.centroid.position.y / root.renderScale) + var centroidInFlickable = tableView.mapFromItem(paper, pinch.centroid.position.x, pinch.centroid.position.y) + var newSourceWidth = image.sourceSize.width * paper.scale + var ratio = newSourceWidth / image.sourceSize.width + if (root.debug) + console.log("pinch ended on page", index, "with centroid", pinch.centroid.position, centroidInPoints, "wrt flickable", centroidInFlickable, + "page at", pageHolder.x.toFixed(2), pageHolder.y.toFixed(2), + "contentX/Y were", tableView.contentX.toFixed(2), tableView.contentY.toFixed(2)) + if (ratio > 1.1 || ratio < 0.9) { + var centroidOnPage = Qt.point(centroidInPoints.x * root.renderScale * ratio, centroidInPoints.y * root.renderScale * ratio) + paper.scale = 1 + paper.x = 0 + paper.y = 0 + root.renderScale *= ratio + tableView.forceLayout() + if (tableView.rotationNorm == 0) { + tableView.contentX = pageHolder.x + tableView.originX + centroidOnPage.x - centroidInFlickable.x + tableView.contentY = pageHolder.y + tableView.originY + centroidOnPage.y - centroidInFlickable.y + } else if (tableView.rotationNorm == 90) { + tableView.contentX = pageHolder.x + tableView.originX + image.height - centroidOnPage.y - centroidInFlickable.x + tableView.contentY = pageHolder.y + tableView.originY + centroidOnPage.x - centroidInFlickable.y + } else if (tableView.rotationNorm == 180) { + tableView.contentX = pageHolder.x + tableView.originX + image.width - centroidOnPage.x - centroidInFlickable.x + tableView.contentY = pageHolder.y + tableView.originY + image.height - centroidOnPage.y - centroidInFlickable.y + } else if (tableView.rotationNorm == 270) { + tableView.contentX = pageHolder.x + tableView.originX + centroidOnPage.y - centroidInFlickable.x + tableView.contentY = pageHolder.y + tableView.originY + image.width - centroidOnPage.x - centroidInFlickable.y + } + if (root.debug) + console.log("contentX/Y adjusted to", tableView.contentX.toFixed(2), tableView.contentY.toFixed(2), "y @top", pageHolder.y) + tableView.returnToBounds() + } + } + 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: Shape { + x: rect.x * paper.pageScale + y: rect.y * paper.pageScale + width: rect.width * paper.pageScale + height: rect.height * paper.pageScale + ShapePath { + strokeWidth: style.linkUnderscoreStrokeWidth + strokeColor: style.linkUnderscoreColor + strokeStyle: style.linkUnderscoreStrokeStyle + dashPattern: style.linkUnderscoreDashPattern + startX: 0; startY: height + PathLine { x: width; y: height } + } + 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 { + id: vscroll + 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: if (currentPage != navigationStack.currentPage) 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..b90ad2d7f --- /dev/null +++ b/src/pdf/quick/qml/PdfPageView.qml @@ -0,0 +1,276 @@ +/**************************************************************************** +** +** 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 selectAll() { + selection.selectAll() + } + 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..6076e57df --- /dev/null +++ b/src/pdf/quick/qml/PdfScrollablePageView.qml @@ -0,0 +1,290 @@ +/**************************************************************************** +** +** 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 selectAll() { + selection.selectAll() + } + 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 + PdfStyle { id: style } + 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 + visible: image.status === Image.Ready + ShapePath { + strokeWidth: -1 + fillColor: style.pageSearchResultsColor + scale: Qt.size(image.pageScale, image.pageScale) + PathMultiline { + paths: searchModel.currentPageBoundingPolygons + } + } + ShapePath { + strokeWidth: style.currentSearchResultStrokeWidth + strokeColor: style.currentSearchResultStrokeColor + fillColor: "transparent" + scale: Qt.size(image.pageScale, image.pageScale) + PathMultiline { + paths: searchModel.currentResultBoundingPolygons + } + } + ShapePath { + fillColor: style.selectionColor + scale: Qt.size(image.pageScale, image.pageScale) + PathMultiline { + paths: selection.geometry + } + } + } + + Repeater { + model: PdfLinkModel { + id: linkModel + document: root.document + page: navigationStack.currentPage + } + delegate: Shape { + x: rect.x * image.pageScale + y: rect.y * image.pageScale + width: rect.width * image.pageScale + height: rect.height * image.pageScale + ShapePath { + strokeWidth: style.linkUnderscoreStrokeWidth + strokeColor: style.linkUnderscoreColor + strokeStyle: style.linkUnderscoreStrokeStyle + dashPattern: style.linkUnderscoreDashPattern + startX: 0; startY: height + PathLine { x: width; y: height } + } + 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) + } + } + } + } + DragHandler { + id: textSelectionDrag + acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus + target: null + } + TapHandler { + id: tapHandler + acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus + } + } + + 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 centroidInPoints = Qt.point(pinch.centroid.position.x / root.renderScale, + pinch.centroid.position.y / root.renderScale) + var centroidInFlickable = root.mapFromItem(paper, pinch.centroid.position.x, pinch.centroid.position.y) + var newSourceWidth = image.sourceSize.width * paper.scale + var ratio = newSourceWidth / image.sourceSize.width + if (root.debug) + console.log("pinch ended with centroid", pinch.centroid.position, centroidInPoints, "wrt flickable", centroidInFlickable, + "page at", paper.x.toFixed(2), paper.y.toFixed(2), + "contentX/Y were", root.contentX.toFixed(2), root.contentY.toFixed(2)) + if (ratio > 1.1 || ratio < 0.9) { + var centroidOnPage = Qt.point(centroidInPoints.x * root.renderScale * ratio, centroidInPoints.y * root.renderScale * ratio) + paper.scale = 1 + paper.x = 0 + paper.y = 0 + root.contentX = centroidOnPage.x - centroidInFlickable.x + root.contentY = centroidOnPage.y - centroidInFlickable.y + root.renderScale *= ratio // onRenderScaleChanged calls navigationStack.update() so we don't need to here + if (root.debug) + console.log("contentX/Y adjusted to", root.contentX.toFixed(2), root.contentY.toFixed(2)) + } else { + paper.x = 0 + paper.y = 0 + } + } + grabPermissions: PointerHandler.CanTakeOverFromAnything + } + } +} diff --git a/src/pdf/quick/qml/PdfStyle.qml b/src/pdf/quick/qml/PdfStyle.qml new file mode 100644 index 000000000..090465ce6 --- /dev/null +++ b/src/pdf/quick/qml/PdfStyle.qml @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** 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 QtQml 2.14 +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Shapes 1.14 + +QtObject { + property Control prototypeControl: Control { } + function withAlpha(color, alpha) { + return Qt.hsla(color.hslHue, color.hslSaturation, color.hslLightness, alpha) + } + property color selectionColor: withAlpha(prototypeControl.palette.highlight, 0.5) + property color pageSearchResultsColor: "#80B0C4DE" + property color currentSearchResultStrokeColor: "cyan" + property real currentSearchResultStrokeWidth: 2 + property color linkUnderscoreColor: prototypeControl.palette.link + property real linkUnderscoreStrokeWidth: 1 + property var linkUnderscoreStrokeStyle: ShapePath.DashLine + property var linkUnderscoreDashPattern: [ 1, 4 ] +} 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..5371e85e5 --- /dev/null +++ b/src/pdf/quick/qquickpdfselection.cpp @@ -0,0 +1,284 @@ +/**************************************************************************** +** +** 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::selectAll() +{ + QPdfSelection sel = m_document->m_doc.getAllText(m_page); + 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(); + } +} + +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..bb4a50fed --- /dev/null +++ b/src/pdf/quick/qquickpdfselection_p.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** 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; + + Q_INVOKABLE void selectAll(); +#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..8270a2028 --- /dev/null +++ b/src/pdf/quick/resources.qrc @@ -0,0 +1,10 @@ +<RCC> + <qresource prefix="/qt-project.org/qtpdf"> + <file>qml/+material/PdfStyle.qml</file> + <file>qml/+universal/PdfStyle.qml</file> + <file>qml/PdfStyle.qml</file> + <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..35e368633 --- /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::RenderMode::MultiThreaded); +} + +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..98e4f4663 --- /dev/null +++ b/src/plugins/imageformats/pdf/qpdfiohandler.cpp @@ -0,0 +1,239 @@ +/**************************************************************************** +** +** 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; +} + +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..99a91154c --- /dev/null +++ b/src/plugins/imageformats/pdf/qpdfiohandler_p.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** 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; + 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..66fdf9797 100644 --- a/src/process/process.pro +++ b/src/process/process.pro @@ -39,13 +39,23 @@ win32 { msvc: QMAKE_LFLAGS += /MANIFESTINPUT:$$PWD/process.exe.manifest } -load(qt_app) +TEMPLATE = app + +load(qt_build_paths) + +!build_pass:qtConfig(debug_and_release): CONFIG += release +CONFIG += relative_qt_rpath 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 @@ -58,3 +68,8 @@ qtConfig(framework) { } else { target.path = $$[QT_INSTALL_LIBEXECS] } + +load(qt_targets) +load(qt_common) + +INSTALLS += target 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 dead005b0..bcbf95569 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); @@ -1429,6 +1435,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 3969ee244..6f9b552ec 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/qwebengine-licensing.qdoc b/src/webengine/doc/src/qwebengine-licensing.qdoc index 8795ca44c..f6a0a6c32 100644 --- a/src/webengine/doc/src/qwebengine-licensing.qdoc +++ b/src/webengine/doc/src/qwebengine-licensing.qdoc @@ -26,7 +26,6 @@ ****************************************************************************/ /*! -\contentspage qtwebengine-licensing.html \group qtwebengine-licensing \title Qt WebEngine Licensing @@ -43,7 +42,6 @@ Third party licenses included in the sources are: */ /*! -\contentspage qtwebengine-licensing.html \page qtwebengine-3rdparty-chromium-global.html attribution \ingroup qtwebengine-licensing \title Chromium License 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/webengine/render_widget_host_view_qt_delegate_quickwindow.cpp b/src/webengine/render_widget_host_view_qt_delegate_quickwindow.cpp index d3ebdbf27..1de4d8835 100644 --- a/src/webengine/render_widget_host_view_qt_delegate_quickwindow.cpp +++ b/src/webengine/render_widget_host_view_qt_delegate_quickwindow.cpp @@ -48,7 +48,7 @@ RenderWidgetHostViewQtDelegateQuickWindow::RenderWidgetHostViewQtDelegateQuickWi : m_realDelegate(realDelegate) , m_virtualParent(nullptr) { - setFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowDoesNotAcceptFocus); + setFlags(Qt::Tool | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::WindowDoesNotAcceptFocus); } RenderWidgetHostViewQtDelegateQuickWindow::~RenderWidgetHostViewQtDelegateQuickWindow() 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/src/webenginewidgets/doc/src/qwebenginesettings_lgpl.qdoc b/src/webenginewidgets/doc/src/qwebenginesettings_lgpl.qdoc index 0706598ef..7043469a0 100644 --- a/src/webenginewidgets/doc/src/qwebenginesettings_lgpl.qdoc +++ b/src/webenginewidgets/doc/src/qwebenginesettings_lgpl.qdoc @@ -114,8 +114,7 @@ subresources, such as scripts, and therefore disabling this setting is not a safety mechanism. \value XSSAuditingEnabled - Monitors load requests for cross-site scripting attempts. Suspicious scripts are blocked - and reported in the inspector's JavaScript console. Enabled by default. + Obsolete and has no effect. \value SpatialNavigationEnabled Enables the Spatial Navigation feature, which means the ability to navigate between focusable elements, such as hyperlinks and form controls, on a web page by using the |