// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "content_client_qt.h" #include "compositor/compositor.h" #include "base/command_line.h" #include "base/files/file_util.h" #include "base/json/json_string_value_serializer.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/values.h" #include "base/version.h" #include "content/public/common/cdm_info.h" #include "content/public/common/content_constants.h" #include "content/public/common/content_switches.h" #include "extensions/buildflags/buildflags.h" #include "extensions/common/constants.h" #include "media/base/media_switches.h" #include "media/base/video_codecs.h" #include "media/cdm/supported_audio_codecs.h" #include "media/media_buildflags.h" #include "ui/base/layout.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "type_conversion.h" #include #include #include #include #include #include #if BUILDFLAG(IS_WIN) #include "ui/gl/gl_utils.h" #endif #if BUILDFLAG(ENABLE_LIBRARY_CDMS) #include "media/cdm/cdm_paths.h" // nogncheck #include "media/cdm/clear_key_cdm_common.h" #include "third_party/widevine/cdm/buildflags.h" #include "third_party/widevine/cdm/widevine_cdm_common.h" #if BUILDFLAG(ENABLE_WIDEVINE) && !BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT) #define WIDEVINE_CDM_AVAILABLE_NOT_COMPONENT // File name of the CDM on different platforms. const char kWidevineCdmFileName[] = #if BUILDFLAG(IS_MAC) "widevinecdm.plugin"; #elif BUILDFLAG(IS_WIN) "widevinecdm.dll"; #else // OS_LINUX, etc. "libwidevinecdm.so"; #endif #endif #endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) #if QT_CONFIG(webengine_printing_and_pdf) #include "components/pdf/common/constants.h" #include "pdf/pdf.h" const char kPdfPluginPath[] = "internal-pdf-viewer"; #endif // QT_CONFIG(webengine_printing_and_pdf) using namespace Qt::StringLiterals; using Robustness = content::CdmInfo::Robustness; static QString webenginePluginsPath() { // Look for plugins in /plugins/webengine or application dir. static bool initialized = false; static QString potentialPluginsPath = QLibraryInfo::path(QLibraryInfo::PluginsPath) % "/webengine"_L1; if (!initialized) { initialized = true; if (!QFileInfo::exists(potentialPluginsPath)) potentialPluginsPath = QCoreApplication::applicationDirPath(); } return potentialPluginsPath; } #if defined(Q_OS_WIN) #include static QString getLocalAppDataDir() { QString result; PWSTR path; HRESULT hr = SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &path); if (SUCCEEDED(hr)) result = QDir::fromNativeSeparators(QString::fromWCharArray(path)); CoTaskMemFree(path); return result; } static QString getProgramFilesDir(bool x86Dir = false) { QString result; PWSTR path; HRESULT hr = SHGetKnownFolderPath(x86Dir ? FOLDERID_ProgramFilesX86 : FOLDERID_ProgramFilesX64, 0, NULL, &path); if (SUCCEEDED(hr)) result = QDir::fromNativeSeparators(QString::fromWCharArray(path)); CoTaskMemFree(path); return result; } static QString getSystem32Dir() { QString result; PWSTR path; HRESULT hr = SHGetKnownFolderPath(FOLDERID_System, 0, NULL, &path); if (SUCCEEDED(hr)) result = QDir::fromNativeSeparators(QString::fromWCharArray(path)); CoTaskMemFree(path); return result; } #endif #if QT_CONFIG(webengine_pepper_plugins) // The plugin logic is based on chrome/common/chrome_content_client.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. #include "content/public/common/content_plugin_info.h" #include "ppapi/shared_impl/ppapi_permissions.h" static QString ppapiPluginsPath() { // Look for plugins in /plugins/ppapi or application dir. static bool initialized = false; static QString potentialPluginsPath = QLibraryInfo::path(QLibraryInfo::PluginsPath) % "/ppapi"_L1; if (!initialized) { initialized = true; if (!QFileInfo::exists(potentialPluginsPath)) potentialPluginsPath = QCoreApplication::applicationDirPath(); } return potentialPluginsPath; } void ComputeBuiltInPlugins(std::vector *plugins) { #if QT_CONFIG(webengine_printing_and_pdf) static constexpr char kPDFPluginExtension[] = "pdf"; static constexpr char kPDFPluginDescription[] = "Portable Document Format"; content::ContentPluginInfo pdf_info; pdf_info.is_internal = true; pdf_info.is_out_of_process = true; pdf_info.name = "Chromium PDF Viewer"; pdf_info.description = kPDFPluginDescription; pdf_info.path = base::FilePath::FromUTF8Unsafe(kPdfPluginPath); content::WebPluginMimeType pdf_mime_type( pdf::kInternalPluginMimeType, kPDFPluginExtension, kPDFPluginDescription); pdf_info.mime_types.push_back(pdf_mime_type); plugins->push_back(pdf_info); #endif // QT_CONFIG(webengine_printing_and_pdf) } namespace QtWebEngineCore { void ContentClientQt::AddPlugins(std::vector *plugins) { ComputeBuiltInPlugins(plugins); } } // namespace QtWebEngineCore #endif // QT_CONFIG(webengine_pepper_plugins) namespace QtWebEngineCore { #if defined(WIDEVINE_CDM_AVAILABLE_NOT_COMPONENT) #if defined(Q_OS_LINUX) static const QDir widevineCdmDirHint(const QDir &widevineDir) { const QString hintFilePath = widevineDir.absolutePath() % QDir::separator() % "latest-component-updated-widevine-cdm"_L1; if (!QFileInfo::exists(hintFilePath)) { // CDM hint file does not exist. return widevineDir; } std::string jsonString; if (!base::ReadFileToString(toFilePath(hintFilePath), &jsonString)) { // Could not read the CDM hint file. return widevineDir; } std::string error_message; JSONStringValueDeserializer deserializer(jsonString); std::unique_ptr dict = deserializer.Deserialize(nullptr, &error_message); if (!dict || !dict->is_dict()) { DLOG(ERROR) << "Could not deserialize the CDM hint file. Error: " << error_message; // Could not deserialize the CDM hint file. return widevineDir; } std::string *widevineCdmDirPath = dict->GetDict().FindString("Path"); if (!widevineCdmDirPath) return widevineDir; return QDir(QString::fromStdString(*widevineCdmDirPath)); } #endif // defined(Q_OS_LINUX) static bool IsWidevineAvailable(base::FilePath *cdm_path, media::CdmCapability *capability) { QStringList pluginPaths; const base::CommandLine::StringType widevine_argument = base::CommandLine::ForCurrentProcess()->GetSwitchValueNative(switches::kCdmWidevinePath); if (!widevine_argument.empty()) pluginPaths << QtWebEngineCore::toQt(widevine_argument); else { pluginPaths << webenginePluginsPath() + u'/' + QLatin1StringView(kWidevineCdmFileName); #if QT_CONFIG(webengine_pepper_plugins) pluginPaths << ppapiPluginsPath() + u'/' + QLatin1StringView(kWidevineCdmFileName); #endif #if defined(Q_OS_MACOS) QDir potentialWidevineDir(u"/Applications/Google Chrome.app/Contents/Frameworks"_s); const auto archDir = QSysInfo::currentCpuArchitecture() == "x86_64"_L1 ? "mac_x64/"_L1 : "mac_arm64/"_L1; if (potentialWidevineDir.exists()) { QFileInfoList widevineVersionDirs = potentialWidevineDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name | QDir::Reversed); const auto libraryBase = "/Versions/Current/Libraries/WidevineCdm/_platform_specific/"_L1; const auto libraryFilename = "libwidevinecdm.dylib"_L1; for (const QFileInfo &info : widevineVersionDirs) pluginPaths << info.absoluteFilePath() + libraryBase + archDir + libraryFilename; } QDir oldPotentialWidevineDir(QDir::homePath() + "/Library/Application Support/Google/Chrome/WidevineCDM"_L1); if (oldPotentialWidevineDir.exists()) { QFileInfoList widevineVersionDirs = oldPotentialWidevineDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name | QDir::Reversed); for (int i = 0; i < widevineVersionDirs.size(); ++i) { const QString versionDirPath = widevineVersionDirs.at(i).absoluteFilePath(); QString potentialWidevinePluginPath = versionDirPath + "/_platform_specific/"_L1 + archDir + QLatin1StringView(kWidevineCdmFileName); pluginPaths.append(std::move(potentialWidevinePluginPath)); } } #elif defined(Q_OS_WIN) const auto arch = QSysInfo::currentCpuArchitecture(); auto appendArchitectureAndFilename = [&arch](QString &inString) { if (arch == "x86_64"_L1) { inString += "/win_x64/"_L1; } else if (arch == "i386"_L1) { inString += "/win_x86/"_L1; } else if (arch == "arm64"_L1) { inString += "/win_arm64/"_L1; } else { Q_UNREACHABLE(); } inString += QLatin1StringView(kWidevineCdmFileName); }; // Look inside Program Files; first for Microsoft Edge, then for Google Chrome const auto microsoftEdgeDir = "/Microsoft/Edge/Application"_L1; const auto googleChromeDir = "/Google/Chrome/Application"_L1; const QStringList programFileDirs { getProgramFilesDir() + microsoftEdgeDir, getProgramFilesDir(true) + microsoftEdgeDir, getProgramFilesDir() + googleChromeDir, getProgramFilesDir(true) + googleChromeDir }; for (const QString &dir : programFileDirs) { QDir d(dir); if (d.exists()) { QFileInfoList widevineVersionDirs = d.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name | QDir::Reversed); for (int i = 0; i < widevineVersionDirs.size(); ++i) { QString potentialWidevinePluginPath = widevineVersionDirs.at(i).absoluteFilePath() + "/WidevineCdm/_platform_specific"_L1; appendArchitectureAndFilename(potentialWidevinePluginPath); pluginPaths << potentialWidevinePluginPath; } } } // Look inside WebView data that lives in system32 QDir potentialEdgeWebViewDir(getSystem32Dir() + "/Microsoft-Edge-WebView"_L1); if (potentialEdgeWebViewDir.exists()) { QString potentialWidevinePluginPath = potentialEdgeWebViewDir.absolutePath() + "/WidevineCdm/_platform_specific"_L1; appendArchitectureAndFilename(potentialWidevinePluginPath); pluginPaths << potentialWidevinePluginPath; } // As a last resort, look for Google Chrome data inside %APPDATA%. This may be obsolete QDir potentialChromeUserDataDir(getLocalAppDataDir() + "/Google/Chrome/User Data/WidevineCDM"_L1); if (potentialChromeUserDataDir.exists()) { QFileInfoList widevineVersionDirs = potentialChromeUserDataDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name | QDir::Reversed); for (int i = 0; i < widevineVersionDirs.size(); ++i) { QString potentialWidevinePluginPath = widevineVersionDirs.at(i).absoluteFilePath() + "/_platform_specific"_L1; appendArchitectureAndFilename(potentialWidevinePluginPath); pluginPaths << potentialWidevinePluginPath; } } #elif defined(Q_OS_LINUX) QList potentialWidevineVersionDirs; // Google Chrome widevine modules QDir chromeWidevineDir(QDir::homePath() + "/.config/google-chrome/WidevineCdm"_L1); if (chromeWidevineDir.exists()) potentialWidevineVersionDirs << widevineCdmDirHint(chromeWidevineDir); // Firefox widevine modules QDir firefoxPotentialProfilesDir(QDir::homePath() + "/.mozilla/firefox"_L1); if (firefoxPotentialProfilesDir.exists()) { QFileInfoList firefoxProfileDirs = firefoxPotentialProfilesDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name | QDir::Reversed); for (const QFileInfo &info : firefoxProfileDirs) { QDir widevinePluginsDir(info.absoluteFilePath() + "/gmp-widevinecdm"_L1); if (widevinePluginsDir.exists()) potentialWidevineVersionDirs << widevinePluginsDir; } } // Chromium widevine modules (might not work with proprietary codecs) QDir chromiumWidevineDir(QDir::homePath() + "/.config/chromium/WidevineCdm"_L1); if (chromiumWidevineDir.exists()) potentialWidevineVersionDirs << widevineCdmDirHint(chromiumWidevineDir); // Search for widewine versions for (const QDir &dir : potentialWidevineVersionDirs) { QFileInfoList widevineVersionDirs = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name | QDir::Reversed); widevineVersionDirs.prepend(QFileInfo(dir.absolutePath())); // ### alternatively look up in the manifest.json and take the path from there. #if Q_PROCESSOR_WORDSIZE == 8 const auto library = "/_platform_specific/linux_x64/libwidevinecdm.so"_L1; #else const auto library = "/_platform_specific/linux_x86/libwidevinecdm.so"_L1; #endif for (const QFileInfo &info : widevineVersionDirs) { pluginPaths << info.absoluteFilePath() + "/libwidevinecdm.so"_L1; pluginPaths << info.absoluteFilePath() + library; } } // Fixed paths: pluginPaths << u"/usr/lib/chromium/libwidevinecdm.so"_s // Arch << u"/usr/lib/chromium-browser/libwidevinecdm.so"_s // Ubuntu/neon << u"/usr/lib64/chromium/libwidevinecdm.so"_s // OpenSUSE style #if Q_PROCESSOR_WORDSIZE == 8 << u"/usr/lib64/chromium-browser/WidevineCdm/_platform_specific/linux_x64/libwidevinecdm.so"_s // Gentoo << u"/opt/google/chrome/WidevineCdm/_platform_specific/linux_x64/libwidevinecdm.so"_s // Old Google Chrome #else << u"/usr/lib/chromium-browser/WidevineCdm/_platform_specific/linux_x86/libwidevinecdm.so"_s // Gentoo << u"/opt/google/chrome/WidevineCdm/_platform_specific/linux_x86/libwidevinecdm.so"_s // Old Google Chrome #endif << u"/opt/google/chrome/libwidevinecdm.so"_s; // Older Google Chrome #endif } for (const QString &pluginPath : std::as_const(pluginPaths)) { *cdm_path = QtWebEngineCore::toFilePath(pluginPath); if (base::PathExists(*cdm_path)) { // Add the supported codecs as if they came from the component manifest. // This list must match the CDM that is being bundled with Chrome. const std::vector kAllProfiles = {}; capability->video_codecs.emplace(media::VideoCodec::kVP8, kAllProfiles); capability->video_codecs.emplace(media::VideoCodec::kVP9, kAllProfiles); capability->video_codecs.emplace(media::VideoCodec::kAV1, kAllProfiles); #if BUILDFLAG(USE_PROPRIETARY_CODECS) capability->video_codecs.emplace(media::VideoCodec::kH264, kAllProfiles); #endif // BUILDFLAG(USE_PROPRIETARY_CODECS) #if BUILDFLAG(ENABLE_PLATFORM_HEVC) capability->video_codecs.emplace(media::VideoCodec::kHEVC, kAllProfiles); #endif capability->audio_codecs = media::GetCdmSupportedAudioCodecs(); // Add the supported encryption schemes as if they came from the // component manifest. This list must match the CDM that is being // bundled with Chrome. capability->encryption_schemes.insert(media::EncryptionScheme::kCenc); capability->encryption_schemes.insert(media::EncryptionScheme::kCbcs); // Temporary session is always supported. capability->session_types.insert(media::CdmSessionType::kTemporary); return true; } } return false; } #endif // defined(WIDEVINE_CDM_AVAILABLE_NOT_COMPONENT) void ContentClientQt::AddContentDecryptionModules(std::vector *cdms, std::vector *cdm_host_file_paths) { Q_UNUSED(cdm_host_file_paths); if (cdms) { #if defined(WIDEVINE_CDM_AVAILABLE_NOT_COMPONENT) || BUILDFLAG(ENABLE_LIBRARY_CDMS) media::CdmCapability capability; #endif #if defined(WIDEVINE_CDM_AVAILABLE_NOT_COMPONENT) base::FilePath cdm_path; if (IsWidevineAvailable(&cdm_path, &capability)) { const base::Version version; cdms->push_back(content::CdmInfo(kWidevineKeySystem, Robustness::kSoftwareSecure, std::move(capability), /*supports_sub_key_systems=*/false, kWidevineCdmDisplayName, kWidevineCdmType, version, cdm_path)); } #endif // defined(WIDEVINE_CDM_AVAILABLE_NOT_COMPONENT) #if BUILDFLAG(ENABLE_LIBRARY_CDMS) // Register Clear Key CDM if specified in command line. base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); base::FilePath clear_key_cdm_path = command_line->GetSwitchValuePath(switches::kClearKeyCdmPathForTesting); if (!clear_key_cdm_path.empty() && base::PathExists(clear_key_cdm_path)) { // Supported codecs are hard-coded in ExternalClearKeyProperties. capability = media::CdmCapability( {}, {}, { media::EncryptionScheme::kCenc, media::EncryptionScheme::kCbcs }, { media::CdmSessionType::kTemporary, media::CdmSessionType::kPersistentLicense }); // Register media::kExternalClearKeyDifferentCdmTypeTestKeySystem first separately. // Otherwise, it'll be treated as a sub-key-system of normal // kExternalClearKeyKeySystem. See MultipleCdmTypes test in // ECKEncryptedMediaTest. cdms->push_back(content::CdmInfo(media::kExternalClearKeyDifferentCdmTypeTestKeySystem, Robustness::kSoftwareSecure, capability, /*supports_sub_key_systems=*/false, media::kClearKeyCdmDisplayName, media::kClearKeyCdmDifferentCdmType, base::Version("0.1.0.0"), clear_key_cdm_path)); cdms->push_back(content::CdmInfo(media::kExternalClearKeyKeySystem, Robustness::kSoftwareSecure, capability, /*supports_sub_key_systems=*/true, media::kClearKeyCdmDisplayName, media::kClearKeyCdmType, base::Version("0.1.0.0"), clear_key_cdm_path)); } #endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) } } void ContentClientQt::AddAdditionalSchemes(Schemes* schemes) { // Matching ChromeContentClient::AddAdditionalSchemes schemes->standard_schemes.push_back(extensions::kExtensionScheme); schemes->secure_schemes.push_back(extensions::kExtensionScheme); #if BUILDFLAG(ENABLE_EXTENSIONS) schemes->service_worker_schemes.push_back(extensions::kExtensionScheme); schemes->cors_enabled_schemes.push_back(extensions::kExtensionScheme); schemes->csp_bypassing_schemes.push_back(extensions::kExtensionScheme); #endif } std::string_view ContentClientQt::GetDataResource(int resource_id, ui::ResourceScaleFactor scale_factor) { return ui::ResourceBundle::GetSharedInstance().GetRawDataResourceForScale(resource_id, scale_factor); } base::RefCountedMemory *ContentClientQt::GetDataResourceBytes(int resource_id) { return ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytes(resource_id); } gfx::Image &ContentClientQt::GetNativeImageNamed(int resource_id) { return ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(resource_id); } std::u16string ContentClientQt::GetLocalizedString(int message_id) { return l10n_util::GetStringUTF16(message_id); } // This method is a copy from chrome/common/chrome_content_client.cc: // Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE.Chromium file. blink::OriginTrialPolicy *ContentClientQt::GetOriginTrialPolicy() { // Prevent initialization race (see crbug.com/721144). There may be a // race when the policy is needed for worker startup (which happens on a // separate worker thread). base::AutoLock auto_lock(origin_trial_policy_lock_); if (!origin_trial_policy_) origin_trial_policy_ = std::make_unique(); return origin_trial_policy_.get(); } void ContentClientQt::SetGpuInfo(const gpu::GPUInfo &gpu_info) { if (Q_LIKELY(!lcWebEngineCompositor().isDebugEnabled())) return; base::CommandLine *commandLine = base::CommandLine::ForCurrentProcess(); const bool isBrowserProcess = !commandLine->HasSwitch(switches::kProcessType); const bool isMainThread = QThread::currentThread() == qApp->thread(); // Limit this to the main thread of the browser process for now. if (!isBrowserProcess || !isMainThread) return; if (!gpu_info.IsInitialized()) { // This is probably not an issue but suspicious. qCDebug(lcWebEngineCompositor, "Failed to initialize GPUInfo."); return; } const gpu::GPUInfo::GPUDevice &primary = gpu_info.gpu; // Do not print the info again if the device hasn't been changed. // Change of the device is unexpected: we don't support or implement fallback yet. // It is suspicious if the info is logged twice. if (m_gpuInfo && m_gpuInfo->gpu.device_string == primary.device_string) return; m_gpuInfo = gpu_info; auto deviceToString = [](const gpu::GPUInfo::GPUDevice &device) -> QString { if (device.vendor_id == 0x0) return "Disabled"_L1; QString log; // TODO: Factor vendor translation out from QtWebEngineCore::GPUInfo. // Only name the most commmon desktop GPU hardware vendors for now. switch (device.vendor_id) { case 0x1002: log += "AMD"_L1; break; case 0x10DE: log += "Nvidia"_L1; break; case 0x8086: log += "Intel"_L1; break; default: log += "vendor id: 0x"_L1 + QString::number(device.vendor_id, 16); } log += ", device id: 0x"_L1 + QString::number(device.device_id, 16); if (!device.driver_vendor.empty()) { log += ", driver: "_L1 + QLatin1StringView(device.driver_vendor) + u' ' + QLatin1StringView(device.driver_version); } log += ", system device id: 0x"_L1 + QString::number(device.system_device_id, 16); log += ", preference: "_L1; switch (device.gpu_preference) { case gl::GpuPreference::kNone: log += "None"_L1; break; case gl::GpuPreference::kDefault: log += "Default"_L1; break; case gl::GpuPreference::kLowPower: log += "LowPower"_L1; break; case gl::GpuPreference::kHighPerformance: log += "HighPerformance"_L1; break; } log += ", active: "_L1 + (device.active ? "yes"_L1 : "no"_L1); return log; }; QString log; if (gpu_info.gl_vendor.empty() || gpu_info.gl_vendor == "Disabled") { log += "ANGLE is disabled:\n"_L1; log += " GL Renderer: "_L1 + QLatin1StringView(gpu_info.gl_renderer) + u'\n'; log += " Software Renderer: "_L1 + (primary.IsSoftwareRenderer() ? "yes"_L1 : "no"_L1) + u'\n'; log += " Primary GPU: "_L1 + deviceToString(primary) + u'\n'; } else { log += QLatin1StringView(gpu_info.display_type) + " display is initialized:\n"_L1; log += " GL Renderer: "_L1 + QLatin1StringView(gpu_info.gl_renderer) + u'\n'; log += " "_L1 + QString::number(gpu_info.GpuCount()) + " GPU(s) detected:\n"_L1; log += " "_L1 + deviceToString(primary) + u'\n'; for (auto &secondary : gpu_info.secondary_gpus) log += " "_L1 + deviceToString(secondary) + u'\n'; log += " NVIDIA Optimus: "_L1 + (gpu_info.optimus ? "enabled"_L1 : "disabled"_L1) + u'\n'; log += " AMD Switchable: "_L1 + (gpu_info.amd_switchable ? "enabled"_L1 : "disabled"_L1); } qCDebug(lcWebEngineCompositor, "%ls", qUtf16Printable(log)); #if BUILDFLAG(IS_WIN) log = "Windows specific driver information:\n"_L1; log += " Direct Composition: "_L1; if (gpu_info.overlay_info.direct_composition) log += "enabled\n"_L1; else if (gl::GetGlWorkarounds().disable_direct_composition) log += "disabled by workaround\n"_L1; else log += "disabled\n"_L1; log += " Supports Overlays: "_L1 + (gpu_info.overlay_info.supports_overlays ? "yes"_L1 : "no"_L1) + u'\n'; log += " Supports D3D Shared Images: "_L1 + (gpu_info.shared_image_d3d ? "yes"_L1 : "no"_L1); qCDebug(lcWebEngineCompositor, "%ls", qUtf16Printable(log)); #endif } } // namespace QtWebEngineCore