/**************************************************************************** ** ** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtWebEngine module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "browser_main_parts_qt.h" #include "api/qwebenginemessagepumpscheduler_p.h" #include "base/message_loop/message_pump_for_ui.h" #include "base/process/process.h" #include "base/task/current_thread.h" #include "base/task/sequence_manager/sequence_manager_impl.h" #include "base/task/sequence_manager/thread_controller_with_message_pump_impl.h" #include "base/threading/thread_restrictions.h" #include "chrome/browser/tab_contents/form_interaction_tab_helper.h" #include "components/performance_manager/embedder/performance_manager_lifetime.h" #include "components/performance_manager/embedder/performance_manager_registry.h" #include "components/performance_manager/public/graph/graph.h" #include "components/performance_manager/public/performance_manager.h" #include "content/public/browser/browser_main_parts.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/child_process_security_policy.h" #include "extensions/buildflags/buildflags.h" #if BUILDFLAG(ENABLE_EXTENSIONS) #include "content/public/browser/plugin_service.h" #include "extensions/common/constants.h" #include "extensions/common/extensions_client.h" #include "extensions/extensions_browser_client_qt.h" #include "extensions/extension_system_factory_qt.h" #include "extensions/plugin_service_filter_qt.h" #include "common/extensions/extensions_client_qt.h" #endif //BUILDFLAG(ENABLE_EXTENSIONS) #include "select_file_dialog_factory_qt.h" #include "services/service_manager/public/cpp/connector.h" #include "services/service_manager/public/cpp/service.h" #include "ui/display/screen.h" #include "web_engine_context.h" #include #if QT_CONFIG(opengl) #include "ui/gl/gl_context.h" #include #endif #if defined(OS_MAC) #include "base/message_loop/message_pump_mac.h" #include "ui/base/idle/idle.h" #endif #if defined(Q_OS_WIN) #include "ui/display/win/screen_win.h" #else #include "desktop_screen_qt.h" #endif namespace QtWebEngineCore { namespace { // Return a timeout suitable for the glib loop, -1 to block forever, // 0 to return right away, or a timeout in milliseconds from now. int GetTimeIntervalMilliseconds(const base::TimeTicks &from) { if (from.is_null()) return -1; // Be careful here. TimeDelta has a precision of microseconds, but we want a // value in milliseconds. If there are 5.5ms left, should the delay be 5 or // 6? It should be 6 to avoid executing delayed work too early. int delay = static_cast(std::ceil((from - base::TimeTicks::Now()).InMillisecondsF())); // If this value is negative, then we need to run delayed work soon. return delay < 0 ? 0 : delay; } } // anonymous namespace class MessagePumpForUIQt : public base::MessagePump { public: MessagePumpForUIQt() : m_scheduler([this]() { handleScheduledWork(); }) {} void Run(Delegate *) override { // This is used only when MessagePumpForUIQt is used outside of the GUI thread. NOTIMPLEMENTED(); } void Quit() override { // This is used only when MessagePumpForUIQt is used outside of the GUI thread. NOTIMPLEMENTED(); } void ScheduleWork() override { // NOTE: This method may called from any thread at any time. ensureDelegate(); m_scheduler.scheduleWork(); } void ScheduleDelayedWork(const base::TimeTicks &delayed_work_time) override { // NOTE: This method may called from any thread at any time. ensureDelegate(); m_scheduler.scheduleDelayedWork(GetTimeIntervalMilliseconds(delayed_work_time)); } private: void ensureDelegate() { if (!m_delegate) { auto seqMan = base::CurrentThread::Get()->GetCurrentSequenceManagerImpl(); m_delegate = static_cast( seqMan->controller_.get()); } } // Both Qt and Chromium keep track of the current GL context by using // thread-local variables, and naturally they are completely unaware of each // other. As a result, when a QOpenGLContext is made current, the previous // gl::GLContext is not released, and vice-versa. This is fine as long as // each thread uses exclusively either Qt or Chromium GL bindings, which is // usually the case. // // The only exception is when the GL driver is considered thread-unsafe // (QOpenGLContext::supportsThreadedOpenGL() is false), in which case we // have to run all GL operations, including Chromium's GPU service, on the // UI thread. Now the bindings are being mixed and both Qt and Chromium get // quite confused regarding the current state of the surface. // // To get this to work we have to release the current QOpenGLContext before // executing any tasks from Chromium's GPU service and the gl::GLContext // afterwards. Since GPU service just posts tasks to the UI thread task // runner, we'll have to instrument the entire UI thread message pump. class ScopedGLContextChecker { #if QT_CONFIG(opengl) public: ScopedGLContextChecker() { if (!m_enabled) return; if (QOpenGLContext *context = QOpenGLContext::currentContext()) context->doneCurrent(); } ~ScopedGLContextChecker() { if (!m_enabled) return; if (gl::GLContext *context = gl::GLContext::GetCurrent()) context->ReleaseCurrent(nullptr); } private: bool m_enabled = WebEngineContext::isGpuServiceOnUIThread(); #endif // QT_CONFIG(opengl) }; void handleScheduledWork() { ScopedGLContextChecker glContextChecker; base::MessagePump::Delegate::NextWorkInfo more_work_info = m_delegate->DoWork(); if (more_work_info.is_immediate()) return ScheduleWork(); if (m_delegate->DoIdleWork()) return ScheduleWork(); ScheduleDelayedWork(more_work_info.delayed_run_time); } Delegate *m_delegate = nullptr; QWebEngineMessagePumpScheduler m_scheduler; }; std::unique_ptr messagePumpFactory() { static bool madePrimaryPump = false; if (!madePrimaryPump) { madePrimaryPump = true; return std::make_unique(); } #if defined(OS_MAC) return base::MessagePumpMac::Create(); #else return std::make_unique(); #endif } int BrowserMainPartsQt::PreEarlyInitialization() { #if BUILDFLAG(ENABLE_EXTENSIONS) content::ChildProcessSecurityPolicy::GetInstance()->RegisterWebSafeScheme(extensions::kExtensionScheme); #endif //ENABLE_EXTENSIONS return 0; } void BrowserMainPartsQt::PreMainMessageLoopStart() { } void BrowserMainPartsQt::PreMainMessageLoopRun() { ui::SelectFileDialog::SetFactory(new SelectFileDialogFactoryQt()); #if BUILDFLAG(ENABLE_EXTENSIONS) extensions::ExtensionsClient::Set(new extensions::ExtensionsClientQt()); extensions::ExtensionsBrowserClient::Set(new extensions::ExtensionsBrowserClientQt()); extensions::ExtensionSystemFactoryQt::GetInstance(); content::PluginService *plugin_service = content::PluginService::GetInstance(); plugin_service->SetFilter(extensions::PluginServiceFilterQt::GetInstance()); #endif //ENABLE_EXTENSIONS } void BrowserMainPartsQt::PostMainMessageLoopRun() { performance_manager_registry_->TearDown(); performance_manager_registry_.reset(); performance_manager::DestroyPerformanceManager(std::move(performance_manager_)); // The ProfileQt's destructor uses the MessageLoop so it should be deleted // right before the RenderProcessHostImpl's destructor destroys it. WebEngineContext::current()->destroyProfileAdapter(); } int BrowserMainPartsQt::PreCreateThreads() { base::ThreadRestrictions::SetIOAllowed(true); #if defined(OS_MAC) ui::InitIdleMonitor(); #endif // Like ChromeBrowserMainExtraPartsViews::PreCreateThreads does. #if defined(Q_OS_WIN) display::Screen::SetScreenInstance(new display::win::ScreenWin); #else display::Screen::SetScreenInstance(new DesktopScreenQt); #endif return 0; } static void CreatePoliciesAndDecorators(performance_manager::Graph *graph) { graph->PassToGraph(FormInteractionTabHelper::CreateGraphObserver()); } void BrowserMainPartsQt::PostCreateThreads() { performance_manager_ = performance_manager::CreatePerformanceManagerWithDefaultDecorators( base::BindOnce(&QtWebEngineCore::CreatePoliciesAndDecorators)); performance_manager_registry_ = performance_manager::PerformanceManagerRegistry::Create(); } } // namespace QtWebEngineCore