diff options
45 files changed, 592 insertions, 90 deletions
diff --git a/configure.json b/configure.json index ba7a84273..ecf6e1132 100644 --- a/configure.json +++ b/configure.json @@ -388,7 +388,7 @@ }, "system-libxml2" : { "label": "libxml2", - "condition": "config.unix && tests.srtp", + "condition": "config.unix && tests.libxml2", "output": [ "privateFeature" ] }, "winversion" : { @@ -433,15 +433,15 @@ "label": "Spellchecker", "purpose": "Provides a spellchecker.", "section": "WebEngine", - "output": [ "privateFeature" ] + "output": [ "publicFeature" ] }, "native-spellchecker": { "label": "Native Spellchecker", - "purpose": "Provides a native spellchecker.", + "purpose": "Use the system's native spellchecking engine.", "section": "WebEngine", "autoDetect": false, "condition": "config.macos && features.spellchecker", - "output": [ "privateFeature" ] + "output": [ "publicFeature" ] }, "ui-delegates": { "label": "UI Delegates", diff --git a/dist/changes-5.9.2 b/dist/changes-5.9.2 new file mode 100644 index 000000000..6cc9dbe37 --- /dev/null +++ b/dist/changes-5.9.2 @@ -0,0 +1,72 @@ +Qt 5.9.2 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.9.0. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +http://doc.qt.io/qt-5/index.html + +The Qt version 5.9 series is binary compatible with the 5.8.x series. +Applications compiled for 5.8 will continue to run with 5.9. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* General * +**************************************************************************** + + - Chromium Snapshot: + * Security fixes from Chromium up to version 61.0.3163.79 + Including: CVE-2017-5092, CVE-2017-5093, CVE-2017-5095, CVE-2017-5097, + CVE-2017-5099, CVE-2017-5102, CVE-2017-5103, CVE-2017-5107, + CVE-2017-5112, CVE-2017-5114, CVE-2017-5117 and CVE-2017-5118 + * Fixed Skia to to render text correctly with FreeType 2.8.1 + * [QTBUG-50389] Fixed assert on some flash content + + - QtWebEngine: + * [QTBUG-57505] Handle --force-webrtc-ip-handling-policy on command-line + * [QTBUG-58306] Fixed handling of menu key + * [QTBUG-60790] Fixed dragging images to desktop + * [QTBUG-61354] Set referrer on download requests + * [QTBUG-61429] Fixed cancelling IME composition + * [QTBUG-61506] Stop searching when navigating away + * [QTBUG-61910] Fixed an issue where system proxy settings were not + picked up correctly + * [QTBUG-62112] Fixed upside-down rendering in software rendering mode + * [QTBUG-62112] Fixed rendering of content with preserve-3d in CSS + * [QTBUG-62311] Fixed hang when exiting with open combobox + * [QTBUG-62808] Handle --explicitly-allowed-ports on command-line + * [QTBUG-62898] Fixed accessing webchannels from document-creation + user-scripts after navigation. + * [QTBUG-62942] Fixed committing IME composition on touch events + + - QWebEngineView: + * [QTBUG-61621] Fixed propagation of unhandled key press events + + - WebEngineView: + * The callback version of printToPdf is now called with a proper + bytearray result instead of a PDF data in a javascript string. + +**************************************************************************** +* Platform Specific Changes * +**************************************************************************** + + - Linux: + * [QTBUG-61528, QTBUG-62673] Fixed various multilib build configurations + * [QTBUG-61846] Fixed host builds on Arm and MIPS + + - Windows: + * [QTBUG-60334] Fixed location of IME window + * [QTBUG-62146] Fixed following system font configuration + * [QTBUG-62200] Fixed assert on hover + * Allow WebGL to work with software OpenGL using the new command-line + argument --enable-webgl-software-rendering + + - macOS: + * [QTBUG-60605] Use OpenGL core-profile when the main application does diff --git a/examples/webengine/webengine.pro b/examples/webengine/webengine.pro index 3f55888cb..b20b8f118 100644 --- a/examples/webengine/webengine.pro +++ b/examples/webengine/webengine.pro @@ -3,5 +3,9 @@ TEMPLATE=subdirs SUBDIRS += \ customdialogs \ minimal \ - quicknanobrowser \ - recipebrowser + quicknanobrowser + +qtHaveModule(quickcontrols2) { + SUBDIRS += \ + recipebrowser +} diff --git a/examples/webenginewidgets/simplebrowser/browser.cpp b/examples/webenginewidgets/simplebrowser/browser.cpp index f5a69793f..c50974531 100644 --- a/examples/webenginewidgets/simplebrowser/browser.cpp +++ b/examples/webenginewidgets/simplebrowser/browser.cpp @@ -61,11 +61,15 @@ Browser::Browser() QObject::connect( QWebEngineProfile::defaultProfile(), &QWebEngineProfile::downloadRequested, &m_downloadManagerWidget, &DownloadManagerWidget::downloadRequested); + QObject::connect( + &m_otrProfile, &QWebEngineProfile::downloadRequested, + &m_downloadManagerWidget, &DownloadManagerWidget::downloadRequested); } -BrowserWindow *Browser::createWindow() +BrowserWindow *Browser::createWindow(bool offTheRecord) { - auto mainWindow = new BrowserWindow(this); + auto profile = offTheRecord ? &m_otrProfile : QWebEngineProfile::defaultProfile(); + auto mainWindow = new BrowserWindow(this, profile); m_windows.append(mainWindow); QObject::connect(mainWindow, &QObject::destroyed, [this, mainWindow]() { m_windows.removeOne(mainWindow); diff --git a/examples/webenginewidgets/simplebrowser/browser.h b/examples/webenginewidgets/simplebrowser/browser.h index 0ea30a7f5..9240cc987 100644 --- a/examples/webenginewidgets/simplebrowser/browser.h +++ b/examples/webenginewidgets/simplebrowser/browser.h @@ -54,6 +54,7 @@ #include "downloadmanagerwidget.h" #include <QVector> +#include <QWebEngineProfile> class BrowserWindow; @@ -64,12 +65,13 @@ public: QVector<BrowserWindow*> windows() { return m_windows; } - BrowserWindow *createWindow(); + BrowserWindow *createWindow(bool offTheRecord = false); DownloadManagerWidget &downloadManagerWidget() { return m_downloadManagerWidget; } private: QVector<BrowserWindow*> m_windows; DownloadManagerWidget m_downloadManagerWidget; + QWebEngineProfile m_otrProfile; }; #endif // BROWSER_H diff --git a/examples/webenginewidgets/simplebrowser/browserwindow.cpp b/examples/webenginewidgets/simplebrowser/browserwindow.cpp index e114ae0f3..016d58afe 100644 --- a/examples/webenginewidgets/simplebrowser/browserwindow.cpp +++ b/examples/webenginewidgets/simplebrowser/browserwindow.cpp @@ -67,9 +67,10 @@ #include <QVBoxLayout> #include <QWebEngineProfile> -BrowserWindow::BrowserWindow(Browser *browser) +BrowserWindow::BrowserWindow(Browser *browser, QWebEngineProfile *profile) : m_browser(browser) - , m_tabWidget(new TabWidget(this)) + , m_profile(profile) + , m_tabWidget(new TabWidget(profile, this)) , m_progressBar(new QProgressBar(this)) , m_historyBackAction(nullptr) , m_historyForwardAction(nullptr) @@ -141,6 +142,7 @@ QMenu *BrowserWindow::createFileMenu(TabWidget *tabWidget) { QMenu *fileMenu = new QMenu(tr("&File")); fileMenu->addAction(tr("&New Window"), this, &BrowserWindow::handleNewWindowTriggered, QKeySequence::New); + fileMenu->addAction(tr("New &Incognito Window"), this, &BrowserWindow::handleNewIncognitoWindowTriggered); QAction *newTabAction = new QAction(tr("New &Tab"), this); newTabAction->setShortcuts(QKeySequence::AddTab); @@ -403,10 +405,14 @@ void BrowserWindow::handleWebActionEnabledChanged(QWebEnginePage::WebAction acti void BrowserWindow::handleWebViewTitleChanged(const QString &title) { + QString suffix = m_profile->isOffTheRecord() + ? tr("Qt Simple Browser (Incognito)") + : tr("Qt Simple Browser"); + if (title.isEmpty()) - setWindowTitle(tr("Qt Simple Browser")); + setWindowTitle(suffix); else - setWindowTitle(tr("%1 - Qt Simple Browser").arg(title)); + setWindowTitle(title + " - " + suffix); } void BrowserWindow::handleNewWindowTriggered() @@ -414,6 +420,11 @@ void BrowserWindow::handleNewWindowTriggered() m_browser->createWindow(); } +void BrowserWindow::handleNewIncognitoWindowTriggered() +{ + m_browser->createWindow(/* offTheRecord: */ true); +} + void BrowserWindow::handleFileOpenTriggered() { QUrl url = QFileDialog::getOpenFileUrl(this, tr("Open Web Resource"), QString(), diff --git a/examples/webenginewidgets/simplebrowser/browserwindow.h b/examples/webenginewidgets/simplebrowser/browserwindow.h index 36f00605c..b522a6b9d 100644 --- a/examples/webenginewidgets/simplebrowser/browserwindow.h +++ b/examples/webenginewidgets/simplebrowser/browserwindow.h @@ -69,7 +69,7 @@ class BrowserWindow : public QMainWindow Q_OBJECT public: - BrowserWindow(Browser *browser); + BrowserWindow(Browser *browser, QWebEngineProfile *profile); QSize sizeHint() const override; TabWidget *tabWidget() const; WebView *currentTab() const; @@ -80,6 +80,7 @@ protected: private slots: void handleNewWindowTriggered(); + void handleNewIncognitoWindowTriggered(); void handleFileOpenTriggered(); void handleFindActionTriggered(); void handleShowWindowTriggered(); @@ -97,6 +98,7 @@ private: private: Browser *m_browser; + QWebEngineProfile *m_profile; TabWidget *m_tabWidget; QProgressBar *m_progressBar; QAction *m_historyBackAction; diff --git a/examples/webenginewidgets/simplebrowser/doc/src/simplebrowser.qdoc b/examples/webenginewidgets/simplebrowser/doc/src/simplebrowser.qdoc index 741ad864d..4f44d9f6c 100644 --- a/examples/webenginewidgets/simplebrowser/doc/src/simplebrowser.qdoc +++ b/examples/webenginewidgets/simplebrowser/doc/src/simplebrowser.qdoc @@ -202,9 +202,6 @@ \section1 Implementing WebPage Functionality - As mentioned earlier, each \c WebView contains a \c WebPage instance that - was created by using QWebEngineProfile::defaultProfile(). - We implement \c WebPage as a subclass of QWebEnginePage to enable HTTP, proxy authentication, and ignoring SSL certificate errors when accessing web pages: @@ -242,8 +239,16 @@ over to \c TabWidget::setUrl: \quotefromfile webenginewidgets/simplebrowser/browserwindow.cpp + \skipto BrowserWindow::BrowserWindow + \printline BrowserWindow::BrowserWindow + \skipto /^\{/ + \printline /^\{/ + \dots \skipto connect(m_urlLineEdit - \printline connect + \printuntil }); + \dots + \skipto /^\}/ + \printline /^\}/ The call is forwarded to the currently selected tab: @@ -255,6 +260,50 @@ The \c setUrl() method of \c WebView just forwards the \c url to the associated \c WebPage, which in turn starts the downloading of the page's content in the background. + \section1 Implementing Private Browsing + + \e{Private browsing}, \e{incognito mode}, or \e{off-the-record mode} is a + feature of many browsers where normally persistent data, such as cookies, + the HTTP cache, or browsing history, is kept only in memory, leaving no + trace on disk. In this example we will implement private browsing on the + window level with tabs in one window all in either normal or private mode. + Alternatively we could implement private browsing on the tab-level, with + some tabs in a window in normal mode, others in private mode. + + Implementing private browsing is quite easy using Qt WebEngine. All one has + to do is to create a new \l{QWebEngineProfile} and use it in the + \l{QWebEnginePage} instead of the default profile. In the example this new + profile is created and owned by the \c Browser object: + + \quotefromfile webenginewidgets/simplebrowser/browser.h + \skipto /^class Browser$/ + \printuntil public: + \dots + \skipto createWindow + \printline createWindow + \skipto private: + \printline private: + \dots + \skipto m_otrProfile + \printline m_otrProfile + \printline /^\};$/ + + The default constructor for \l{QWebEngineProfile} already puts it in + \e{off-the-record} mode. All that is left to do is to pass the appropriate + profile down to the appropriate \l QWebEnginePage objects. The \c Browser + object will hand to each new \c BrowserWindow either the global default + profile (see \l{QWebEngineProfile::defaultProfile}) or its own + off-the-record profile: + + \quotefromfile webenginewidgets/simplebrowser/browser.cpp + \skipto Browser::createWindow + \printuntil mainWindow = new BrowserWindow + \skipto return mainWindow + \printuntil /^\}/ + + The \c BrowserWindow and \c TabWidget objects will then ensure that all \l + QWebEnginePage objects contained in a window will use this profile. + \section1 Managing Downloads Downloads are associated with a \l QWebEngineProfile. Whenever a download is diff --git a/examples/webenginewidgets/simplebrowser/tabwidget.cpp b/examples/webenginewidgets/simplebrowser/tabwidget.cpp index c3a90bce7..e7376c7a5 100644 --- a/examples/webenginewidgets/simplebrowser/tabwidget.cpp +++ b/examples/webenginewidgets/simplebrowser/tabwidget.cpp @@ -55,8 +55,9 @@ #include <QTabBar> #include <QWebEngineProfile> -TabWidget::TabWidget(QWidget *parent) +TabWidget::TabWidget(QWebEngineProfile *profile, QWidget *parent) : QTabWidget(parent) + , m_profile(profile) { QTabBar *tabBar = this->tabBar(); tabBar->setTabsClosable(true); @@ -201,7 +202,7 @@ WebView *TabWidget::createTab() WebView *TabWidget::createBackgroundTab() { WebView *webView = new WebView; - WebPage *webPage = new WebPage(QWebEngineProfile::defaultProfile(), webView); + WebPage *webPage = new WebPage(m_profile, webView); webView->setPage(webPage); setupView(webView); int index = addTab(webView, tr("(Untitled)")); diff --git a/examples/webenginewidgets/simplebrowser/tabwidget.h b/examples/webenginewidgets/simplebrowser/tabwidget.h index f33ecf897..5b09f2708 100644 --- a/examples/webenginewidgets/simplebrowser/tabwidget.h +++ b/examples/webenginewidgets/simplebrowser/tabwidget.h @@ -65,7 +65,7 @@ class TabWidget : public QTabWidget Q_OBJECT public: - TabWidget(QWidget *parent = nullptr); + TabWidget(QWebEngineProfile *profile, QWidget *parent = nullptr); WebView *currentWebView() const; @@ -100,6 +100,8 @@ private slots: private: WebView *webView(int index) const; void setupView(WebView *webView); + + QWebEngineProfile *m_profile; }; #endif // TABWIDGET_H diff --git a/examples/webenginewidgets/spellchecker/spellchecker.pro b/examples/webenginewidgets/spellchecker/spellchecker.pro index 4c7ad2c36..a5f59974a 100644 --- a/examples/webenginewidgets/spellchecker/spellchecker.pro +++ b/examples/webenginewidgets/spellchecker/spellchecker.pro @@ -1,5 +1,4 @@ -include($$QTWEBENGINE_OUT_ROOT/qtwebengine-config.pri) -QT_FOR_CONFIG += webengine-private +QT_FOR_CONFIG += webengine TEMPLATE = app TARGET = spellchecker diff --git a/examples/webenginewidgets/webenginewidgets.pro b/examples/webenginewidgets/webenginewidgets.pro index 63deb5854..8e91c530b 100644 --- a/examples/webenginewidgets/webenginewidgets.pro +++ b/examples/webenginewidgets/webenginewidgets.pro @@ -1,5 +1,4 @@ -include($$QTWEBENGINE_OUT_ROOT/qtwebengine-config.pri) -QT_FOR_CONFIG += webengine-private +QT_FOR_CONFIG += webengine TEMPLATE=subdirs diff --git a/src/3rdparty b/src/3rdparty -Subproject 04cd5620124dc05c019a582363a93a78665b181 +Subproject 0c43a097b39603be90162b519927a7e47f15ae9 diff --git a/src/buildtools/gn.pro b/src/buildtools/gn.pro index 02d3df652..9d9af9eb8 100644 --- a/src/buildtools/gn.pro +++ b/src/buildtools/gn.pro @@ -4,7 +4,7 @@ option(host_build) !debug_and_release: CONFIG += release include($$QTWEBENGINE_OUT_ROOT/qtwebengine-config.pri) -QT_FOR_CONFIG += webengine-private +QT_FOR_CONFIG += webengine webengine-private build_pass|!debug_and_release { !qtConfig(system-gn): CONFIG(release, debug|release) { diff --git a/src/core/api/core_api.pro b/src/core/api/core_api.pro index 05166536e..270595378 100644 --- a/src/core/api/core_api.pro +++ b/src/core/api/core_api.pro @@ -55,3 +55,11 @@ unix:!isEmpty(QMAKE_LFLAGS_VERSION_SCRIPT):!static { SOURCES += qtbug-60565.cpp \ qtbug-61521.cpp } + +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. + QMAKE_POST_LINK = \ + "if exist $(DESTDIR_TARGET).objects del $(DESTDIR_TARGET).objects$$escape_expand(\\n\\t)" \ + "for %%a in ($(OBJECTS)) do echo $$shell_quote($$shell_path($$OUT_PWD))\\%%a >> $(DESTDIR_TARGET).objects" +} diff --git a/src/core/content_main_delegate_qt.cpp b/src/core/content_main_delegate_qt.cpp index 720db77bf..359e0b3d9 100644 --- a/src/core/content_main_delegate_qt.cpp +++ b/src/core/content_main_delegate_qt.cpp @@ -40,19 +40,23 @@ #include "content_main_delegate_qt.h" #include "base/command_line.h" +#include <base/i18n/rtl.h> #include "base/logging.h" #include "base/path_service.h" #include "base/strings/string_number_conversions.h" +#include <chrome/grit/generated_resources.h> #include "content/public/common/content_paths.h" #include "content/public/common/content_switches.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/ui_base_paths.h" #include "ui/base/resource/resource_bundle.h" +#include <ui/base/webui/jstemplate_builder.h> #include "net/grit/net_resources.h" #include "net/base/net_module.h" #include "content_client_qt.h" #include "renderer/content_renderer_client_qt.h" +#include "type_conversion.h" #include "web_engine_library_info.h" #if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX)) @@ -63,12 +67,38 @@ #include "ui/base/ui_base_switches.h" #endif +#include <QtCore/qcoreapplication.h> + namespace QtWebEngineCore { +// 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) { - base::StringPiece html_data = ui::ResourceBundle::GetSharedInstance().GetRawDataResource(IDR_DIR_HEADER_HTML); - return html_data; + static std::string html_data = constructDirHeaderHTML(); + return base::StringPiece(html_data); } return base::StringPiece(); } diff --git a/src/core/core_module.pro b/src/core/core_module.pro index 55b20cda2..78bb8baee 100644 --- a/src/core/core_module.pro +++ b/src/core/core_module.pro @@ -61,7 +61,9 @@ osx { QMAKE_LFLAGS_DEBUG -= /DEBUG QMAKE_LFLAGS_DEBUG += /DEBUG:FASTLINK } - QMAKE_LFLAGS += /WHOLEARCHIVE:$${api_library_path}$${QMAKE_DIR_SEP}$${api_library_name}.lib + # Simulate -whole-archive by passing the list of object files that belong to the public + # API library as response file to the linker. + QMAKE_LFLAGS += @$${api_library_path}$${QMAKE_DIR_SEP}$${api_library_name}.lib.objects } else { LIBS_PRIVATE += -Wl,-whole-archive -l$$api_library_name -Wl,-no-whole-archive } diff --git a/src/core/delegated_frame_node.cpp b/src/core/delegated_frame_node.cpp index d0d840ecb..d6ee87950 100644 --- a/src/core/delegated_frame_node.cpp +++ b/src/core/delegated_frame_node.cpp @@ -849,8 +849,8 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, // countering the scale of devicePixel-scaled tiles when rendering them // to the final surface. QMatrix4x4 matrix; - matrix.scale(1 / m_chromiumCompositorData->frameDevicePixelRatio, - 1 / m_chromiumCompositorData->frameDevicePixelRatio); + const float devicePixelRatio = m_chromiumCompositorData->frameDevicePixelRatio; + matrix.scale(1 / devicePixelRatio, 1 / devicePixelRatio); if (QSGTransformNode::matrix() != matrix) setMatrix(matrix); @@ -872,11 +872,19 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, frameData->resource_list.clear(); QScopedPointer<DelegatedNodeTreeHandler> nodeHandler; + const QSizeF viewportSizeInPt = apiDelegate->screenRect().size(); + const QSize viewportSize = (viewportSizeInPt * devicePixelRatio).toSize(); + // We first compare if the render passes from the previous frame data are structurally // equivalent to the render passes in the current frame data. If they are, we are going // to reuse the old nodes. Otherwise, we will delete the old nodes and build a new tree. - cc::CompositorFrame *previousFrameData = &m_chromiumCompositorData->previousFrameData; - const bool buildNewTree = !areRenderPassStructuresEqual(frameData, previousFrameData) || m_sceneGraphNodes.empty(); + // + // Additionally, because we clip (i.e. don't build scene graph nodes for) quads outside + // of the visible area, we also have to rebuild the tree whenever the window is resized. + const bool buildNewTree = + !areRenderPassStructuresEqual(frameData, &m_chromiumCompositorData->previousFrameData) || + m_sceneGraphNodes.empty() || + viewportSize != m_previousViewportSize; m_chromiumCompositorData->previousFrameData = cc::CompositorFrame(); SGObjects previousSGObjects; @@ -904,10 +912,7 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, // All RenderPasses except the last one are rendered to an FBO. cc::RenderPass *rootRenderPass = frameData->render_pass_list.back().get(); - QRectF screenRectQt = apiDelegate->screenRect(); - gfx::Size thisSize(int(screenRectQt.width() * m_chromiumCompositorData->frameDevicePixelRatio), - int(screenRectQt.height() * m_chromiumCompositorData->frameDevicePixelRatio)); - + gfx::Rect viewportRect(toGfx(viewportSize)); for (unsigned i = 0; i < frameData->render_pass_list.size(); ++i) { cc::RenderPass *pass = frameData->render_pass_list.at(i).get(); @@ -939,12 +944,14 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, scissorRect = pass->output_rect; } else { renderPassParent = this; - scissorRect = gfx::Rect(thisSize); + scissorRect = viewportRect; scissorRect += rootRenderPass->output_rect.OffsetFromOrigin(); } - if (scissorRect.IsEmpty()) + if (scissorRect.IsEmpty()) { + holdResources(pass, resourceCandidates); continue; + } QSGNode *renderPassChain = nullptr; if (buildNewTree) @@ -967,8 +974,10 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, if (quadState->is_clipped) targetRect.Intersect(quadState->clip_rect); targetRect.Intersect(scissorRect); - if (targetRect.IsEmpty()) + if (targetRect.IsEmpty()) { + holdResources(quad, resourceCandidates); continue; + } if (quadState->sorting_context_id != currentSortingContextId) { flushPolygons(&polygonQueue, renderPassChain, @@ -1007,6 +1016,8 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, ResourceHolderIterator end = resourceCandidates.constEnd(); for (ResourceHolderIterator it = resourceCandidates.constBegin(); it != end ; ++it) resourcesToRelease->push_back((*it)->returnResource()); + + m_previousViewportSize = viewportSize; } void DelegatedFrameNode::flushPolygons( @@ -1224,6 +1235,18 @@ ResourceHolder *DelegatedFrameNode::findAndHoldResource(unsigned resourceId, QHa return resource.data(); } +void DelegatedFrameNode::holdResources(const cc::DrawQuad *quad, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates) +{ + for (auto resource : quad->resources) + findAndHoldResource(resource, candidates); +} + +void DelegatedFrameNode::holdResources(const cc::RenderPass *pass, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates) +{ + for (const auto &quad : pass->quad_list) + holdResources(quad, candidates); +} + QSGTexture *DelegatedFrameNode::initAndHoldTexture(ResourceHolder *resource, bool quadIsAllOpaque, RenderWidgetHostViewQtDelegate *apiDelegate) { // QSGTextures must be destroyed in the scene graph thread as part of the QSGNode tree, diff --git a/src/core/delegated_frame_node.h b/src/core/delegated_frame_node.h index b83f4682a..c627cdf95 100644 --- a/src/core/delegated_frame_node.h +++ b/src/core/delegated_frame_node.h @@ -126,6 +126,8 @@ private: static void fenceAndUnlockQt(DelegatedFrameNode *frameNode); ResourceHolder *findAndHoldResource(unsigned resourceId, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates); + void holdResources(const cc::DrawQuad *quad, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates); + void holdResources(const cc::RenderPass *pass, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates); QSGTexture *initAndHoldTexture(ResourceHolder *resource, bool quadIsAllOpaque, RenderWidgetHostViewQtDelegate *apiDelegate = 0); QExplicitlySharedDataPointer<ChromiumCompositorData> m_chromiumCompositorData; @@ -143,6 +145,7 @@ private: bool m_contextShared; QScopedPointer<QOffscreenSurface> m_offsurface; #endif + QSize m_previousViewportSize; }; } // namespace QtWebEngineCore diff --git a/src/core/gn_run.pro b/src/core/gn_run.pro index c565b99a4..a9089c569 100644 --- a/src/core/gn_run.pro +++ b/src/core/gn_run.pro @@ -1,5 +1,5 @@ include($$QTWEBENGINE_OUT_ROOT/qtwebengine-config.pri) -QT_FOR_CONFIG += webengine-private +QT_FOR_CONFIG += webengine webengine-private TEMPLATE = aux diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp index 7ce71a81d..a12dea6ad 100644 --- a/src/core/render_widget_host_view_qt.cpp +++ b/src/core/render_widget_host_view_qt.cpp @@ -282,6 +282,7 @@ RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost* widget , m_cursorPositionWithinSelection(-1) , m_cursorPosition(0) , m_emptyPreviousSelection(true) + , m_wheelAckPending(false) { m_host->SetView(this); #ifndef QT_NO_ACCESSIBILITY @@ -1315,7 +1316,28 @@ void RenderWidgetHostViewQt::accessibilityActiveChanged(bool active) void RenderWidgetHostViewQt::handleWheelEvent(QWheelEvent *ev) { - m_host->ForwardWheelEvent(WebEventFactory::toWebWheelEvent(ev, dpiScale())); + if (!m_wheelAckPending) { + Q_ASSERT(m_pendingWheelEvents.isEmpty()); + m_wheelAckPending = true; + m_host->ForwardWheelEvent(WebEventFactory::toWebWheelEvent(ev, dpiScale())); + return; + } + if (!m_pendingWheelEvents.isEmpty()) { + // Try to combine with this wheel event with the last pending one. + if (WebEventFactory::coalesceWebWheelEvent(m_pendingWheelEvents.last(), ev, dpiScale())) + return; + } + m_pendingWheelEvents.append(WebEventFactory::toWebWheelEvent(ev, dpiScale())); +} + +void RenderWidgetHostViewQt::WheelEventAck(const blink::WebMouseWheelEvent &/*event*/, content::InputEventAckState /*ack_result*/) +{ + m_wheelAckPending = false; + if (!m_pendingWheelEvents.isEmpty()) { + m_wheelAckPending = true; + m_host->ForwardWheelEvent(m_pendingWheelEvents.takeFirst()); + } + // TODO: We could forward unhandled wheelevents to our parent. } void RenderWidgetHostViewQt::clearPreviousTouchMotionState() diff --git a/src/core/render_widget_host_view_qt.h b/src/core/render_widget_host_view_qt.h index 74f14d0d4..c896cf058 100644 --- a/src/core/render_widget_host_view_qt.h +++ b/src/core/render_widget_host_view_qt.h @@ -153,6 +153,7 @@ public: bool HasAcceleratedSurface(const gfx::Size&) override; void DidCreateNewRendererCompositorFrameSink(cc::mojom::CompositorFrameSinkClient* renderer_compositor_frame_sink) override; void SubmitCompositorFrame(const viz::LocalSurfaceId&, cc::CompositorFrame) override; + void WheelEventAck(const blink::WebMouseWheelEvent &event, content::InputEventAckState ack_result) override; void GetScreenInfo(content::ScreenInfo* results); gfx::Rect GetBoundsInRootWindow() override; @@ -274,6 +275,9 @@ private: QString m_surroundingText; bool m_imeHasHiddenTextCapability; + + bool m_wheelAckPending; + QList<blink::WebMouseWheelEvent> m_pendingWheelEvents; }; } // namespace QtWebEngineCore diff --git a/src/core/surface_factory_qt.cpp b/src/core/surface_factory_qt.cpp index d0741506a..c976e8b04 100644 --- a/src/core/surface_factory_qt.cpp +++ b/src/core/surface_factory_qt.cpp @@ -56,7 +56,13 @@ #if defined(USE_OZONE) #include <EGL/egl.h> -#include <dlfcn.h> + +#ifndef QT_LIBDIR_EGL +#define QT_LIBDIR_EGL "/usr/lib" +#endif +#ifndef QT_LIBDIR_GLES2 +#define QT_LIBDIR_GLES2 QT_LIBDIR_EGL +#endif namespace QtWebEngineCore { @@ -92,21 +98,29 @@ base::NativeLibrary LoadLibrary(const base::FilePath& filename) { bool GLOzoneQt::LoadGLES2Bindings(gl::GLImplementation /*implementation*/) { - base::NativeLibrary eglgles2Library = dlopen(NULL, RTLD_LAZY); - if (!eglgles2Library) { - LOG(ERROR) << "Failed to open EGL/GLES2 context " << dlerror(); + base::FilePath libEGLPath = QtWebEngineCore::toFilePath(QT_LIBDIR_EGL); + libEGLPath = libEGLPath.Append("libEGL.so.1"); + base::NativeLibrary eglLibrary = LoadLibrary(libEGLPath); + if (!eglLibrary) + return false; + + base::FilePath libGLES2Path = QtWebEngineCore::toFilePath(QT_LIBDIR_GLES2); + libGLES2Path = libGLES2Path.Append("libGLESv2.so.2"); + base::NativeLibrary gles2Library = LoadLibrary(libGLES2Path); + if (!gles2Library) return false; - } - gl::GLGetProcAddressProc get_proc_address = reinterpret_cast<gl::GLGetProcAddressProc>(base::GetFunctionPointerFromNativeLibrary(eglgles2Library, "eglGetProcAddress")); + gl::GLGetProcAddressProc get_proc_address = reinterpret_cast<gl::GLGetProcAddressProc>(base::GetFunctionPointerFromNativeLibrary(eglLibrary, "eglGetProcAddress")); if (!get_proc_address) { LOG(ERROR) << "eglGetProcAddress not found."; - base::UnloadNativeLibrary(eglgles2Library); + base::UnloadNativeLibrary(eglLibrary); + base::UnloadNativeLibrary(gles2Library); return false; } gl::SetGLGetProcAddressProc(get_proc_address); - gl::AddGLNativeLibrary(eglgles2Library); + gl::AddGLNativeLibrary(eglLibrary); + gl::AddGLNativeLibrary(gles2Library); return true; } diff --git a/src/core/type_conversion.h b/src/core/type_conversion.h index d9ba735bd..eb7c9d48b 100644 --- a/src/core/type_conversion.h +++ b/src/core/type_conversion.h @@ -142,6 +142,11 @@ inline QRectF toQt(const gfx::RectF &rect) return QRectF(rect.x(), rect.y(), rect.width(), rect.height()); } +inline gfx::Size toGfx(const QSize &size) +{ + return gfx::Size(size.width(), size.height()); +} + inline QSize toQt(const gfx::Size &size) { return QSize(size.width(), size.height()); diff --git a/src/core/url_request_context_getter_qt.cpp b/src/core/url_request_context_getter_qt.cpp index 4f30881c8..b77cd50f9 100644 --- a/src/core/url_request_context_getter_qt.cpp +++ b/src/core/url_request_context_getter_qt.cpp @@ -59,6 +59,8 @@ #include "net/dns/mapped_host_resolver.h" #include "net/extras/sqlite/sqlite_channel_id_store.h" #include "net/http/http_auth_handler_factory.h" +#include "net/http/http_auth_preferences.h" +#include "net/http/http_auth_scheme.h" #include "net/http/http_cache.h" #include "net/http/http_network_session.h" #include "net/http/http_server_properties_impl.h" @@ -209,6 +211,13 @@ void URLRequestContextGetterQt::generateAllStorage() m_updateAllStorage = false; } +static const char* const kDefaultAuthSchemes[] = { net::kBasicAuthScheme, + net::kDigestAuthScheme, +#if defined(USE_KERBEROS) && !defined(OS_ANDROID) + net::kNegotiateAuthScheme, +#endif + net::kNtlmAuthScheme }; + void URLRequestContextGetterQt::generateStorage() { Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); @@ -251,7 +260,15 @@ void URLRequestContextGetterQt::generateStorage() m_storage->set_ssl_config_service(new net::SSLConfigServiceDefaults); m_storage->set_transport_security_state(std::unique_ptr<net::TransportSecurityState>(new net::TransportSecurityState())); - m_storage->set_http_auth_handler_factory(net::HttpAuthHandlerFactory::CreateDefault(host_resolver.get())); + if (!m_httpAuthPreferences) { + std::vector<std::string> auth_types(std::begin(kDefaultAuthSchemes), std::end(kDefaultAuthSchemes)); + m_httpAuthPreferences.reset(new net::HttpAuthPreferences(auth_types +#if defined(OS_POSIX) && !defined(OS_ANDROID) + , std::string() /* gssapi library name */ +#endif + )); + } + m_storage->set_http_auth_handler_factory(net::HttpAuthHandlerRegistryFactory::Create(m_httpAuthPreferences.get(), host_resolver.get())); m_storage->set_http_server_properties(std::unique_ptr<net::HttpServerProperties>(new net::HttpServerPropertiesImpl)); // Give |m_storage| ownership at the end in case it's |mapped_host_resolver|. diff --git a/src/core/url_request_context_getter_qt.h b/src/core/url_request_context_getter_qt.h index 511d9eb04..495c9eb28 100644 --- a/src/core/url_request_context_getter_qt.h +++ b/src/core/url_request_context_getter_qt.h @@ -62,6 +62,7 @@ #include <QtCore/qsharedpointer.h> namespace net { +class HttpAuthPreferences; class MappedHostResolver; class ProxyConfigService; } @@ -126,6 +127,7 @@ private: scoped_refptr<CookieMonsterDelegateQt> m_cookieDelegate; content::URLRequestInterceptorScopedVector m_requestInterceptors; std::unique_ptr<net::HttpNetworkSession> m_httpNetworkSession; + std::unique_ptr<net::HttpAuthPreferences> m_httpAuthPreferences; QList<QByteArray> m_installedCustomSchemes; QWebEngineUrlRequestInterceptor* m_requestInterceptor; diff --git a/src/core/url_request_custom_job.cpp b/src/core/url_request_custom_job.cpp index 47c9b3b4c..b49135f79 100644 --- a/src/core/url_request_custom_job.cpp +++ b/src/core/url_request_custom_job.cpp @@ -56,6 +56,9 @@ URLRequestCustomJob::URLRequestCustomJob(URLRequest *request, , m_proxy(new URLRequestCustomJobProxy(this, scheme, adapter)) , m_device(nullptr) , m_error(0) + , m_pendingReadSize(0) + , m_pendingReadPos(0) + , m_pendingReadBuffer(nullptr) { } @@ -83,6 +86,12 @@ void URLRequestCustomJob::Kill() DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 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; content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, base::Bind(&URLRequestCustomJobProxy::release, @@ -127,13 +136,64 @@ int URLRequestCustomJob::ReadRawData(IOBuffer *buf, int bufSize) if (m_error) return m_error; qint64 rv = m_device ? m_device->read(buf->data(), bufSize) : -1; - if (rv >= 0) { + 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/url_request_custom_job.h b/src/core/url_request_custom_job.h index 68a834d48..021cf3204 100644 --- a/src/core/url_request_custom_job.h +++ b/src/core/url_request_custom_job.h @@ -70,12 +70,16 @@ 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; int m_error; + int m_pendingReadSize; + int m_pendingReadPos; + net::IOBuffer *m_pendingReadBuffer; friend class URLRequestCustomJobProxy; diff --git a/src/core/url_request_custom_job_delegate.cpp b/src/core/url_request_custom_job_delegate.cpp index 14de9a812..6b82cebb5 100644 --- a/src/core/url_request_custom_job_delegate.cpp +++ b/src/core/url_request_custom_job_delegate.cpp @@ -73,16 +73,23 @@ QByteArray URLRequestCustomJobDelegate::method() const void URLRequestCustomJobDelegate::reply(const QByteArray &contentType, QIODevice *device) { + if (device) + QObject::connect(device, &QIODevice::readyRead, this, &URLRequestCustomJobDelegate::slotReadyRead); content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, base::Bind(&URLRequestCustomJobProxy::reply, m_proxy,contentType.toStdString(),device)); } +void URLRequestCustomJobDelegate::slotReadyRead() +{ + content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, + base::Bind(&URLRequestCustomJobProxy::readyRead, m_proxy)); +} + void URLRequestCustomJobDelegate::abort() { content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::Bind(&URLRequestCustomJobProxy::abort, - m_proxy)); + base::Bind(&URLRequestCustomJobProxy::abort, m_proxy)); } void URLRequestCustomJobDelegate::redirect(const QUrl &url) diff --git a/src/core/url_request_custom_job_delegate.h b/src/core/url_request_custom_job_delegate.h index eb99f3576..3f5e6d591 100644 --- a/src/core/url_request_custom_job_delegate.h +++ b/src/core/url_request_custom_job_delegate.h @@ -74,6 +74,9 @@ public: void abort(); void fail(Error); +private Q_SLOTS: + void slotReadyRead(); + private: URLRequestCustomJobDelegate(URLRequestCustomJobProxy *proxy, const QUrl &url, diff --git a/src/core/url_request_custom_job_proxy.cpp b/src/core/url_request_custom_job_proxy.cpp index d53602c85..832d3d11e 100644 --- a/src/core/url_request_custom_job_proxy.cpp +++ b/src/core/url_request_custom_job_proxy.cpp @@ -144,6 +144,13 @@ void URLRequestCustomJobProxy::fail(int 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(); +} + void URLRequestCustomJobProxy::initialize(GURL url, std::string method) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); diff --git a/src/core/url_request_custom_job_proxy.h b/src/core/url_request_custom_job_proxy.h index df7171f5e..3ea30a4e1 100644 --- a/src/core/url_request_custom_job_proxy.h +++ b/src/core/url_request_custom_job_proxy.h @@ -63,6 +63,7 @@ public: QWeakPointer<const BrowserContextAdapter> adapter); ~URLRequestCustomJobProxy(); + // Called from URLRequestCustomJobDelegate via post: //void setReplyCharset(const std::string &); void reply(std::string mimeType, QIODevice *device); void redirect(GURL url); @@ -70,12 +71,13 @@ public: void fail(int error); void release(); void initialize(GURL url, std::string method); + void readyRead(); - //IO thread owned + // IO thread owned: URLRequestCustomJob *m_job; bool m_started; - //UI thread owned + // UI thread owned: std::string m_scheme; URLRequestCustomJobDelegate *m_delegate; QWeakPointer<const BrowserContextAdapter> m_adapter; diff --git a/src/core/web_event_factory.cpp b/src/core/web_event_factory.cpp index b785adec0..2e0323f6d 100644 --- a/src/core/web_event_factory.cpp +++ b/src/core/web_event_factory.cpp @@ -1289,38 +1289,56 @@ WebGestureEvent WebEventFactory::toWebGestureEvent(QNativeGestureEvent *ev, doub } #endif -blink::WebMouseWheelEvent WebEventFactory::toWebWheelEvent(QWheelEvent *ev, double dpiScale) +static void setBlinkWheelEventDelta(blink::WebMouseWheelEvent &webEvent) { - WebMouseWheelEvent webEvent; - webEvent.delta_x = 0; - webEvent.delta_y = 0; - webEvent.wheel_ticks_x = 0; - webEvent.wheel_ticks_y = 0; - webEvent.SetType(webEventTypeForEvent(ev)); - webEvent.SetModifiers(modifiersForEvent(ev)); - webEvent.SetTimeStampSeconds(currentTimeForEvent(ev)); - - webEvent.wheel_ticks_x = static_cast<float>(ev->angleDelta().x()) / QWheelEvent::DefaultDeltasPerStep; - webEvent.wheel_ticks_y = static_cast<float>(ev->angleDelta().y()) / QWheelEvent::DefaultDeltasPerStep; - // We can't use the device specific QWheelEvent::pixelDelta(), so we calculate // a pixel delta based on ticks and scroll per line. static const float cDefaultQtScrollStep = 20.f; #if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)) - const int wheelScrollLines = QGuiApplication::styleHints()->wheelScrollLines(); + static const int wheelScrollLines = QGuiApplication::styleHints()->wheelScrollLines(); #else - const int wheelScrollLines = 3; + static const int wheelScrollLines = 3; #endif webEvent.delta_x = webEvent.wheel_ticks_x * wheelScrollLines * cDefaultQtScrollStep; webEvent.delta_y = webEvent.wheel_ticks_y * wheelScrollLines * cDefaultQtScrollStep; +} + +blink::WebMouseWheelEvent WebEventFactory::toWebWheelEvent(QWheelEvent *ev, double dpiScale) +{ + WebMouseWheelEvent webEvent; + webEvent.SetType(webEventTypeForEvent(ev)); + webEvent.SetModifiers(modifiersForEvent(ev)); + webEvent.SetTimeStampSeconds(currentTimeForEvent(ev)); webEvent.SetPositionInWidget(ev->x() / dpiScale, ev->y() / dpiScale); webEvent.SetPositionInScreen(ev->globalX(), ev->globalY()); + webEvent.wheel_ticks_x = static_cast<float>(ev->angleDelta().x()) / QWheelEvent::DefaultDeltasPerStep; + webEvent.wheel_ticks_y = static_cast<float>(ev->angleDelta().y()) / QWheelEvent::DefaultDeltasPerStep; + setBlinkWheelEventDelta(webEvent); + return webEvent; } +bool WebEventFactory::coalesceWebWheelEvent(blink::WebMouseWheelEvent &webEvent, QWheelEvent *ev, double dpiScale) +{ + if (webEventTypeForEvent(ev) != webEvent.GetType()) + return false; + if (modifiersForEvent(ev) != webEvent.GetModifiers()) + return false; + + webEvent.SetTimeStampSeconds(currentTimeForEvent(ev)); + webEvent.SetPositionInWidget(ev->x() / dpiScale, ev->y() / dpiScale); + webEvent.SetPositionInScreen(ev->globalX(), ev->globalY()); + + webEvent.wheel_ticks_x += static_cast<float>(ev->angleDelta().x()) / QWheelEvent::DefaultDeltasPerStep; + webEvent.wheel_ticks_y += static_cast<float>(ev->angleDelta().y()) / QWheelEvent::DefaultDeltasPerStep; + setBlinkWheelEventDelta(webEvent); + + return true; +} + content::NativeWebKeyboardEvent WebEventFactory::toWebKeyboardEvent(QKeyEvent *ev) { content::NativeWebKeyboardEvent webKitEvent(reinterpret_cast<gfx::NativeEvent>(ev)); diff --git a/src/core/web_event_factory.h b/src/core/web_event_factory.h index c9ae86a73..ca0f6035f 100644 --- a/src/core/web_event_factory.h +++ b/src/core/web_event_factory.h @@ -70,6 +70,7 @@ public: static blink::WebGestureEvent toWebGestureEvent(QNativeGestureEvent *, double dpiScale); #endif static blink::WebMouseWheelEvent toWebWheelEvent(QWheelEvent*, double dpiScale); + static bool coalesceWebWheelEvent(blink::WebMouseWheelEvent &, QWheelEvent*, double dpiScale); static content::NativeWebKeyboardEvent toWebKeyboardEvent(QKeyEvent*); }; diff --git a/src/src.pro b/src/src.pro index 1cd23f9fa..76d342c8d 100644 --- a/src/src.pro +++ b/src/src.pro @@ -1,5 +1,5 @@ include($$QTWEBENGINE_OUT_ROOT/qtwebengine-config.pri) -QT_FOR_CONFIG += webengine-private +QT_FOR_CONFIG += webengine webengine-private TEMPLATE = subdirs diff --git a/src/tools/qwebengine_convert_dict/main.cpp b/src/tools/qwebengine_convert_dict/main.cpp index e7dcc22d9..3a1a1ff64 100644 --- a/src/tools/qwebengine_convert_dict/main.cpp +++ b/src/tools/qwebengine_convert_dict/main.cpp @@ -30,6 +30,7 @@ #include <QTextStream> #include <QLibraryInfo> #include <QDir> +#include <QCoreApplication> // see also src/core/type_conversion.h inline base::FilePath::StringType toFilePathString(const QString &str) @@ -117,6 +118,11 @@ QString frameworkIcuDataPath() int main(int argc, char *argv[]) { + // Required only for making QLibraryInfo::location() return a valid path, when the application + // picks up a qt.conf file (which is the case for official Qt packages). + QCoreApplication app(argc, argv); + Q_UNUSED(app); + QTextStream out(stdout); if (argc != 3) { diff --git a/src/webengine/api/qquickwebenginefaviconprovider.cpp b/src/webengine/api/qquickwebenginefaviconprovider.cpp index b5ad6960a..3255f22be 100644 --- a/src/webengine/api/qquickwebenginefaviconprovider.cpp +++ b/src/webengine/api/qquickwebenginefaviconprovider.cpp @@ -70,7 +70,11 @@ QUrl QQuickWebEngineFaviconProvider::faviconProviderUrl(const QUrl &url) QUrl providerUrl; providerUrl.setScheme(QStringLiteral("image")); providerUrl.setHost(identifier()); - providerUrl.setPath(QStringLiteral("/%1").arg(url.toString())); + providerUrl.setPath(QStringLiteral("/%1").arg(url.toString(QUrl::RemoveQuery | QUrl::RemoveFragment))); + if (url.hasQuery()) + providerUrl.setQuery(url.query(QUrl::FullyDecoded)); + if (url.hasFragment()) + providerUrl.setFragment(url.fragment(QUrl::FullyDecoded)); return providerUrl; } diff --git a/src/webengine/webengine.pro b/src/webengine/webengine.pro index 4b2170cbc..24fa2d9d8 100644 --- a/src/webengine/webengine.pro +++ b/src/webengine/webengine.pro @@ -1,5 +1,5 @@ include($$QTWEBENGINE_OUT_ROOT/qtwebengine-config.pri) -QT_FOR_CONFIG += webengine-private +QT_FOR_CONFIG += webengine webengine-private TARGET = QtWebEngine diff --git a/src/webenginewidgets/api/qwebengineprofile.cpp b/src/webenginewidgets/api/qwebengineprofile.cpp index f844ddcd6..c4de46b67 100644 --- a/src/webenginewidgets/api/qwebengineprofile.cpp +++ b/src/webenginewidgets/api/qwebengineprofile.cpp @@ -145,24 +145,35 @@ using QtWebEngineCore::BrowserContextAdapter; \sa QWebEngineDownloadItem, QWebEnginePage::download() */ -QWebEngineProfilePrivate::QWebEngineProfilePrivate(QSharedPointer<BrowserContextAdapter> browserContext) +QWebEngineBrowserContext::QWebEngineBrowserContext(QSharedPointer<QtWebEngineCore::BrowserContextAdapter> browserContext, QWebEngineProfilePrivate *profile) + : QObject(BrowserContextAdapter::globalQObjectRoot()) + , browserContextRef(browserContext) + , m_profile(profile) +{ + browserContextRef->addClient(m_profile); +} + +QWebEngineBrowserContext::~QWebEngineBrowserContext() +{ + Q_ASSERT(m_profile); + // In the case the user sets this profile as the parent of the interceptor + // it can be deleted before the browser-context still referencing it is. + browserContextRef->setRequestInterceptor(nullptr); + browserContextRef->removeClient(m_profile); +} + +QWebEngineProfilePrivate::QWebEngineProfilePrivate(QSharedPointer<QtWebEngineCore::BrowserContextAdapter> browserContext) : m_settings(new QWebEngineSettings()) , m_scriptCollection(new QWebEngineScriptCollection(new QWebEngineScriptCollectionPrivate(browserContext->userResourceController()))) - , m_browserContextRef(browserContext) + , m_browserContext(new QWebEngineBrowserContext(browserContext, this)) { - m_browserContextRef->addClient(this); m_settings->d_ptr->initDefaults(); } QWebEngineProfilePrivate::~QWebEngineProfilePrivate() { - // In the case the user sets this profile as the parent of the interceptor - // it can be deleted before the browser-context still referencing it is. - m_browserContextRef->setRequestInterceptor(nullptr); - delete m_settings; m_settings = 0; - m_browserContextRef->removeClient(this); Q_FOREACH (QWebEngineDownloadItem* download, m_ongoingDownloads) { if (download) @@ -172,6 +183,11 @@ QWebEngineProfilePrivate::~QWebEngineProfilePrivate() m_ongoingDownloads.clear(); } +QSharedPointer<QtWebEngineCore::BrowserContextAdapter> QWebEngineProfilePrivate::browserContext() const +{ + return m_browserContext->browserContextRef; +} + void QWebEngineProfilePrivate::downloadDestroyed(quint32 downloadId) { m_ongoingDownloads.remove(downloadId); diff --git a/src/webenginewidgets/api/qwebengineprofile_p.h b/src/webenginewidgets/api/qwebengineprofile_p.h index ffb28ec6d..8cbf241f3 100644 --- a/src/webenginewidgets/api/qwebengineprofile_p.h +++ b/src/webenginewidgets/api/qwebengineprofile_p.h @@ -65,15 +65,30 @@ class BrowserContextAdapter; QT_BEGIN_NAMESPACE +class QWebEngineProfilePrivate; class QWebEngineSettings; +// This is a wrapper class for BrowserContextAdapter. BrowserContextAdapter must be destructed before WebEngineContext +// is destructed. Therefore access it via the QWebEngineBrowserContext which parent is the WebEngineContext::globalQObject. +// This guarantees the destruction together with the WebEngineContext. +class QWebEngineBrowserContext : public QObject { +public: + QWebEngineBrowserContext(QSharedPointer<QtWebEngineCore::BrowserContextAdapter> browserContext, QWebEngineProfilePrivate *profile); + ~QWebEngineBrowserContext(); + + QSharedPointer<QtWebEngineCore::BrowserContextAdapter> browserContextRef; + +private: + QWebEngineProfilePrivate *m_profile; +}; + class QWebEngineProfilePrivate : public QtWebEngineCore::BrowserContextAdapterClient { public: Q_DECLARE_PUBLIC(QWebEngineProfile) QWebEngineProfilePrivate(QSharedPointer<QtWebEngineCore::BrowserContextAdapter> browserContext); ~QWebEngineProfilePrivate(); - QSharedPointer<QtWebEngineCore::BrowserContextAdapter> browserContext() const { return m_browserContextRef; } + QSharedPointer<QtWebEngineCore::BrowserContextAdapter> browserContext() const; QWebEngineSettings *settings() const { return m_settings; } void downloadDestroyed(quint32 downloadId); @@ -85,7 +100,7 @@ private: QWebEngineProfile *q_ptr; QWebEngineSettings *m_settings; QScopedPointer<QWebEngineScriptCollection> m_scriptCollection; - QSharedPointer<QtWebEngineCore::BrowserContextAdapter> m_browserContextRef; + QPointer<QWebEngineBrowserContext> m_browserContext; QMap<quint32, QPointer<QWebEngineDownloadItem> > m_ongoingDownloads; }; diff --git a/src/webenginewidgets/webenginewidgets.pro b/src/webenginewidgets/webenginewidgets.pro index 29c961c80..958dec07b 100644 --- a/src/webenginewidgets/webenginewidgets.pro +++ b/src/webenginewidgets/webenginewidgets.pro @@ -1,4 +1,3 @@ -include($$QTWEBENGINE_OUT_ROOT/qtwebengine-config.pri) QT_FOR_CONFIG += webengine-private TARGET = QtWebEngineWidgets diff --git a/tests/auto/quick/quick.pro b/tests/auto/quick/quick.pro index 75217c1ec..2e6343469 100644 --- a/tests/auto/quick/quick.pro +++ b/tests/auto/quick/quick.pro @@ -1,4 +1,3 @@ -include($$QTWEBENGINE_OUT_ROOT/qtwebengine-config.pri) QT_FOR_CONFIG += webengine-private TEMPLATE = subdirs diff --git a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp index 093bc2e43..400105152 100644 --- a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp +++ b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp @@ -49,6 +49,7 @@ private Q_SLOTS: void urlSchemeHandlers(); void urlSchemeHandlerFailRequest(); void urlSchemeHandlerFailOnRead(); + void urlSchemeHandlerStreaming(); void customUserAgent(); void httpAcceptLanguage(); void downloadItem(); @@ -178,6 +179,74 @@ public: } }; +class StreamingIODevice : public QIODevice { + Q_OBJECT +public: + StreamingIODevice(QObject *parent) : QIODevice(parent), m_bytesRead(0), m_bytesAvailable(0) + { + setOpenMode(QIODevice::ReadOnly); + m_timer.start(100, this); + } + bool isSequential() const override { return true; } + qint64 bytesAvailable() const override + { return m_bytesAvailable; } + bool atEnd() const override + { + return (m_data.size() >= 1000 && m_bytesRead >= 1000); + } +protected: + void timerEvent(QTimerEvent *) override + { + QMutexLocker lock(&m_mutex); + m_bytesAvailable += 200; + m_data.append(200, 'c'); + emit readyRead(); + if (m_data.size() >= 1000) { + m_timer.stop(); + emit readChannelFinished(); + } + } + + qint64 readData(char *data, qint64 maxlen) override + { + QMutexLocker lock(&m_mutex); + qint64 len = qMin(qint64(m_bytesAvailable), maxlen); + if (len) { + memcpy(data, m_data.constData() + m_bytesRead, len); + m_bytesAvailable -= len; + m_bytesRead += len; + } else if (m_data.size() > 0) + return -1; + + return len; + } + qint64 writeData(const char *, qint64) override + { + return 0; + } + +private: + QMutex m_mutex; + QByteArray m_data; + QBasicTimer m_timer; + int m_bytesRead; + int m_bytesAvailable; +}; + +class StreamingUrlSchemeHandler : public QWebEngineUrlSchemeHandler +{ +public: + StreamingUrlSchemeHandler(QObject *parent = nullptr) + : QWebEngineUrlSchemeHandler(parent) + { + } + + void requestStarted(QWebEngineUrlRequestJob *job) + { + job->reply("text/plain;charset=utf-8", new StreamingIODevice(job)); + } +}; + static bool loadSync(QWebEngineView *view, const QUrl &url, int timeout = 5000) { // Ripped off QTRY_VERIFY. @@ -310,6 +379,22 @@ void tst_QWebEngineProfile::urlSchemeHandlerFailOnRead() QCOMPARE(toPlainTextSync(view.page()), QString()); } +void tst_QWebEngineProfile::urlSchemeHandlerStreaming() +{ + StreamingUrlSchemeHandler handler; + QWebEngineProfile profile; + profile.installUrlSchemeHandler("stream", &handler); + QWebEngineView view; + QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool))); + view.setPage(new QWebEnginePage(&profile, &view)); + view.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); + view.load(QUrl(QStringLiteral("stream://whatever"))); + QVERIFY(loadFinishedSpy.wait()); + QByteArray result; + result.append(1000, 'c'); + QCOMPARE(toPlainTextSync(view.page()), QString::fromLatin1(result)); +} + void tst_QWebEngineProfile::customUserAgent() { QString defaultUserAgent = QWebEngineProfile::defaultProfile()->httpUserAgent(); diff --git a/tests/auto/widgets/tests.pri b/tests/auto/widgets/tests.pri index dc0461e2b..7bd00834c 100644 --- a/tests/auto/widgets/tests.pri +++ b/tests/auto/widgets/tests.pri @@ -1,4 +1,3 @@ -include($$QTWEBENGINE_OUT_ROOT/qtwebengine-config.pri) QT_FOR_CONFIG += webengine-private TEMPLATE = app diff --git a/tests/auto/widgets/widgets.pro b/tests/auto/widgets/widgets.pro index e007df711..fbabdeaad 100644 --- a/tests/auto/widgets/widgets.pro +++ b/tests/auto/widgets/widgets.pro @@ -1,10 +1,8 @@ -include($$QTWEBENGINE_OUT_ROOT/qtwebengine-config.pri) -QT_FOR_CONFIG += webengine-private +QT_FOR_CONFIG += webengine TEMPLATE = subdirs SUBDIRS += \ - qwebengineaccessibility \ qwebenginedefaultsurfaceformat \ qwebenginedownloads \ qwebenginefaviconmanager \ @@ -18,6 +16,10 @@ SUBDIRS += \ qwebenginesettings \ qwebengineview +qtConfig(accessibility) { + SUBDIRS += qwebengineaccessibility +} + qtConfig(spellchecker):!cross_compile { !qtConfig(native-spellchecker) { SUBDIRS += qwebenginespellcheck |