summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/devtools_frontend_qt.cpp4
-rw-r--r--src/core/favicon_manager.cpp5
-rw-r--r--src/core/favicon_manager.h1
-rw-r--r--src/core/media_capture_devices_dispatcher.cpp51
-rw-r--r--src/core/render_widget_host_view_qt.cpp3
-rw-r--r--src/core/renderer/web_channel_ipc_transport.cpp4
-rw-r--r--src/core/web_contents_adapter.cpp320
-rw-r--r--src/core/web_contents_adapter.h30
-rw-r--r--src/core/web_contents_adapter_client.h15
-rw-r--r--src/core/web_contents_delegate_qt.cpp133
-rw-r--r--src/core/web_contents_delegate_qt.h34
11 files changed, 559 insertions, 41 deletions
diff --git a/src/core/devtools_frontend_qt.cpp b/src/core/devtools_frontend_qt.cpp
index 9eedee42a..c2d79331f 100644
--- a/src/core/devtools_frontend_qt.cpp
+++ b/src/core/devtools_frontend_qt.cpp
@@ -190,6 +190,8 @@ DevToolsFrontendQt *DevToolsFrontendQt::Show(QSharedPointer<WebContentsAdapter>
frontendAdapter->initialize(site.get());
}
+ frontendAdapter->setInspector(true);
+
content::WebContents *contents = frontendAdapter->webContents();
if (contents == inspectedContents) {
qWarning() << "You can not inspect youself";
@@ -232,6 +234,8 @@ DevToolsFrontendQt::DevToolsFrontendQt(QSharedPointer<WebContentsAdapter> webCon
DevToolsFrontendQt::~DevToolsFrontendQt()
{
+ if (QSharedPointer<WebContentsAdapter> p = m_webContentsAdapter)
+ p->setInspector(false);
}
void DevToolsFrontendQt::Activate()
diff --git a/src/core/favicon_manager.cpp b/src/core/favicon_manager.cpp
index f7ba858c1..489e23d99 100644
--- a/src/core/favicon_manager.cpp
+++ b/src/core/favicon_manager.cpp
@@ -362,6 +362,11 @@ void FaviconManager::generateCandidateIcon(bool touchIconsEnabled)
}
}
+void FaviconManager::copyStateFrom(FaviconManager *source)
+{
+ m_faviconInfoMap = source->m_faviconInfoMap;
+ m_icons = source->m_icons;
+}
FaviconInfo::FaviconInfo()
: url(QUrl())
diff --git a/src/core/favicon_manager.h b/src/core/favicon_manager.h
index 75d6aa75b..df74f6303 100644
--- a/src/core/favicon_manager.h
+++ b/src/core/favicon_manager.h
@@ -118,6 +118,7 @@ public:
QIcon getIcon(const QUrl &url = QUrl()) const;
FaviconInfo getFaviconInfo(const QUrl &) const;
QList<FaviconInfo> getFaviconInfoList(bool) const;
+ void copyStateFrom(FaviconManager *source);
private:
void update(const QList<FaviconInfo> &);
diff --git a/src/core/media_capture_devices_dispatcher.cpp b/src/core/media_capture_devices_dispatcher.cpp
index ecc46f244..55c0bb39b 100644
--- a/src/core/media_capture_devices_dispatcher.cpp
+++ b/src/core/media_capture_devices_dispatcher.cpp
@@ -45,6 +45,7 @@
#include "javascript_dialog_manager_qt.h"
#include "type_conversion.h"
+#include "web_contents_delegate_qt.h"
#include "web_contents_view_qt.h"
#include "web_engine_settings.h"
@@ -166,6 +167,40 @@ WebContentsAdapterClient::MediaRequestFlags mediaRequestFlagsForRequest(const co
return requestFlags;
}
+// Based on MediaStreamCaptureIndicator::UIDelegate
+class MediaStreamUIQt : public content::MediaStreamUI
+{
+public:
+ MediaStreamUIQt(content::WebContents *webContents, const blink::MediaStreamDevices &devices)
+ : m_delegate(static_cast<WebContentsDelegateQt *>(webContents->GetDelegate())->AsWeakPtr())
+ , m_devices(devices)
+ {
+ DCHECK(!m_devices.empty());
+ }
+
+ ~MediaStreamUIQt() override
+ {
+ if (m_started && m_delegate)
+ m_delegate->removeDevices(m_devices);
+ }
+
+private:
+ gfx::NativeViewId OnStarted(base::OnceClosure, base::RepeatingClosure) override
+ {
+ DCHECK(!m_started);
+ m_started = true;
+ if (m_delegate)
+ m_delegate->addDevices(m_devices);
+ return 0;
+ }
+
+ base::WeakPtr<WebContentsDelegateQt> m_delegate;
+ const blink::MediaStreamDevices m_devices;
+ bool m_started = false;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaStreamUIQt);
+};
+
} // namespace
MediaCaptureDevicesDispatcher::PendingAccessRequest::PendingAccessRequest(const content::MediaStreamRequest &request,
@@ -237,8 +272,12 @@ void MediaCaptureDevicesDispatcher::handleMediaAccessPermissionResponse(content:
base::Unretained(this), webContents));
}
- std::move(callback).Run(devices, devices.empty() ? blink::MEDIA_DEVICE_INVALID_STATE : blink::MEDIA_DEVICE_OK,
- std::unique_ptr<content::MediaStreamUI>());
+ if (devices.empty())
+ std::move(callback).Run(devices, blink::MEDIA_DEVICE_INVALID_STATE,
+ std::unique_ptr<content::MediaStreamUI>());
+ else
+ std::move(callback).Run(devices, blink::MEDIA_DEVICE_OK,
+ std::make_unique<MediaStreamUIQt>(webContents, devices));
}
MediaCaptureDevicesDispatcher *MediaCaptureDevicesDispatcher::GetInstance()
@@ -336,8 +375,12 @@ void MediaCaptureDevicesDispatcher::processDesktopCaptureAccessRequest(content::
getDevicesForDesktopCapture(&devices, mediaId, capture_audio);
- std::move(callback).Run(devices, devices.empty() ? blink::MEDIA_DEVICE_INVALID_STATE : blink::MEDIA_DEVICE_OK,
- std::unique_ptr<content::MediaStreamUI>());
+ if (devices.empty())
+ std::move(callback).Run(devices, blink::MEDIA_DEVICE_INVALID_STATE,
+ std::unique_ptr<content::MediaStreamUI>());
+ else
+ std::move(callback).Run(devices, blink::MEDIA_DEVICE_OK,
+ std::make_unique<MediaStreamUIQt>(webContents, devices));
}
void MediaCaptureDevicesDispatcher::enqueueMediaAccessRequest(content::WebContents *webContents,
diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp
index 994e3a3d6..091171f90 100644
--- a/src/core/render_widget_host_view_qt.cpp
+++ b/src/core/render_widget_host_view_qt.cpp
@@ -634,7 +634,8 @@ void RenderWidgetHostViewQt::ImeCompositionRangeChanged(const gfx::Range&, const
void RenderWidgetHostViewQt::RenderProcessGone(base::TerminationStatus terminationStatus,
int exitCode)
{
- if (m_adapterClient) {
+ // RenderProcessHost::FastShutdownIfPossible results in TERMINATION_STATUS_STILL_RUNNING
+ if (m_adapterClient && terminationStatus != base::TERMINATION_STATUS_STILL_RUNNING) {
m_adapterClient->renderProcessTerminated(
m_adapterClient->renderProcessExitStatus(terminationStatus),
exitCode);
diff --git a/src/core/renderer/web_channel_ipc_transport.cpp b/src/core/renderer/web_channel_ipc_transport.cpp
index 3b9c17b6a..745fe8b1e 100644
--- a/src/core/renderer/web_channel_ipc_transport.cpp
+++ b/src/core/renderer/web_channel_ipc_transport.cpp
@@ -214,9 +214,11 @@ void WebChannelIPCTransport::ResetWorldId()
void WebChannelIPCTransport::DispatchWebChannelMessage(const std::vector<uint8_t> &binaryJson, uint32_t worldId)
{
- DCHECK(m_canUseContext);
DCHECK(m_worldId == worldId);
+ if (!m_canUseContext)
+ return;
+
QJsonDocument doc = QJsonDocument::fromRawData(reinterpret_cast<const char *>(binaryJson.data()),
binaryJson.size(), QJsonDocument::BypassValidation);
DCHECK(doc.isObject());
diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp
index c4f4591e3..3c6e651a7 100644
--- a/src/core/web_contents_adapter.cpp
+++ b/src/core/web_contents_adapter.cpp
@@ -478,32 +478,7 @@ void WebContentsAdapter::initialize(content::SiteInstance *site)
m_webContents = content::WebContents::Create(create_params);
}
- content::RendererPreferences* rendererPrefs = m_webContents->GetMutableRendererPrefs();
- rendererPrefs->use_custom_colors = true;
- // Qt returns a flash time (the whole cycle) in ms, chromium expects just the interval in seconds
- const int qtCursorFlashTime = QGuiApplication::styleHints()->cursorFlashTime();
- rendererPrefs->caret_blink_interval = base::TimeDelta::FromMillisecondsD(0.5 * static_cast<double>(qtCursorFlashTime));
- rendererPrefs->user_agent_override = m_profileAdapter->httpUserAgent().toStdString();
- rendererPrefs->accept_languages = m_profileAdapter->httpAcceptLanguageWithoutQualities().toStdString();
-#if QT_CONFIG(webengine_webrtc)
- base::CommandLine* commandLine = base::CommandLine::ForCurrentProcess();
- if (commandLine->HasSwitch(switches::kForceWebRtcIPHandlingPolicy))
- rendererPrefs->webrtc_ip_handling_policy = commandLine->GetSwitchValueASCII(switches::kForceWebRtcIPHandlingPolicy);
- else
- rendererPrefs->webrtc_ip_handling_policy = m_adapterClient->webEngineSettings()->testAttribute(WebEngineSettings::WebRTCPublicInterfacesOnly)
- ? content::kWebRTCIPHandlingDefaultPublicInterfaceOnly
- : content::kWebRTCIPHandlingDefault;
-#endif
- // Set web-contents font settings to the default font settings as Chromium constantly overrides
- // the global font defaults with the font settings of the latest web-contents created.
- static const gfx::FontRenderParams params = gfx::GetFontRenderParams(gfx::FontRenderParamsQuery(), nullptr);
- rendererPrefs->should_antialias_text = params.antialiasing;
- rendererPrefs->use_subpixel_positioning = params.subpixel_positioning;
- rendererPrefs->hinting = params.hinting;
- rendererPrefs->use_autohinter = params.autohinter;
- rendererPrefs->use_bitmaps = params.use_bitmaps;
- rendererPrefs->subpixel_rendering = params.subpixel_rendering;
- m_webContents->GetRenderViewHost()->SyncRendererPrefs();
+ initializeRenderPrefs();
// Create and attach observers to the WebContents.
m_webContentsDelegate.reset(new WebContentsDelegateQt(m_webContents.get(), m_adapterClient));
@@ -540,6 +515,40 @@ void WebContentsAdapter::initialize(content::SiteInstance *site)
m_adapterClient->initializationFinished();
}
+void WebContentsAdapter::initializeRenderPrefs()
+{
+ content::RendererPreferences *rendererPrefs = m_webContents->GetMutableRendererPrefs();
+ rendererPrefs->use_custom_colors = true;
+ // Qt returns a flash time (the whole cycle) in ms, chromium expects just the interval in
+ // seconds
+ const int qtCursorFlashTime = QGuiApplication::styleHints()->cursorFlashTime();
+ rendererPrefs->caret_blink_interval =
+ base::TimeDelta::FromMillisecondsD(0.5 * static_cast<double>(qtCursorFlashTime));
+ rendererPrefs->user_agent_override = m_profileAdapter->httpUserAgent().toStdString();
+ rendererPrefs->accept_languages = m_profileAdapter->httpAcceptLanguageWithoutQualities().toStdString();
+#if QT_CONFIG(webengine_webrtc)
+ base::CommandLine* commandLine = base::CommandLine::ForCurrentProcess();
+ if (commandLine->HasSwitch(switches::kForceWebRtcIPHandlingPolicy))
+ rendererPrefs->webrtc_ip_handling_policy =
+ commandLine->GetSwitchValueASCII(switches::kForceWebRtcIPHandlingPolicy);
+ else
+ rendererPrefs->webrtc_ip_handling_policy =
+ m_adapterClient->webEngineSettings()->testAttribute(WebEngineSettings::WebRTCPublicInterfacesOnly)
+ ? content::kWebRTCIPHandlingDefaultPublicInterfaceOnly
+ : content::kWebRTCIPHandlingDefault;
+#endif
+ // Set web-contents font settings to the default font settings as Chromium constantly overrides
+ // the global font defaults with the font settings of the latest web-contents created.
+ static const gfx::FontRenderParams params = gfx::GetFontRenderParams(gfx::FontRenderParamsQuery(), nullptr);
+ rendererPrefs->should_antialias_text = params.antialiasing;
+ rendererPrefs->use_subpixel_positioning = params.subpixel_positioning;
+ rendererPrefs->hinting = params.hinting;
+ rendererPrefs->use_autohinter = params.autohinter;
+ rendererPrefs->use_bitmaps = params.use_bitmaps;
+ rendererPrefs->subpixel_rendering = params.subpixel_rendering;
+ m_webContents->GetRenderViewHost()->SyncRendererPrefs();
+}
+
bool WebContentsAdapter::canGoBack() const
{
CHECK_INITIALIZED(false);
@@ -568,16 +577,22 @@ void WebContentsAdapter::stop()
void WebContentsAdapter::reload()
{
CHECK_INITIALIZED();
+ bool wasDiscarded = (m_lifecycleState == LifecycleState::Discarded);
+ setLifecycleState(LifecycleState::Active);
CHECK_VALID_RENDER_WIDGET_HOST_VIEW(m_webContents->GetRenderViewHost());
- m_webContents->GetController().Reload(content::ReloadType::NORMAL, /*checkRepost = */false);
+ if (!wasDiscarded) // undiscard() already triggers a reload
+ m_webContents->GetController().Reload(content::ReloadType::NORMAL, /*checkRepost = */false);
focusIfNecessary();
}
void WebContentsAdapter::reloadAndBypassCache()
{
CHECK_INITIALIZED();
+ bool wasDiscarded = (m_lifecycleState == LifecycleState::Discarded);
+ setLifecycleState(LifecycleState::Active);
CHECK_VALID_RENDER_WIDGET_HOST_VIEW(m_webContents->GetRenderViewHost());
- m_webContents->GetController().Reload(content::ReloadType::BYPASSING_CACHE, /*checkRepost = */false);
+ if (!wasDiscarded) // undiscard() already triggers a reload
+ m_webContents->GetController().Reload(content::ReloadType::BYPASSING_CACHE, /*checkRepost = */false);
focusIfNecessary();
}
@@ -600,6 +615,8 @@ void WebContentsAdapter::load(const QWebEngineHttpRequest &request)
scoped_refptr<content::SiteInstance> site =
content::SiteInstance::CreateForURL(m_profileAdapter->profile(), gurl);
initialize(site.get());
+ } else {
+ setLifecycleState(LifecycleState::Active);
}
CHECK_VALID_RENDER_WIDGET_HOST_VIEW(m_webContents->GetRenderViewHost());
@@ -691,6 +708,8 @@ void WebContentsAdapter::setContent(const QByteArray &data, const QString &mimeT
{
if (!isInitialized())
loadDefault();
+ else
+ setLifecycleState(LifecycleState::Active);
CHECK_VALID_RENDER_WIDGET_HOST_VIEW(m_webContents->GetRenderViewHost());
@@ -1106,7 +1125,7 @@ void WebContentsAdapter::setAudioMuted(bool muted)
m_webContents->SetAudioMuted(muted);
}
-bool WebContentsAdapter::recentlyAudible()
+bool WebContentsAdapter::recentlyAudible() const
{
CHECK_INITIALIZED(false);
return m_webContents->IsCurrentlyAudible();
@@ -1167,6 +1186,18 @@ bool WebContentsAdapter::hasInspector() const
return false;
}
+bool WebContentsAdapter::isInspector() const
+{
+ return m_inspector;
+}
+
+void WebContentsAdapter::setInspector(bool inspector)
+{
+ m_inspector = inspector;
+ if (inspector)
+ setLifecycleState(LifecycleState::Active);
+}
+
void WebContentsAdapter::openDevToolsFrontend(QSharedPointer<WebContentsAdapter> frontendAdapter)
{
Q_ASSERT(isInitialized());
@@ -1179,7 +1210,10 @@ void WebContentsAdapter::openDevToolsFrontend(QSharedPointer<WebContentsAdapter>
m_devToolsFrontend->Close();
}
+ setLifecycleState(LifecycleState::Active);
+
m_devToolsFrontend = DevToolsFrontendQt::Show(frontendAdapter, m_webContents.get());
+ updateRecommendedState();
}
void WebContentsAdapter::closeDevToolsFrontend()
@@ -1195,6 +1229,7 @@ void WebContentsAdapter::devToolsFrontendDestroyed(DevToolsFrontendQt *frontend)
Q_ASSERT(frontend == m_devToolsFrontend);
Q_UNUSED(frontend);
m_devToolsFrontend = nullptr;
+ updateRecommendedState();
}
void WebContentsAdapter::exitFullScreen()
@@ -1654,8 +1689,7 @@ WebContentsAdapterClient::renderProcessExitStatus(int terminationStatus) {
break;
case base::TERMINATION_STATUS_STILL_RUNNING:
case base::TERMINATION_STATUS_MAX_ENUM:
- // should be unreachable since Chromium asserts status != TERMINATION_STATUS_STILL_RUNNING
- // before calling this method
+ Q_UNREACHABLE();
break;
}
@@ -1680,6 +1714,230 @@ bool WebContentsAdapter::canViewSource()
return m_webContents->GetController().CanViewSource();
}
+WebContentsAdapter::LifecycleState WebContentsAdapter::lifecycleState() const
+{
+ return m_lifecycleState;
+}
+
+void WebContentsAdapter::setLifecycleState(LifecycleState state)
+{
+ CHECK_INITIALIZED();
+
+ LifecycleState from = m_lifecycleState;
+ LifecycleState to = state;
+
+ const auto warn = [from, to](const char *reason) {
+ static const char *names[] { "Active", "Frozen", "Discarded" };
+ qWarning("setLifecycleState: failed to transition from %s to %s state: %s",
+ names[(int)from], names[(int)to], reason);
+ };
+
+ if (from == to)
+ return;
+
+ if (from == LifecycleState::Active) {
+ if (isVisible()) {
+ warn("page is visible");
+ return;
+ }
+ if (hasInspector() || isInspector()) {
+ warn("DevTools open");
+ return;
+ }
+ }
+
+ if (from == LifecycleState::Discarded && to != LifecycleState::Active) {
+ warn("illegal transition");
+ return;
+ }
+
+ // Prevent recursion due to initializationFinished() in undiscard().
+ m_lifecycleState = to;
+
+ switch (to) {
+ case LifecycleState::Active:
+ if (from == LifecycleState::Frozen)
+ unfreeze();
+ else
+ undiscard();
+ break;
+ case LifecycleState::Frozen:
+ freeze();
+ break;
+ case LifecycleState::Discarded:
+ discard();
+ break;
+ }
+
+ m_adapterClient->lifecycleStateChanged(to);
+ updateRecommendedState();
+}
+
+WebContentsAdapter::LifecycleState WebContentsAdapter::recommendedState() const
+{
+ return m_recommendedState;
+}
+
+WebContentsAdapter::LifecycleState WebContentsAdapter::determineRecommendedState() const
+{
+ CHECK_INITIALIZED(LifecycleState::Active);
+
+ if (m_lifecycleState == LifecycleState::Discarded)
+ return LifecycleState::Discarded;
+
+ if (isVisible())
+ return LifecycleState::Active;
+
+ if (m_webContentsDelegate->loadingState() != WebContentsDelegateQt::LoadingState::Loaded)
+ return LifecycleState::Active;
+
+ if (recentlyAudible())
+ return LifecycleState::Active;
+
+ if (m_webContents->GetSiteInstance()->GetRelatedActiveContentsCount() > 1U)
+ return LifecycleState::Active;
+
+ if (hasInspector() || isInspector())
+ return LifecycleState::Active;
+
+ if (m_webContentsDelegate->isCapturingAudio() || m_webContentsDelegate->isCapturingVideo()
+ || m_webContentsDelegate->isMirroring() || m_webContentsDelegate->isCapturingDesktop())
+ return LifecycleState::Active;
+
+ if (m_webContents->IsCrashed())
+ return LifecycleState::Active;
+
+ if (m_lifecycleState == LifecycleState::Active)
+ return LifecycleState::Frozen;
+
+ // Form input is not saved.
+ if (m_webContents->GetPageImportanceSignals().had_form_interaction)
+ return LifecycleState::Frozen;
+
+ // Do not discard PDFs as they might contain entry that is not saved and they
+ // don't remember their scrolling positions. See crbug.com/547286 and
+ // crbug.com/65244.
+ if (m_webContents->GetContentsMimeType() == "application/pdf")
+ return LifecycleState::Frozen;
+
+ return LifecycleState::Discarded;
+}
+
+void WebContentsAdapter::updateRecommendedState()
+{
+ LifecycleState newState = determineRecommendedState();
+ if (m_recommendedState == newState)
+ return;
+
+ m_recommendedState = newState;
+ m_adapterClient->recommendedStateChanged(newState);
+}
+
+bool WebContentsAdapter::isVisible() const
+{
+ CHECK_INITIALIZED(false);
+
+ // Visibility::OCCLUDED is not used
+ return m_webContents->GetVisibility() == content::Visibility::VISIBLE;
+}
+
+void WebContentsAdapter::setVisible(bool visible)
+{
+ CHECK_INITIALIZED();
+
+ if (isVisible() == visible)
+ return;
+
+ if (visible) {
+ setLifecycleState(LifecycleState::Active);
+ wasShown();
+ } else {
+ Q_ASSERT(m_lifecycleState == LifecycleState::Active);
+ wasHidden();
+ }
+
+ m_adapterClient->visibleChanged(visible);
+ updateRecommendedState();
+}
+
+void WebContentsAdapter::freeze()
+{
+ m_webContents->SetPageFrozen(true);
+}
+
+void WebContentsAdapter::unfreeze()
+{
+ m_webContents->SetPageFrozen(false);
+}
+
+void WebContentsAdapter::discard()
+{
+ // Based on TabLifecycleUnitSource::TabLifecycleUnit::FinishDiscard
+
+ if (m_webContents->IsLoading()) {
+ m_webContentsDelegate->didFailLoad(m_webContentsDelegate->url(), net::Error::ERR_ABORTED,
+ QStringLiteral("Discarded"));
+ }
+
+ content::WebContents::CreateParams createParams(m_profileAdapter->profile());
+ createParams.initially_hidden = true;
+ createParams.desired_renderer_state = content::WebContents::CreateParams::kNoRendererProcess;
+ createParams.last_active_time = m_webContents->GetLastActiveTime();
+ std::unique_ptr<content::WebContents> nullContents = content::WebContents::Create(createParams);
+ std::unique_ptr<WebContentsDelegateQt> nullDelegate(new WebContentsDelegateQt(nullContents.get(), m_adapterClient));
+ nullContents->GetController().CopyStateFrom(&m_webContents->GetController(),
+ /* needs_reload */ false);
+ nullDelegate->copyStateFrom(m_webContentsDelegate.get());
+ nullContents->SetWasDiscarded(true);
+
+ // Kill render process if this is the only page it's got.
+ content::RenderProcessHost *renderProcessHost = m_webContents->GetMainFrame()->GetProcess();
+ renderProcessHost->FastShutdownIfPossible(/* page_count */ 1u,
+ /* skip_unload_handlers */ false);
+
+#if QT_CONFIG(webengine_webchannel)
+ if (m_webChannel)
+ m_webChannel->disconnectFrom(m_webChannelTransport.get());
+ m_webChannelTransport.reset();
+ m_webChannel = nullptr;
+ m_webChannelWorld = 0;
+#endif
+ m_renderViewObserverHost.reset();
+ m_webContentsDelegate.reset();
+ m_webContents.reset();
+
+ m_webContents = std::move(nullContents);
+ initializeRenderPrefs();
+ m_webContentsDelegate = std::move(nullDelegate);
+ m_renderViewObserverHost.reset(new RenderViewObserverHostQt(m_webContents.get(), m_adapterClient));
+ WebContentsViewQt *contentsView =
+ static_cast<WebContentsViewQt *>(static_cast<content::WebContentsImpl *>(m_webContents.get())->GetView());
+ contentsView->setClient(m_adapterClient);
+#if QT_CONFIG(webengine_printing_and_pdf)
+ PrintViewManagerQt::CreateForWebContents(webContents());
+#endif
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+ extensions::ExtensionWebContentsObserverQt::CreateForWebContents(webContents());
+#endif
+}
+
+void WebContentsAdapter::undiscard()
+{
+ m_webContents->GetController().SetNeedsReload();
+ m_webContents->GetController().LoadIfNecessary();
+ // Create a RenderView with the initial empty document
+ content::RenderViewHost *rvh = m_webContents->GetRenderViewHost();
+ Q_ASSERT(rvh);
+ if (!rvh->IsRenderViewLive())
+ static_cast<content::WebContentsImpl *>(m_webContents.get())
+ ->CreateRenderViewForRenderManager(rvh, MSG_ROUTING_NONE, MSG_ROUTING_NONE,
+ base::UnguessableToken::Create(),
+ content::FrameReplicationState());
+ m_webContentsDelegate->RenderViewHostChanged(nullptr, rvh);
+ m_adapterClient->initializationFinished();
+ m_adapterClient->selectionChanged();
+}
+
ASSERT_ENUMS_MATCH(WebContentsAdapterClient::UnknownDisposition, WindowOpenDisposition::UNKNOWN)
ASSERT_ENUMS_MATCH(WebContentsAdapterClient::CurrentTabDisposition, WindowOpenDisposition::CURRENT_TAB)
ASSERT_ENUMS_MATCH(WebContentsAdapterClient::SingletonTabDisposition, WindowOpenDisposition::SINGLETON_TAB)
diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h
index 79aa68456..ab9ec5b81 100644
--- a/src/core/web_contents_adapter.h
+++ b/src/core/web_contents_adapter.h
@@ -109,6 +109,14 @@ public:
void load(const QWebEngineHttpRequest &request);
void setContent(const QByteArray &data, const QString &mimeType, const QUrl &baseUrl);
+ using LifecycleState = WebContentsAdapterClient::LifecycleState;
+ LifecycleState lifecycleState() const;
+ void setLifecycleState(LifecycleState state);
+ LifecycleState recommendedState() const;
+
+ bool isVisible() const;
+ void setVisible(bool visible);
+
bool canGoBack() const;
bool canGoForward() const;
void stop();
@@ -155,7 +163,7 @@ public:
ReferrerPolicy referrerPolicy = ReferrerPolicy::Default);
bool isAudioMuted() const;
void setAudioMuted(bool mute);
- bool recentlyAudible();
+ bool recentlyAudible() const;
// Must match blink::WebMediaPlayerAction::Type.
enum MediaPlayerAction {
@@ -171,6 +179,8 @@ public:
void inspectElementAt(const QPoint &location);
bool hasInspector() const;
+ bool isInspector() const;
+ void setInspector(bool inspector);
void exitFullScreen();
void requestClose();
void changedFullScreen();
@@ -178,8 +188,6 @@ public:
void closeDevToolsFrontend();
void devToolsFrontendDestroyed(DevToolsFrontendQt *frontend);
- void wasShown();
- void wasHidden();
void grantMediaAccessPermission(const QUrl &securityOrigin, WebContentsAdapterClient::MediaRequestFlags flags);
void runGeolocationRequestCallback(const QUrl &securityOrigin, bool allowed);
void grantMouseLockPermission(bool granted);
@@ -221,12 +229,25 @@ public:
// meant to be used within WebEngineCore only
void initialize(content::SiteInstance *site);
content::WebContents *webContents() const;
+ void updateRecommendedState();
private:
Q_DISABLE_COPY(WebContentsAdapter)
void waitForUpdateDragActionCalled();
bool handleDropDataFileContents(const content::DropData &dropData, QMimeData *mimeData);
+ void wasShown();
+ void wasHidden();
+
+ LifecycleState determineRecommendedState() const;
+
+ void freeze();
+ void unfreeze();
+ void discard();
+ void undiscard();
+
+ void initializeRenderPrefs();
+
ProfileAdapter *m_profileAdapter;
std::unique_ptr<content::WebContents> m_webContents;
std::unique_ptr<WebContentsDelegateQt> m_webContentsDelegate;
@@ -246,6 +267,9 @@ private:
QPointF m_lastDragScreenPos;
std::unique_ptr<QTemporaryDir> m_dndTmpDir;
DevToolsFrontendQt *m_devToolsFrontend;
+ LifecycleState m_lifecycleState = LifecycleState::Active;
+ LifecycleState m_recommendedState = LifecycleState::Active;
+ bool m_inspector = false;
};
} // namespace QtWebEngineCore
diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h
index 7ba45aea8..780c14466 100644
--- a/src/core/web_contents_adapter_client.h
+++ b/src/core/web_contents_adapter_client.h
@@ -412,11 +412,26 @@ public:
};
Q_DECLARE_FLAGS(MediaRequestFlags, MediaRequestFlag)
+ enum class LifecycleState {
+ Active,
+ Frozen,
+ Discarded,
+ };
+
+ enum class LoadingState {
+ Unloaded,
+ Loading,
+ Loaded,
+ };
+
virtual ~WebContentsAdapterClient() { }
virtual RenderWidgetHostViewQtDelegate* CreateRenderWidgetHostViewQtDelegate(RenderWidgetHostViewQtDelegateClient *client) = 0;
virtual RenderWidgetHostViewQtDelegate* CreateRenderWidgetHostViewQtDelegateForPopup(RenderWidgetHostViewQtDelegateClient *client) = 0;
virtual void initializationFinished() = 0;
+ virtual void lifecycleStateChanged(LifecycleState) = 0;
+ virtual void recommendedStateChanged(LifecycleState) = 0;
+ virtual void visibleChanged(bool) = 0;
virtual void titleChanged(const QString&) = 0;
virtual void urlChanged(const QUrl&) = 0;
virtual void iconChanged(const QUrl&) = 0;
diff --git a/src/core/web_contents_delegate_qt.cpp b/src/core/web_contents_delegate_qt.cpp
index 021044a71..f4d794de5 100644
--- a/src/core/web_contents_delegate_qt.cpp
+++ b/src/core/web_contents_delegate_qt.cpp
@@ -104,6 +104,8 @@ WebContentsDelegateQt::WebContentsDelegateQt(content::WebContents *webContents,
, m_lastReceivedFindReply(0)
, m_faviconManager(new FaviconManager(webContents, adapterClient))
, m_lastLoadProgress(-1)
+ , m_loadingState(determineLoadingState(webContents))
+ , m_didStartLoadingSeen(m_loadingState == LoadingState::Loading)
{
webContents->SetDelegate(this);
Observe(webContents);
@@ -267,6 +269,18 @@ void WebContentsDelegateQt::RenderFrameDeleted(content::RenderFrameHost *render_
m_loadingErrorFrameList.removeOne(render_frame_host->GetRoutingID());
}
+void WebContentsDelegateQt::RenderProcessGone(base::TerminationStatus status)
+{
+ // Based one TabLoadTracker::RenderProcessGone
+
+ if (status == base::TerminationStatus::TERMINATION_STATUS_NORMAL_TERMINATION
+ || status == base::TerminationStatus::TERMINATION_STATUS_STILL_RUNNING) {
+ return;
+ }
+
+ setLoadingState(LoadingState::Unloaded);
+}
+
void WebContentsDelegateQt::RenderViewHostChanged(content::RenderViewHost *, content::RenderViewHost *newHost)
{
if (newHost && newHost->GetWidget() && newHost->GetWidget()->GetView()) {
@@ -354,6 +368,46 @@ void WebContentsDelegateQt::DidFinishNavigation(content::NavigationHandle *navig
}
}
+void WebContentsDelegateQt::DidStartLoading()
+{
+ // Based on TabLoadTracker::DidStartLoading
+
+ if (!web_contents()->IsLoadingToDifferentDocument())
+ return;
+ if (m_loadingState == LoadingState::Loading) {
+ DCHECK(m_didStartLoadingSeen);
+ return;
+ }
+ m_didStartLoadingSeen = true;
+}
+
+void WebContentsDelegateQt::DidReceiveResponse()
+{
+ // Based on TabLoadTracker::DidReceiveResponse
+
+ if (m_loadingState == LoadingState::Loading) {
+ DCHECK(m_didStartLoadingSeen);
+ return;
+ }
+
+ // A transition to loading requires both DidStartLoading (navigation
+ // committed) and DidReceiveResponse (data has been transmitted over the
+ // network) events to occur. This is because NavigationThrottles can block
+ // actual network requests, but not the rest of the state machinery.
+ if (m_didStartLoadingSeen)
+ setLoadingState(LoadingState::Loading);
+}
+
+void WebContentsDelegateQt::DidStopLoading()
+{
+ // Based on TabLoadTracker::DidStopLoading
+
+ // NOTE: PageAlmostIdle feature not implemented
+
+ if (m_loadingState == LoadingState::Loading)
+ setLoadingState(LoadingState::Loaded);
+}
+
void WebContentsDelegateQt::didFailLoad(const QUrl &url, int errorCode, const QString &errorDescription)
{
m_viewClient->iconChanged(QUrl());
@@ -362,6 +416,9 @@ void WebContentsDelegateQt::didFailLoad(const QUrl &url, int errorCode, const QS
void WebContentsDelegateQt::DidFailLoad(content::RenderFrameHost* render_frame_host, const GURL& validated_url, int error_code, const base::string16& error_description)
{
+ if (m_loadingState == LoadingState::Loading)
+ setLoadingState(LoadingState::Loaded);
+
if (render_frame_host != web_contents()->GetMainFrame())
return;
@@ -724,4 +781,80 @@ WebContentsAdapter *WebContentsDelegateQt::webContentsAdapter() const
return m_viewClient->webContentsAdapter();
}
+void WebContentsDelegateQt::copyStateFrom(WebContentsDelegateQt *source)
+{
+ m_url = source->m_url;
+ m_title = source->m_title;
+ NavigationStateChanged(web_contents(), content::INVALIDATE_TYPE_URL);
+ m_faviconManager->copyStateFrom(source->m_faviconManager.data());
+}
+
+WebContentsDelegateQt::LoadingState WebContentsDelegateQt::determineLoadingState(content::WebContents *contents)
+{
+ // Based on TabLoadTracker::DetermineLoadingState
+
+ if (contents->IsLoadingToDifferentDocument() && !contents->IsWaitingForResponse())
+ return LoadingState::Loading;
+
+ content::NavigationController &controller = contents->GetController();
+ if (controller.GetLastCommittedEntry() != nullptr && !controller.IsInitialNavigation() && !controller.NeedsReload())
+ return LoadingState::Loaded;
+
+ return LoadingState::Unloaded;
+}
+
+void WebContentsDelegateQt::setLoadingState(LoadingState state)
+{
+ if (m_loadingState == state)
+ return;
+
+ m_loadingState = state;
+
+ webContentsAdapter()->updateRecommendedState();
+}
+
+int &WebContentsDelegateQt::streamCount(blink::MediaStreamType type)
+{
+ // Based on MediaStreamCaptureIndicator::WebContentsDeviceUsage::GetStreamCount
+ switch (type) {
+ case blink::MEDIA_DEVICE_AUDIO_CAPTURE:
+ return m_audioStreamCount;
+
+ case blink::MEDIA_DEVICE_VIDEO_CAPTURE:
+ return m_videoStreamCount;
+
+ case blink::MEDIA_GUM_TAB_AUDIO_CAPTURE:
+ case blink::MEDIA_GUM_TAB_VIDEO_CAPTURE:
+ return m_mirroringStreamCount;
+
+ case blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE:
+ case blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE:
+ case blink::MEDIA_DISPLAY_VIDEO_CAPTURE:
+ return m_desktopStreamCount;
+
+ case blink::MEDIA_NO_SERVICE:
+ case blink::NUM_MEDIA_TYPES:
+ NOTREACHED();
+ return m_videoStreamCount;
+ }
+ NOTREACHED();
+ return m_videoStreamCount;
+}
+
+void WebContentsDelegateQt::addDevices(const blink::MediaStreamDevices &devices)
+{
+ for (const auto &device : devices)
+ ++streamCount(device.type);
+
+ webContentsAdapter()->updateRecommendedState();
+}
+
+void WebContentsDelegateQt::removeDevices(const blink::MediaStreamDevices &devices)
+{
+ for (const auto &device : devices)
+ ++streamCount(device.type);
+
+ webContentsAdapter()->updateRecommendedState();
+}
+
} // namespace QtWebEngineCore
diff --git a/src/core/web_contents_delegate_qt.h b/src/core/web_contents_delegate_qt.h
index 1629222c2..2ef4f22fc 100644
--- a/src/core/web_contents_delegate_qt.h
+++ b/src/core/web_contents_delegate_qt.h
@@ -40,6 +40,7 @@
#ifndef WEB_CONTENTS_DELEGATE_QT_H
#define WEB_CONTENTS_DELEGATE_QT_H
+#include "content/public/browser/media_capture_devices.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/browser/web_contents_observer.h"
#include "third_party/skia/include/core/SkColor.h"
@@ -133,9 +134,13 @@ public:
// WebContentsObserver overrides
void RenderFrameDeleted(content::RenderFrameHost *render_frame_host) override;
+ void RenderProcessGone(base::TerminationStatus status) override;
void RenderViewHostChanged(content::RenderViewHost *old_host, content::RenderViewHost *new_host) override;
void DidStartNavigation(content::NavigationHandle *navigation_handle) override;
void DidFinishNavigation(content::NavigationHandle *navigation_handle) override;
+ void DidStartLoading() override;
+ void DidReceiveResponse() override;
+ void DidStopLoading() override;
void DidFailLoad(content::RenderFrameHost* render_frame_host, const GURL& validated_url, int error_code, const base::string16& error_description) override;
void DidFinishLoad(content::RenderFrameHost *render_frame_host, const GURL &validated_url) override;
void BeforeUnloadFired(bool proceed, const base::TimeTicks& proceed_time) override;
@@ -160,12 +165,32 @@ public:
WebContentsAdapter *webContentsAdapter() const;
WebContentsAdapterClient *adapterClient() const { return m_viewClient; }
+ void copyStateFrom(WebContentsDelegateQt *source);
+
+ using LoadingState = WebContentsAdapterClient::LoadingState;
+ LoadingState loadingState() const { return m_loadingState; }
+
+ void addDevices(const blink::MediaStreamDevices &devices);
+ void removeDevices(const blink::MediaStreamDevices &devices);
+
+ bool isCapturingAudio() const { return m_audioStreamCount > 0; }
+ bool isCapturingVideo() const { return m_videoStreamCount > 0; }
+ bool isMirroring() const { return m_mirroringStreamCount > 0; }
+ bool isCapturingDesktop() const { return m_desktopStreamCount > 0; }
+
+ base::WeakPtr<WebContentsDelegateQt> AsWeakPtr() { return m_weakPtrFactory.GetWeakPtr(); }
+
private:
QWeakPointer<WebContentsAdapter> createWindow(std::unique_ptr<content::WebContents> new_contents, WindowOpenDisposition disposition, const gfx::Rect& initial_pos, bool user_gesture);
void EmitLoadStarted(const QUrl &url, bool isErrorPage = false);
void EmitLoadFinished(bool success, const QUrl &url, bool isErrorPage = false, int errorCode = 0, const QString &errorDescription = QString());
void EmitLoadCommitted();
+ LoadingState determineLoadingState(content::WebContents *contents);
+ void setLoadingState(LoadingState state);
+
+ int &streamCount(blink::MediaStreamType type);
+
WebContentsAdapterClient *m_viewClient;
QString m_lastSearchedString;
int m_lastReceivedFindReply;
@@ -175,9 +200,16 @@ private:
QSharedPointer<FilePickerController> m_filePickerController;
QUrl m_initialTargetUrl;
int m_lastLoadProgress;
-
+ LoadingState m_loadingState;
+ bool m_didStartLoadingSeen;
QUrl m_url;
QString m_title;
+ int m_audioStreamCount = 0;
+ int m_videoStreamCount = 0;
+ int m_mirroringStreamCount = 0;
+ int m_desktopStreamCount = 0;
+
+ base::WeakPtrFactory<WebContentsDelegateQt> m_weakPtrFactory { this };
};
} // namespace QtWebEngineCore