diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-05-28 09:55:20 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-05-28 12:02:27 +0200 |
commit | cdfa08e44b416aebaf5a5e4762ed759062a10e88 (patch) | |
tree | 03b97f5b8a85514b4344b7dad015e359d5e19e4c | |
parent | d927e9a0b45a79bc49f7e80f6c05ac20617902ad (diff) | |
parent | 5087a4eaa32e08fe59f1f5b71dc21dda0876a759 (diff) | |
download | qtwebengine-cdfa08e44b416aebaf5a5e4762ed759062a10e88.tar.gz |
Merge "Merge remote-tracking branch 'origin/5.15' into dev"
32 files changed, 503 insertions, 150 deletions
diff --git a/mkspecs/features/gn_generator.prf b/mkspecs/features/gn_generator.prf index 6ff09d851..a83b59847 100644 --- a/mkspecs/features/gn_generator.prf +++ b/mkspecs/features/gn_generator.prf @@ -230,6 +230,9 @@ win32 { CONFIG(rtti_off): GN_CONTENTS += " configs += [\"//build/config/compiler:no_rtti\"]" CONFIG(rtti): GN_CONTENTS += " configs += [\"//build/config/compiler:rtti\"]" } +gcc:!qtConfig(reduce_exports) { + GN_CONTENTS += " configs -= [\"//build/config/gcc:symbol_visibility_hidden\"]" +} GN_CONTENTS += " if (moc_source_h_files != []) {" GN_CONTENTS += " deps += [" diff --git a/src/3rdparty b/src/3rdparty -Subproject 623647821aa7c7565ed5153a27c5a1bb088efbe +Subproject 7b2f027ea83c372c33d5b50deb65a2d98244aa0 diff --git a/src/core/compositor/chromium_gpu_helper.cpp b/src/core/compositor/chromium_gpu_helper.cpp index 71d0f3687..1ddbf75e5 100644 --- a/src/core/compositor/chromium_gpu_helper.cpp +++ b/src/core/compositor/chromium_gpu_helper.cpp @@ -57,9 +57,7 @@ #include "content/gpu/gpu_child_thread.h" #include "gpu/ipc/service/gpu_channel_manager.h" -#ifdef Q_OS_QNX -#include "content/common/gpu/stream_texture_qnx.h" -#endif +#include <QtGlobal> // We need this for Q_UNUSED scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner() { @@ -87,15 +85,3 @@ void ProgressFlingIfNeeded(content::RenderWidgetHost *host, const base::TimeTick { content::RenderWidgetHostImpl::From(host)->ProgressFlingIfNeeded(current_time); } - -#ifdef Q_OS_QNX -EGLStreamData eglstream_connect_consumer(gpu::Texture *tex) -{ - EGLStreamData egl_stream; - content::StreamTexture* image = static_cast<content::StreamTexture *>(tex->GetLevelImage(GL_TEXTURE_EXTERNAL_OES, 0)); - if (image) { - image->ConnectConsumerIfNeeded(&egl_stream.egl_display, &egl_stream.egl_str_handle); - } - return egl_stream; -} -#endif diff --git a/src/core/compositor/chromium_gpu_helper.h b/src/core/compositor/chromium_gpu_helper.h index 4086d12ab..e692b9b85 100644 --- a/src/core/compositor/chromium_gpu_helper.h +++ b/src/core/compositor/chromium_gpu_helper.h @@ -40,8 +40,6 @@ #ifndef CHROMIUM_GPU_HELPER_H #define CHROMIUM_GPU_HELPER_H -#include <QtGlobal> // We need this for the Q_OS_QNX define. - #include "base/memory/scoped_refptr.h" namespace base { @@ -72,18 +70,4 @@ unsigned int service_id(gpu::TextureBase *tex); void ProgressFlingIfNeeded(content::RenderWidgetHost *host, const base::TimeTicks ¤t_time); -#ifdef Q_OS_QNX -typedef void* EGLDisplay; -typedef void* EGLStreamKHR; - -struct EGLStreamData { - EGLDisplay egl_display; - EGLStreamKHR egl_str_handle; - - EGLStreamData(): egl_display(NULL), egl_str_handle(NULL) {} -}; - -EGLStreamData eglstream_connect_consumer(gpu::Texture *tex); -#endif - #endif // CHROMIUM_GPU_HELPER_H diff --git a/src/core/compositor/delegated_frame_node.cpp b/src/core/compositor/delegated_frame_node.cpp index c20069558..de79ba514 100644 --- a/src/core/compositor/delegated_frame_node.cpp +++ b/src/core/compositor/delegated_frame_node.cpp @@ -142,9 +142,6 @@ private: #if defined(USE_OZONE) bool m_ownsTexture; #endif -#ifdef Q_OS_QNX - EGLStreamData m_eglStreamData; -#endif friend class DelegatedFrameNode; }; #endif // QT_CONFIG(opengl) @@ -467,20 +464,6 @@ void MailboxTexture::bind() if (m_fence) m_fence->wait(); glBindTexture(m_target, m_textureId); -#ifdef Q_OS_QNX - if (m_target == GL_TEXTURE_EXTERNAL_OES) { - static bool resolved = false; - static PFNEGLSTREAMCONSUMERACQUIREKHRPROC eglStreamConsumerAcquire = 0; - - if (!resolved) { - QOpenGLContext *context = QOpenGLContext::currentContext(); - eglStreamConsumerAcquire = (PFNEGLSTREAMCONSUMERACQUIREKHRPROC)context->getProcAddress("eglStreamConsumerAcquireKHR"); - resolved = true; - } - if (eglStreamConsumerAcquire) - eglStreamConsumerAcquire(m_eglStreamData.egl_display, m_eglStreamData.egl_str_handle); - } -#endif } #endif // QT_CONFIG(opengl) diff --git a/src/core/content_client_qt.cpp b/src/core/content_client_qt.cpp index 80eb9ceb1..083e10f2a 100644 --- a/src/core/content_client_qt.cpp +++ b/src/core/content_client_qt.cpp @@ -54,7 +54,7 @@ #include "ui/base/layout.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" - +#include "services/service_manager/embedder/switches.h" #include "type_conversion.h" #include <QCoreApplication> @@ -68,9 +68,7 @@ #include "third_party/widevine/cdm/widevine_cdm_common.h" #if BUILDFLAG(ENABLE_WIDEVINE) && !BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT) #define WIDEVINE_CDM_AVAILABLE_NOT_COMPONENT -namespace switches { -const char kCdmWidevinePath[] = "widevine-path"; -} + // File name of the CDM on different platforms. const char kWidevineCdmFileName[] = #if defined(OS_MACOSX) @@ -287,7 +285,7 @@ static bool IsWidevineAvailable(base::FilePath *cdm_path, content::CdmCapability *capability) { QStringList pluginPaths; - const base::CommandLine::StringType widevine_argument = base::CommandLine::ForCurrentProcess()->GetSwitchValueNative(switches::kCdmWidevinePath); + const base::CommandLine::StringType widevine_argument = base::CommandLine::ForCurrentProcess()->GetSwitchValueNative(service_manager::switches::kCdmWidevinePath); if (!widevine_argument.empty()) pluginPaths << QtWebEngineCore::toQt(widevine_argument); else { diff --git a/src/core/extensions/component_extension_resource_manager_qt.cpp b/src/core/extensions/component_extension_resource_manager_qt.cpp index bb1dd045c..1f58de151 100644 --- a/src/core/extensions/component_extension_resource_manager_qt.cpp +++ b/src/core/extensions/component_extension_resource_manager_qt.cpp @@ -92,12 +92,27 @@ const ui::TemplateReplacements *ComponentExtensionResourceManagerQt::GetTemplate void ComponentExtensionResourceManagerQt::AddComponentResourceEntries(const GritResourceMap *entries, size_t size) { + base::FilePath gen_folder_path = base::FilePath().AppendASCII("@out_folder@/gen/chrome/browser/resources/"); + gen_folder_path = gen_folder_path.NormalizePathSeparators(); + for (size_t i = 0; i < size; ++i) { base::FilePath resource_path = base::FilePath().AppendASCII(entries[i].name); resource_path = resource_path.NormalizePathSeparators(); - DCHECK(!base::Contains(path_to_resource_id_, resource_path)); - path_to_resource_id_[resource_path] = entries[i].value; + + if (!gen_folder_path.IsParent(resource_path)) { + DCHECK(!base::Contains(path_to_resource_id_, resource_path)); + path_to_resource_id_[resource_path] = entries[i].value; + } else { + // If the resource is a generated file, strip the generated folder's path, + // so that it can be served from a normal URL (as if it were not + // generated). + base::FilePath effective_path = + base::FilePath().AppendASCII(resource_path.AsUTF8Unsafe().substr( + gen_folder_path.value().length())); + DCHECK(!base::Contains(path_to_resource_id_, effective_path)); + path_to_resource_id_[effective_path] = entries[i].value; + } } } diff --git a/src/core/file_picker_controller.cpp b/src/core/file_picker_controller.cpp index 3c81a977c..01a6d0746 100644 --- a/src/core/file_picker_controller.cpp +++ b/src/core/file_picker_controller.cpp @@ -39,9 +39,8 @@ #include "file_picker_controller.h" #include "type_conversion.h" -#if defined(OS_WIN) + #include "base/files/file_path.h" -#endif #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/file_select_listener.h" @@ -72,27 +71,64 @@ void FilePickerController::accepted(const QStringList &files) for (const QString &urlString : files) { // We accept strings on both absolute-path and file-URL form: - if (QDir::isAbsolutePath(urlString)) { - QString absolutePath = QDir::fromNativeSeparators(urlString); -#if defined(OS_WIN) - if (absolutePath.at(0).isLetter() && absolutePath.at(1) == QLatin1Char(':') && !base::FilePath::IsSeparator(absolutePath.at(2).toLatin1())) - qWarning("Ignoring invalid item in FilePickerController::accepted(QStringList): %s", qPrintable(urlString)); - else + if (toFilePath(urlString).IsAbsolute()) { + stringList.append(urlString); + continue; + } + + if (urlString.startsWith("file:")) { + base::FilePath filePath = toFilePath(urlString).NormalizePathSeparators(); + std::vector<base::FilePath::StringType> pathComponents; + // Splits the file URL into host name, path and file name. + filePath.GetComponents(&pathComponents); + + QString absolutePath; +#if !defined(OS_WIN) + absolutePath = "/"; #endif - stringList.append(absolutePath); - } else { - QUrl url(urlString, QUrl::StrictMode); - if (url.isLocalFile() && QDir::isAbsolutePath(url.toLocalFile())) { - QString absolutePath = url.toLocalFile(); + + QString scheme = toQt(pathComponents[0]); + if (scheme.size() > 5) { #if defined(OS_WIN) - if (absolutePath.at(0).isLetter() && absolutePath.at(1) == QLatin1Char(':') && !base::FilePath::IsSeparator(absolutePath.at(2).toLatin1())) + // There is no slash at the end of the file scheme and it is valid on Windows: file:C:/ + if (scheme.at(5).isLetter() && scheme.at(6) != ':') { + absolutePath += scheme.at(5) + ":/"; + } else { +#endif qWarning("Ignoring invalid item in FilePickerController::accepted(QStringList): %s", qPrintable(urlString)); - else + continue; +#if defined(OS_WIN) + } +#endif + } + + // Non-local file and UNC Path validation: file://path/file + if (base::FilePath::IsSeparator(urlString.at(5).toLatin1()) + && base::FilePath::IsSeparator(urlString.at(6).toLatin1()) + && !base::FilePath::IsSeparator(urlString.at(7).toLatin1())) { +#if defined(OS_WIN) + if (urlString.at(8) != ':' && pathComponents.size() > 2) { + absolutePath += "//"; +#else + if (pathComponents.size() > 2) { + absolutePath += "/"; #endif - stringList.append(absolutePath); - } else - qWarning("Ignoring invalid item in FilePickerController::accepted(QStringList): %s", qPrintable(urlString)); + } else { + qWarning("Ignoring invalid item in FilePickerController::accepted(QStringList): %s", qPrintable(urlString)); + continue; + } + } + + // Build absolute path from file URI componenets. + for (int j = 1; j < pathComponents.size(); j++) + absolutePath += toQt(pathComponents[j]) + (j != pathComponents.size()-1 ? "/" : ""); + + if (toFilePath(absolutePath).IsAbsolute()) { + stringList.append(absolutePath); + continue; + } } + qWarning("Ignoring invalid item in FilePickerController::accepted(QStringList): %s", qPrintable(urlString)); } FilePickerController::filesSelectedInChooser(stringList); diff --git a/src/core/gn_run.pro b/src/core/gn_run.pro index 4f1ccce43..f72efc8d5 100644 --- a/src/core/gn_run.pro +++ b/src/core/gn_run.pro @@ -61,3 +61,8 @@ build_pass|!debug_and_release { notParallel.target = .NOTPARALLEL QMAKE_EXTRA_TARGETS += notParallel } + +build_pass:CONFIG(debug, debug|release) { + TARGET = gn_run_debug +} + diff --git a/src/core/permission_manager_qt.cpp b/src/core/permission_manager_qt.cpp index 2f9543769..862a1c262 100644 --- a/src/core/permission_manager_qt.cpp +++ b/src/core/permission_manager_qt.cpp @@ -102,6 +102,18 @@ static bool canRequestPermissionFor(ProfileAdapter::PermissionType type) return false; } +static blink::mojom::PermissionStatus toBlink(ProfileAdapter::PermissionState reply) +{ + switch (reply) { + case ProfileAdapter::AskPermission: + return blink::mojom::PermissionStatus::ASK; + case ProfileAdapter::AllowedPermission: + return blink::mojom::PermissionStatus::GRANTED; + case ProfileAdapter::DeniedPermission: + return blink::mojom::PermissionStatus::DENIED; + } +} + PermissionManagerQt::PermissionManagerQt() : m_requestIdCount(0) , m_subscriberIdCount(0) @@ -112,7 +124,7 @@ PermissionManagerQt::~PermissionManagerQt() { } -void PermissionManagerQt::permissionRequestReply(const QUrl &url, ProfileAdapter::PermissionType type, bool reply) +void PermissionManagerQt::permissionRequestReply(const QUrl &url, ProfileAdapter::PermissionType type, ProfileAdapter::PermissionState reply) { // Normalize the QUrl to GURL origin form. const GURL gorigin = toGurl(url).GetOrigin(); @@ -120,9 +132,12 @@ void PermissionManagerQt::permissionRequestReply(const QUrl &url, ProfileAdapter if (origin.isEmpty()) return; QPair<QUrl, ProfileAdapter::PermissionType> key(origin, type); - m_permissions[key] = reply; - blink::mojom::PermissionStatus status = reply ? blink::mojom::PermissionStatus::GRANTED : blink::mojom::PermissionStatus::DENIED; - { + if (reply == ProfileAdapter::AskPermission) + m_permissions.remove(key); + else + m_permissions[key] = (reply == ProfileAdapter::AllowedPermission); + blink::mojom::PermissionStatus status = toBlink(reply); + if (reply != ProfileAdapter::AskPermission) { auto it = m_requests.begin(); while (it != m_requests.end()) { if (it->origin == origin && it->type == type) { @@ -137,6 +152,9 @@ void PermissionManagerQt::permissionRequestReply(const QUrl &url, ProfileAdapter it.second.callback.Run(status); } + if (reply == ProfileAdapter::AskPermission) + return; + auto it = m_multiRequests.begin(); while (it != m_multiRequests.end()) { if (it->origin == origin) { diff --git a/src/core/permission_manager_qt.h b/src/core/permission_manager_qt.h index 6ab071237..e046174df 100644 --- a/src/core/permission_manager_qt.h +++ b/src/core/permission_manager_qt.h @@ -54,10 +54,9 @@ class PermissionManagerQt : public content::PermissionControllerDelegate { public: PermissionManagerQt(); ~PermissionManagerQt(); - typedef ProfileAdapter::PermissionType PermissionType; - void permissionRequestReply(const QUrl &origin, PermissionType type, bool reply); - bool checkPermission(const QUrl &origin, PermissionType type); + void permissionRequestReply(const QUrl &origin, ProfileAdapter::PermissionType type, ProfileAdapter::PermissionState reply); + bool checkPermission(const QUrl &origin, ProfileAdapter::PermissionType type); // content::PermissionManager implementation: int RequestPermission( @@ -99,10 +98,10 @@ public: void UnsubscribePermissionStatusChange(int subscription_id) override; private: - QHash<QPair<QUrl, PermissionType>, bool> m_permissions; + QHash<QPair<QUrl, ProfileAdapter::PermissionType>, bool> m_permissions; struct Request { int id; - PermissionType type; + ProfileAdapter::PermissionType type; QUrl origin; base::OnceCallback<void(blink::mojom::PermissionStatus)> callback; }; @@ -113,7 +112,7 @@ private: base::OnceCallback<void(const std::vector<blink::mojom::PermissionStatus>&)> callback; }; struct Subscription { - PermissionType type; + ProfileAdapter::PermissionType type; QUrl origin; base::RepeatingCallback<void(blink::mojom::PermissionStatus)> callback; }; diff --git a/src/core/profile_adapter.cpp b/src/core/profile_adapter.cpp index b87591c97..4557ad7a4 100644 --- a/src/core/profile_adapter.cpp +++ b/src/core/profile_adapter.cpp @@ -543,7 +543,7 @@ UserResourceControllerHost *ProfileAdapter::userResourceController() return m_userResourceController.data(); } -void ProfileAdapter::permissionRequestReply(const QUrl &origin, PermissionType type, bool reply) +void ProfileAdapter::permissionRequestReply(const QUrl &origin, PermissionType type, PermissionState reply) { static_cast<PermissionManagerQt*>(profile()->GetPermissionControllerDelegate())->permissionRequestReply(origin, type, reply); } diff --git a/src/core/profile_adapter.h b/src/core/profile_adapter.h index 1e5a3e21f..efd56e50e 100644 --- a/src/core/profile_adapter.h +++ b/src/core/profile_adapter.h @@ -164,6 +164,12 @@ public: ClipboardWrite = 6, }; + enum PermissionState { + AskPermission = 0, + AllowedPermission = 1, + DeniedPermission = 2 + }; + HttpCacheType httpCacheType() const; void setHttpCacheType(ProfileAdapter::HttpCacheType); @@ -187,7 +193,7 @@ public: const QList<QByteArray> customUrlSchemes() const; UserResourceControllerHost *userResourceController(); - void permissionRequestReply(const QUrl &origin, PermissionType type, bool reply); + void permissionRequestReply(const QUrl &origin, PermissionType type, PermissionState reply); bool checkPermission(const QUrl &origin, PermissionType type); QString httpAcceptLanguageWithoutQualities() const; diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp index 0f2f21f83..ef482ef08 100644 --- a/src/core/web_contents_adapter.cpp +++ b/src/core/web_contents_adapter.cpp @@ -1360,15 +1360,15 @@ void WebContentsAdapter::grantMediaAccessPermission(const QUrl &securityOrigin, CHECK_INITIALIZED(); // Let the permission manager remember the reply. if (flags & WebContentsAdapterClient::MediaAudioCapture) - m_profileAdapter->permissionRequestReply(securityOrigin, ProfileAdapter::AudioCapturePermission, true); + m_profileAdapter->permissionRequestReply(securityOrigin, ProfileAdapter::AudioCapturePermission, ProfileAdapter::AllowedPermission); if (flags & WebContentsAdapterClient::MediaVideoCapture) - m_profileAdapter->permissionRequestReply(securityOrigin, ProfileAdapter::VideoCapturePermission, true); + m_profileAdapter->permissionRequestReply(securityOrigin, ProfileAdapter::VideoCapturePermission, ProfileAdapter::AllowedPermission); MediaCaptureDevicesDispatcher::GetInstance()->handleMediaAccessPermissionResponse(m_webContents.get(), securityOrigin, flags); } -void WebContentsAdapter::runFeatureRequestCallback(const QUrl &securityOrigin, ProfileAdapter::PermissionType feature, bool allowed) +void WebContentsAdapter::grantFeaturePermission(const QUrl &securityOrigin, ProfileAdapter::PermissionType feature, ProfileAdapter::PermissionState allowed) { - CHECK_INITIALIZED(); + Q_ASSERT(m_profileAdapter); m_profileAdapter->permissionRequestReply(securityOrigin, feature, allowed); } diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h index 66808ce5e..d833314f2 100644 --- a/src/core/web_contents_adapter.h +++ b/src/core/web_contents_adapter.h @@ -196,7 +196,7 @@ public: void grantMediaAccessPermission(const QUrl &securityOrigin, WebContentsAdapterClient::MediaRequestFlags flags); void grantMouseLockPermission(const QUrl &securityOrigin, bool granted); void handlePendingMouseLockPermission(); - void runFeatureRequestCallback(const QUrl &securityOrigin, ProfileAdapter::PermissionType feature, bool allowed); + void grantFeaturePermission(const QUrl &securityOrigin, ProfileAdapter::PermissionType feature, ProfileAdapter::PermissionState allowed); void setBackgroundColor(const QColor &color); QAccessibleInterface *browserAccessible(); diff --git a/src/core/web_contents_delegate_qt.cpp b/src/core/web_contents_delegate_qt.cpp index bf0254e82..63f52433e 100644 --- a/src/core/web_contents_delegate_qt.cpp +++ b/src/core/web_contents_delegate_qt.cpp @@ -251,8 +251,9 @@ void WebContentsDelegateQt::AddNewContents(content::WebContents* source, std::un void WebContentsDelegateQt::CloseContents(content::WebContents *source) { - m_viewClient->close(); GetJavaScriptDialogManager(source)->CancelDialogs(source, /* whatever?: */false); + // Must be the last call because close() might trigger the destruction of this object. + m_viewClient->close(); } void WebContentsDelegateQt::LoadProgressChanged(double progress) diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp index 6e5469ab4..f659d15ca 100644 --- a/src/webengine/api/qquickwebengineview.cpp +++ b/src/webengine/api/qquickwebengineview.cpp @@ -1644,9 +1644,6 @@ void QQuickWebEngineView::grantFeaturePermission(const QUrl &securityOrigin, QQu case MediaAudioVideoCapture: d_ptr->adapter->grantMediaAccessPermission(securityOrigin, WebContentsAdapterClient::MediaRequestFlags(WebContentsAdapterClient::MediaAudioCapture | WebContentsAdapterClient::MediaVideoCapture)); break; - case Geolocation: - d_ptr->adapter->runFeatureRequestCallback(securityOrigin, ProfileAdapter::GeolocationPermission, granted); - break; case DesktopVideoCapture: d_ptr->adapter->grantMediaAccessPermission(securityOrigin, WebContentsAdapterClient::MediaDesktopVideoCapture); break; @@ -1657,8 +1654,13 @@ void QQuickWebEngineView::grantFeaturePermission(const QUrl &securityOrigin, QQu WebContentsAdapterClient::MediaDesktopAudioCapture | WebContentsAdapterClient::MediaDesktopVideoCapture)); break; + case Geolocation: + d_ptr->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::GeolocationPermission, + granted ? ProfileAdapter::AllowedPermission : ProfileAdapter::DeniedPermission); + break; case Notifications: - d_ptr->adapter->runFeatureRequestCallback(securityOrigin, ProfileAdapter::NotificationPermission, granted); + d_ptr->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::NotificationPermission, + granted ? ProfileAdapter::AllowedPermission : ProfileAdapter::DeniedPermission); break; default: Q_UNREACHABLE(); diff --git a/src/webengine/doc/src/qtwebengine-debugging.qdoc b/src/webengine/doc/src/qtwebengine-debugging.qdoc index 088db5f87..0db35c85c 100644 --- a/src/webengine/doc/src/qtwebengine-debugging.qdoc +++ b/src/webengine/doc/src/qtwebengine-debugging.qdoc @@ -97,6 +97,11 @@ \li \c {--single-process} runs the renderer and plugins in the same process as the browser. This is useful for getting stack traces for renderer crashes. + \li \c {--enable-features=NetworkServiceInProcess} runs networking in + the main process. This may help firewall management, since only the + application executable will need to be whitelisted and + not QtWebEngineProcess. It means losing the security of + sandboxing of the network service though. \endlist Alternatively, the environment variable QTWEBENGINE_CHROMIUM_FLAGS can be @@ -106,4 +111,7 @@ \code QTWEBENGINE_CHROMIUM_FLAGS="--disable-logging" mybrowser \endcode + + QTWEBENGINE_CHROMIUM_FLAGS can also be set using {qputenv} from within the + application if called before QtWebEngine::initialize(). */ diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp index 7045fd856..6fb3c5c43 100644 --- a/src/webenginewidgets/api/qwebenginepage.cpp +++ b/src/webenginewidgets/api/qwebenginepage.cpp @@ -1910,8 +1910,24 @@ QMenu *QWebEnginePage::createStandardContextMenu() void QWebEnginePage::setFeaturePermission(const QUrl &securityOrigin, QWebEnginePage::Feature feature, QWebEnginePage::PermissionPolicy policy) { Q_D(QWebEnginePage); - if (policy == PermissionUnknown) + if (policy == PermissionUnknown) { + switch (feature) { + case MediaAudioVideoCapture: + case MediaAudioCapture: + case MediaVideoCapture: + case DesktopAudioVideoCapture: + case DesktopVideoCapture: + case MouseLock: + break; + case Geolocation: + d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::GeolocationPermission, ProfileAdapter::AskPermission); + break; + case Notifications: + d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::NotificationPermission, ProfileAdapter::AskPermission); + break; + } return; + } const WebContentsAdapterClient::MediaRequestFlags audioVideoCaptureFlags( WebContentsAdapterClient::MediaVideoCapture | @@ -1937,14 +1953,14 @@ void QWebEnginePage::setFeaturePermission(const QUrl &securityOrigin, QWebEngine case DesktopVideoCapture: d->adapter->grantMediaAccessPermission(securityOrigin, WebContentsAdapterClient::MediaDesktopVideoCapture); break; - case Geolocation: - d->adapter->runFeatureRequestCallback(securityOrigin, ProfileAdapter::GeolocationPermission, true); - break; case MouseLock: d->adapter->grantMouseLockPermission(securityOrigin, true); break; + case Geolocation: + d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::GeolocationPermission, ProfileAdapter::AllowedPermission); + break; case Notifications: - d->adapter->runFeatureRequestCallback(securityOrigin, ProfileAdapter::NotificationPermission, true); + d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::NotificationPermission, ProfileAdapter::AllowedPermission); break; } } else { // if (policy == PermissionDeniedByUser) @@ -1957,13 +1973,13 @@ void QWebEnginePage::setFeaturePermission(const QUrl &securityOrigin, QWebEngine d->adapter->grantMediaAccessPermission(securityOrigin, WebContentsAdapterClient::MediaNone); break; case Geolocation: - d->adapter->runFeatureRequestCallback(securityOrigin, ProfileAdapter::GeolocationPermission, false); + d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::GeolocationPermission, ProfileAdapter::DeniedPermission); break; case MouseLock: d->adapter->grantMouseLockPermission(securityOrigin, false); break; case Notifications: - d->adapter->runFeatureRequestCallback(securityOrigin, ProfileAdapter::NotificationPermission, false); + d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::NotificationPermission, ProfileAdapter::DeniedPermission); break; } } diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/resources/sw.html b/tests/auto/core/qwebengineurlrequestinterceptor/resources/sw.html new file mode 100644 index 000000000..af44b45a2 --- /dev/null +++ b/tests/auto/core/qwebengineurlrequestinterceptor/resources/sw.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<html> + <body> + <script> + if ('serviceWorker' in navigator) { + window.addEventListener('load', function() { + navigator.serviceWorker.register('/sw.js').then(function(registration) { + console.log('ServiceWorker registration successful with scope: ', registration.scope); + }, function(err) { + console.error('ServiceWorker registration failed: ', err); + }); + }); + } + </script> + </body> +</html> diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/resources/sw.js b/tests/auto/core/qwebengineurlrequestinterceptor/resources/sw.js new file mode 100644 index 000000000..2216e2a07 --- /dev/null +++ b/tests/auto/core/qwebengineurlrequestinterceptor/resources/sw.js @@ -0,0 +1,3 @@ +self.addEventListener('install', function(event) { + console.log('ServiceWorker installed'); +}); diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp index b8ecce635..350c15174 100644 --- a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp +++ b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp @@ -74,6 +74,8 @@ private Q_SLOTS: void passRefererHeader(); void initiator_data(); void initiator(); + void jsServiceWorker_data(); + void jsServiceWorker(); }; tst_QWebEngineUrlRequestInterceptor::tst_QWebEngineUrlRequestInterceptor() @@ -186,6 +188,58 @@ public: } }; +class TestServer : public HttpServer +{ +public: + TestServer() + { + connect(this, &HttpServer::newRequest, this, &TestServer::onNewRequest); + } + +private: + void onNewRequest(HttpReqRep *rr) + { + const QDir resourceDir(TESTS_SOURCE_DIR "qwebengineurlrequestinterceptor/resources"); + QString path = rr->requestPath(); + path.remove(0, 1); + + if (rr->requestMethod() != "GET" || !resourceDir.exists(path)) + { + rr->setResponseStatus(404); + rr->sendResponse(); + return; + } + + QFile file(resourceDir.filePath(path)); + file.open(QIODevice::ReadOnly); + QByteArray data = file.readAll(); + rr->setResponseBody(data); + QMimeDatabase db; + QMimeType mime = db.mimeTypeForFileNameAndData(file.fileName(), data); + rr->setResponseHeader(QByteArrayLiteral("content-type"), mime.name().toUtf8()); + rr->sendResponse(); + } +}; + +class ConsolePage : public QWebEnginePage { + Q_OBJECT +public: + ConsolePage(QWebEngineProfile* profile) : QWebEnginePage(profile) {} + + virtual void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString& message, int lineNumber, const QString& sourceID) + { + levels.append(level); + messages.append(message); + lineNumbers.append(lineNumber); + sourceIDs.append(sourceID); + } + + QList<int> levels; + QStringList messages; + QList<int> lineNumbers; + QStringList sourceIDs; +}; + void tst_QWebEngineUrlRequestInterceptor::interceptRequest_data() { QTest::addColumn<InterceptorSetter>("setter"); @@ -699,5 +753,39 @@ void tst_QWebEngineUrlRequestInterceptor::initiator() QVERIFY(interceptor.requestInitiatorUrls[info.requestUrl].contains(info.initiator)); } +void tst_QWebEngineUrlRequestInterceptor::jsServiceWorker_data() +{ + interceptRequest_data(); +} + +void tst_QWebEngineUrlRequestInterceptor::jsServiceWorker() +{ + QFETCH(InterceptorSetter, setter); + + TestServer server; + QVERIFY(server.start()); + + QWebEngineProfile profile(QStringLiteral("Test")); + std::unique_ptr<ConsolePage> page; + page.reset(new ConsolePage(&profile)); + TestRequestInterceptor interceptor(/* intercept */ false); + (profile.*setter)(&interceptor); + QVERIFY(loadSync(page.get(), server.url("/sw.html"))); + + // We expect only one message here, because logging of services workers is not exposed in our API. + QTRY_COMPARE(page->messages.count(), 1); + QCOMPARE(page->levels.at(0), QWebEnginePage::InfoMessageLevel); + + QUrl firstPartyUrl = QUrl(server.url().toString() + "sw.js"); + QList<RequestInfo> infos; + // Service Worker + QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeServiceWorker)); + infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeServiceWorker); + foreach (auto info, infos) + QCOMPARE(info.firstPartyUrl, firstPartyUrl); + + QVERIFY(server.stop()); +} + QTEST_MAIN(tst_QWebEngineUrlRequestInterceptor) #include "tst_qwebengineurlrequestinterceptor.moc" diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.qrc b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.qrc index 13dbb134e..6a34635f7 100644 --- a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.qrc +++ b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.qrc @@ -17,6 +17,8 @@ <file>resources/resource_in_iframe.html</file> <file>resources/script.js</file> <file>resources/style.css</file> + <file>resources/sw.html</file> + <file>resources/sw.js</file> <file>resources/icons/favicon.png</file> </qresource> </RCC> diff --git a/tests/auto/quick/qmltests/data/TestWebEngineView.qml b/tests/auto/quick/qmltests/data/TestWebEngineView.qml index f5e83c5d2..6db076ae8 100644 --- a/tests/auto/quick/qmltests/data/TestWebEngineView.qml +++ b/tests/auto/quick/qmltests/data/TestWebEngineView.qml @@ -111,5 +111,12 @@ WebEngineView { onWindowCloseRequested: { windowCloseRequestedSignalEmitted = true; } + + function getBodyText() { + let text + runJavaScript('document.body.innerText', function(t) { text = t }) + testCase.tryVerify(function() { return text !== undefined }) + return text + } } diff --git a/tests/auto/quick/qmltests/data/tst_certificateError.qml b/tests/auto/quick/qmltests/data/tst_certificateError.qml new file mode 100644 index 000000000..0629be175 --- /dev/null +++ b/tests/auto/quick/qmltests/data/tst_certificateError.qml @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** 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:GPL-EXCEPT$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtTest 1.0 +import QtWebEngine 1.9 + +import Test.Shared 1.0 as Shared + +TestWebEngineView { + id: view; width: 320; height: 320 + + property bool deferError: false + property bool acceptCertificate: false + + onCertificateError: function(error) { + if (deferError) + error.defer() + else if (acceptCertificate) + error.ignoreCertificateError() + else + error.rejectCertificate() + } + + SignalSpy { + id: spyError + target: view + signalName: 'certificateError' + } + + TestCase { + name: 'CertificateError' + when: windowShown + + function initTestCase() { + Shared.HttpsServer.setExpectError(true) + Shared.HttpsServer.newRequest.connect(function (request) { + request.setResponseBody('<html><body>Test</body></html>') + request.sendResponse() + }) + view.settings.errorPageEnabled = false + } + + function init() { + verify(Shared.HttpsServer.start()) + } + + function cleanup() { + Shared.HttpsServer.stop() + view.deferError = false + view.acceptCertificate = false + spyError.clear() + } + + function test_error_data() { + return [ + { tag: 'reject', deferError: false, acceptCertificate: false, expectedContent: '' }, + { tag: 'defer_reject', deferError: true, acceptCertificate: false, expectedContent: '' }, + { tag: 'defer_accept', deferError: true, acceptCertificate: true, expectedContent: 'Test' }, + ] + } + + function test_error(data) { + view.deferError = data.deferError + view.acceptCertificate = data.acceptCertificate + view.url = Shared.HttpsServer.url() + + if (data.deferError) { + spyError.wait() + compare(spyError.count, 1) + compare('', view.getBodyText()) + + let error = spyError.signalArguments[0][0] + if (data.acceptCertificate) + error.ignoreCertificateError() + else + error.rejectCertificate() + } + + if (data.acceptCertificate) + verify(view.waitForLoadSucceeded()) + else + verify(view.waitForLoadFailed()) + + compare(spyError.count, 1) + compare(data.expectedContent, view.getBodyText()) + } + } +} diff --git a/tests/auto/quick/qmltests/qmltests.pro b/tests/auto/quick/qmltests/qmltests.pro index 5c57f7ad9..6bec6dc0d 100644 --- a/tests/auto/quick/qmltests/qmltests.pro +++ b/tests/auto/quick/qmltests/qmltests.pro @@ -1,4 +1,5 @@ include(../tests.pri) +include(../../shared/https.pri) QT += qmltest diff --git a/tests/auto/quick/qmltests/tst_qmltests.cpp b/tests/auto/quick/qmltests/tst_qmltests.cpp index 9e8d25222..0d830931d 100644 --- a/tests/auto/quick/qmltests/tst_qmltests.cpp +++ b/tests/auto/quick/qmltests/tst_qmltests.cpp @@ -26,6 +26,8 @@ ** ****************************************************************************/ +#include <httpsserver.h> + #include <QtCore/QScopedPointer> #include <QTemporaryDir> #include <QtQuickTest/quicktest.h> @@ -144,6 +146,8 @@ int main(int argc, char **argv) QTEST_SET_MAIN_SOURCE_PATH + qmlRegisterSingletonType<HttpsServer>("Test.Shared", 1, 0, "HttpsServer", [&] (QQmlEngine *, QJSEngine *) { return new HttpsServer; }); + int i = quick_test_main(argc, argv, "qmltests", QUICK_TEST_SOURCE_DIR); return i; } diff --git a/tests/auto/quick/qmltests2/data/tst_filePicker.qml b/tests/auto/quick/qmltests2/data/tst_filePicker.qml index ffd7ef87b..ab30d9e82 100644 --- a/tests/auto/quick/qmltests2/data/tst_filePicker.qml +++ b/tests/auto/quick/qmltests2/data/tst_filePicker.qml @@ -36,6 +36,7 @@ TestWebEngineView { id: webEngineView width: 400 height: 300 + property var titleChanges: [] function driveLetter() { if (Qt.platform.os !== "windows") @@ -54,6 +55,8 @@ TestWebEngineView { signalName: "renderProcessTerminated" } + onTitleChanged: { titleChanges.push(webEngineView.title) } + TestCase { name: "WebEngineViewSingleFileUpload" when: windowShown @@ -65,6 +68,7 @@ TestWebEngineView { FilePickerParams.nameFilters = [] titleSpy.clear() terminationSpy.clear() + titleChanges = [] } function cleanup() { @@ -80,14 +84,14 @@ TestWebEngineView { function test_acceptSingleFileSelection_data() { return [ + { tag: "test.txt", input: "test.txt", expected: "Failed to Upload" }, { tag: driveLetter() + "/test.txt", input: driveLetter() + "/test.txt", expected: "test.txt" }, - { tag: driveLetter() + "test.txt", input: driveLetter() + "test.txt", expected: "Failed to Upload" }, { tag: driveLetter() + "/tést.txt", input: driveLetter() + "/tést.txt", expected: "tést.txt" }, { tag: driveLetter() + "/t%65st.txt", input: driveLetter() + "/t%65st.txt", expected: "t%65st.txt" }, { tag: "file:///" + driveLetter() + "test.txt", input: "file:///" + driveLetter() + "test.txt", expected: "test.txt" }, { tag: "file:///" + driveLetter() + "tést.txt", input: "file:///" + driveLetter() + "tést.txt", expected: "tést.txt" }, - { tag: "file:///" + driveLetter() + "t%65st.txt", input: "file:///" + driveLetter() + "t%65st.txt", expected: "test.txt" }, - { tag: "file://" + driveLetter() + "test.txt", input: "file://" + driveLetter() + "test.txt", expected: "test.txt" }, + { tag: "file:///" + driveLetter() + "t%65st.txt", input: "file:///" + driveLetter() + "t%65st.txt", expected: "t%65st.txt" }, + { tag: "file://" + driveLetter() + "test.txt", input: "file://" + driveLetter() + "test.txt", expected: "Failed to Upload" }, { tag: "file:/" + driveLetter() + "test.txt", input: "file:/" + driveLetter() + "test.txt", expected: "test.txt"}, { tag: "file:test//test.txt", input: "file:test//test.txt", expected: "Failed to Upload" }, { tag: "http://test.txt", input: "http://test.txt", expected: "Failed to Upload" }, @@ -107,7 +111,10 @@ TestWebEngineView { keyClick(Qt.Key_Enter); // Focus is on the button. Open FileDialog. tryCompare(FilePickerParams, "filePickerOpened", true); - tryCompare(webEngineView, "title", row.expected); + webEngineView.url = Qt.resolvedUrl("about:blank"); + verify(webEngineView.waitForLoadSucceeded()); + tryCompare(webEngineView, "title", "about:blank"); + compare(titleChanges[titleChanges.length-2], row.expected); // Custom dialog @@ -125,7 +132,10 @@ TestWebEngineView { keyClick(Qt.Key_Enter); // Focus is on the button. Open FileDialog. tryVerify(function() { return finished; }); - tryCompare(webEngineView, "title", row.expected); + webEngineView.url = Qt.resolvedUrl("about:blank"); + verify(webEngineView.waitForLoadSucceeded()); + tryCompare(webEngineView, "title", "about:blank"); + compare(titleChanges[titleChanges.length-2], row.expected); webEngineView.fileDialogRequested.disconnect(acceptedFileHandler); } @@ -212,19 +222,19 @@ TestWebEngineView { { tag: "C:/test.txt", input: "C:/test.txt", expected: "test.txt"}, { tag: "C:\\test.txt", input: "C:\\test.txt", expected: "test.txt"}, { tag: "C:\\Documents and Settings\\test\\test.txt", input: "C:\\Documents and Settings\\test\\test.txt", expected: "test.txt"}, - { tag: "\\\\applib\\products\\a%2Db\\ abc%5F9\\t.est\\test.txt", input: "file://applib/products/a%2Db/ abc%5F9/4148.920a/media/test.txt", expected: "test.txt"}, - { tag: "file://applib/products/a%2Db/ abc%5F9/t.est/test.txt", input: "file://applib/products/a%2Db/ abc%5F9/4148.920a/media/test.txt", expected: "test.txt"}, + { tag: "\\\\applib\\products\\a%2Db\\ abc%5F9\\t.est\\test.txt", input: "\\\\applib\\products\\a%2Db\\ abc%5F9\\t.est\\test.txt", expected: "test.txt"}, + { tag: "file://applib/products/a%2Db/ abc%5F9/4148.920a/media/test.txt", input: "file://applib/products/a%2Db/ abc%5F9/4148.920a/media/test.txt", expected: "test.txt"}, { tag: "file://applib/products/a-b/abc_1/t.est/test.txt", input: "file://applib/products/a-b/abc_1/t.est/test.txt", expected: "test.txt"}, { tag: "file:\\\\applib\\products\\a-b\\abc_1\\t:est\\test.txt", input: "file:\\\\applib\\products\\a-b\\abc_1\\t:est\\test.txt", expected: "test.txt"}, - { tag: "file:C:/test.txt", input: "file:C:/test.txt", expected: "Failed to Upload"}, - { tag: "file:/C:/test.txt", input: "file:/C:/test.txt", expected: "Failed to Upload"}, + { tag: "file:C:/test.txt", input: "file:C:/test.txt", expected: "test.tx"}, + { tag: "file:/C:/test.txt", input: "file:/C:/test.txt", expected: "test.tx"}, { tag: "file://C:/test.txt", input: "file://C:/test.txt", expected: "Failed to Upload"}, { tag: "file:///C:test.txt", input: "file:///C:test.txt", expected: "Failed to Upload"}, { tag: "file:///C:/test.txt", input: "file:///C:/test.txt", expected: "test.txt"}, { tag: "file:///C:\\test.txt", input: "file:///C:\\test.txt", expected: "test.txt"}, { tag: "file:\\//C:/test.txt", input: "file:\\//C:/test.txt", expected: "test.txt"}, { tag: "file:\\\\/C:\\test.txt", input: "file:\\\\/C:\\test.txt", expected: "test.txt"}, - { tag: "\\\\?\\C:/test.txt", input: "\\\\?\\C:/test.txt", expected: "Failed to Upload"}, + { tag: "\\\\?\\C:/test.txt", input: "\\\\?\\C:/test.txt", expected: "test.tx"}, ]; } @@ -241,7 +251,10 @@ TestWebEngineView { keyClick(Qt.Key_Enter); // Focus is on the button. Open FileDialog. tryCompare(FilePickerParams, "filePickerOpened", true); - tryCompare(webEngineView, "title", row.expected); + webEngineView.url = Qt.resolvedUrl("about:blank"); + verify(webEngineView.waitForLoadSucceeded()); + tryCompare(webEngineView, "title", "about:blank"); + compare(titleChanges[titleChanges.length-2], row.expected); // Custom dialog @@ -259,7 +272,10 @@ TestWebEngineView { keyClick(Qt.Key_Enter); // Focus is on the button. Open FileDialog. tryVerify(function() { return finished; }); - tryCompare(webEngineView, "title", row.expected); + webEngineView.url = Qt.resolvedUrl("about:blank"); + verify(webEngineView.waitForLoadSucceeded()); + tryCompare(webEngineView, "title", "about:blank"); + compare(titleChanges[titleChanges.length-2], row.expected); webEngineView.fileDialogRequested.disconnect(acceptedFileHandler); } diff --git a/tests/auto/shared/httpreqrep.h b/tests/auto/shared/httpreqrep.h index bee8119eb..1666b17d0 100644 --- a/tests/auto/shared/httpreqrep.h +++ b/tests/auto/shared/httpreqrep.h @@ -40,7 +40,7 @@ class HttpReqRep : public QObject public: explicit HttpReqRep(QTcpSocket *socket, QObject *parent = nullptr); - void sendResponse(); + Q_INVOKABLE void sendResponse(); void close(); // Request parameters (only valid after requestReceived()) @@ -61,7 +61,7 @@ public: m_responseHeaders[key.toLower()] = std::move(value); } QByteArray responseBody() const { return m_responseBody; } - void setResponseBody(QByteArray content) + Q_INVOKABLE void setResponseBody(QByteArray content) { m_responseHeaders["content-length"] = QByteArray::number(content.size()); m_responseBody = std::move(content); diff --git a/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp b/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp index 55d8ac6e8..39948c211 100644 --- a/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp +++ b/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp @@ -1016,8 +1016,10 @@ void tst_QWebEngineDownloadItem::downloadUniqueFilenameWithTimestamp() QRegularExpressionMatch match = fileNameCheck.match(downloadedFilePath); QVERIFY(match.hasMatch()); // ISO 8601 Date and time in UTC - QRegExp timestamp("^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9])([0-5][0-9])([0-5][0-9])([.][0-9]+)?(Z|[+-](?:2[0-3]|[01][0-9])[0-5][0-9])?$"); - QVERIFY(timestamp.exactMatch(match.captured(1))); + QRegularExpression timestamp("^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[" + "12][0-9])T(2[0-3]|[01][0-9])([0-5][0-9])([0-5][0-9])([.][0-9]" + "+)?(Z|[+-](?:2[0-3]|[01][0-9])[0-5][0-9])?$"); + QVERIFY(timestamp.match(match.captured(1)).hasMatch()); QCOMPARE(suggestedFileName, fileName); } } diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp index 190758ed2..7bd540cc6 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -200,11 +200,10 @@ private Q_SLOTS: void triggerActionWithoutMenu(); void dynamicFrame(); - void notificationRequest_data(); - void notificationRequest(); + void notificationPermission_data(); + void notificationPermission(); void sendNotification(); void contentsSize(); - void notificationPermission(); void setLifecycleState(); void setVisible(); @@ -229,6 +228,7 @@ private Q_SLOTS: void renderProcessPid(); void backgroundColor(); void audioMuted(); + void closeContents(); private: static QPoint elementCenter(QWebEnginePage *page, const QString &id); @@ -3575,42 +3575,61 @@ public: } }; -void tst_QWebEnginePage::notificationRequest_data() +void tst_QWebEnginePage::notificationPermission_data() { + QTest::addColumn<bool>("setOnInit"); QTest::addColumn<QWebEnginePage::PermissionPolicy>("policy"); QTest::addColumn<QString>("permission"); - QTest::newRow("deny") << QWebEnginePage::PermissionDeniedByUser << "denied"; - QTest::newRow("grant") << QWebEnginePage::PermissionGrantedByUser << "granted"; + QTest::newRow("denyOnInit") << true << QWebEnginePage::PermissionDeniedByUser << "denied"; + QTest::newRow("deny") << false << QWebEnginePage::PermissionDeniedByUser << "denied"; + QTest::newRow("grant") << false << QWebEnginePage::PermissionGrantedByUser << "granted"; + QTest::newRow("grantOnInit") << true << QWebEnginePage::PermissionGrantedByUser << "granted"; } -void tst_QWebEnginePage::notificationRequest() +void tst_QWebEnginePage::notificationPermission() { + QFETCH(bool, setOnInit); QFETCH(QWebEnginePage::PermissionPolicy, policy); QFETCH(QString, permission); - NotificationPage page(policy); - QVERIFY(page.spyLoad.waitForResult()); + QWebEngineProfile otr; + QWebEnginePage page(&otr, nullptr); - page.resetPermission(); - QCOMPARE(page.getPermission(), "default"); + QUrl baseUrl("https://www.example.com/somepage.html"); - page.requestPermission(); - page.spyRequest.waitForResult(); - QVERIFY(page.spyRequest.wasCalled()); + bool permissionRequested = false, errorState = false; + connect(&page, &QWebEnginePage::featurePermissionRequested, &page, [&] (const QUrl &o, QWebEnginePage::Feature f) { + if (f != QWebEnginePage::Notifications) + return; + if (permissionRequested || o != baseUrl.url(QUrl::RemoveFilename)) { + qWarning() << "Unexpected case. Can't proceed." << setOnInit << permissionRequested << o; + errorState = true; + return; + } + permissionRequested = true; + page.setFeaturePermission(o, f, policy); + }); - QCOMPARE(page.getPermission(), permission); -} + if (setOnInit) + page.setFeaturePermission(baseUrl, QWebEnginePage::Notifications, policy); -void tst_QWebEnginePage::notificationPermission() -{ - QWebEngineProfile otr; - QWebEnginePage page(&otr, nullptr); QSignalSpy spy(&page, &QWebEnginePage::loadFinished); - page.setHtml(QString("<html><body>Test</body></html>"), QUrl("https://www.example.com")); + page.setHtml(QString("<html><body>Test</body></html>"), baseUrl); QTRY_COMPARE(spy.count(), 1); - QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("Notification.permission")), QLatin1String("default")); - page.setFeaturePermission(QUrl("https://www.example.com"), QWebEnginePage::Notifications, QWebEnginePage::PermissionGrantedByUser); - QTRY_COMPARE(evaluateJavaScriptSync(&page, QStringLiteral("Notification.permission")), QLatin1String("granted")); + + QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("Notification.permission")), setOnInit ? permission : QLatin1String("default")); + + if (!setOnInit) { + page.setFeaturePermission(baseUrl, QWebEnginePage::Notifications, policy); + QTRY_COMPARE(evaluateJavaScriptSync(&page, QStringLiteral("Notification.permission")), permission); + } + + auto js = QStringLiteral("var permission; Notification.requestPermission().then(p => { permission = p })"); + evaluateJavaScriptSync(&page, js); + QTRY_COMPARE(evaluateJavaScriptSync(&page, "permission").toString(), permission); + // permission is not 'remembered' from api standpoint, hence is not suppressed on explicit call from JS + QVERIFY(permissionRequested); + QVERIFY(!errorState); } void tst_QWebEnginePage::sendNotification() @@ -4608,6 +4627,27 @@ void tst_QWebEnginePage::audioMuted() QCOMPARE(spy[1][0], QVariant(false)); } +void tst_QWebEnginePage::closeContents() +{ + TestPage page; + QSignalSpy windowCreatedSpy(&page, &TestPage::windowCreated); + page.runJavaScript("var dialog = window.open('', '', 'width=100, height=100');"); + QTRY_COMPARE(windowCreatedSpy.count(), 1); + + QWebEngineView *dialogView = new QWebEngineView; + QWebEnginePage *dialogPage = page.createdWindows[0]; + dialogPage->setView(dialogView); + QCOMPARE(dialogPage->lifecycleState(), QWebEnginePage::LifecycleState::Active); + + // This should not crash. + connect(dialogPage, &QWebEnginePage::windowCloseRequested, dialogView, &QWebEngineView::close); + page.runJavaScript("dialog.close();"); + + // QWebEngineView::closeEvent() sets the life cycle state to discarded. + QTRY_COMPARE(dialogPage->lifecycleState(), QWebEnginePage::LifecycleState::Discarded); + delete dialogView; +} + static QByteArrayList params = {QByteArrayLiteral("--use-fake-device-for-media-stream")}; W_QTEST_MAIN(tst_QWebEnginePage, params) diff --git a/tests/manual/quick/faviconbrowser/faviconbrowser.qrc b/tests/manual/quick/faviconbrowser/faviconbrowser.qrc index 95d0dc2c4..b641fa2fa 100644 --- a/tests/manual/quick/faviconbrowser/faviconbrowser.qrc +++ b/tests/manual/quick/faviconbrowser/faviconbrowser.qrc @@ -5,13 +5,13 @@ <file>FaviconPanel.qml</file> </qresource> <qresource prefix="test"> - <file alias="favicon-multi-gray.html">../../../auto/quick/qmltests/data/favicon-multi-gray.html</file> - <file alias="favicon-candidates-gray.html">../../../auto/quick/qmltests/data/favicon-candidates-gray.html</file> - <file alias="icons/grayicons.ico">../../../auto/quick/qmltests/data/icons/grayicons.ico</file> - <file alias="icons/gray16.png">../../../auto/quick/qmltests/data/icons/gray16.png</file> - <file alias="icons/gray32.png">../../../auto/quick/qmltests/data/icons/gray32.png</file> - <file alias="icons/gray64.png">../../../auto/quick/qmltests/data/icons/gray64.png</file> - <file alias="icons/gray128.png">../../../auto/quick/qmltests/data/icons/gray128.png</file> - <file alias="icons/gray255.png">../../../auto/quick/qmltests/data/icons/gray255.png</file> + <file alias="favicon-multi-gray.html">../../../auto/quick/qmltests2/data/favicon-multi-gray.html</file> + <file alias="favicon-candidates-gray.html">../../../auto/quick/qmltests2/data/favicon-candidates-gray.html</file> + <file alias="icons/grayicons.ico">../../../auto/quick/qmltests2/data/icons/grayicons.ico</file> + <file alias="icons/gray16.png">../../../auto/quick/qmltests2/data/icons/gray16.png</file> + <file alias="icons/gray32.png">../../../auto/quick/qmltests2/data/icons/gray32.png</file> + <file alias="icons/gray64.png">../../../auto/quick/qmltests2/data/icons/gray64.png</file> + <file alias="icons/gray128.png">../../../auto/quick/qmltests2/data/icons/gray128.png</file> + <file alias="icons/gray255.png">../../../auto/quick/qmltests2/data/icons/gray255.png</file> </qresource> </RCC> |