blob: e88e0fcd0cbf6fd70a33000793866abc50f2c30e [file] [log] [blame]
// Copyright (c) 2013 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 file.
#include "content/browser/media/capture/web_contents_tracker.h"
#include "base/bind.h"
#include "base/task/post_task.h"
#include "base/threading/thread_task_runner_handle.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
namespace content {
WebContentsTracker::WebContentsTracker() : last_target_view_(nullptr) {}
WebContentsTracker::~WebContentsTracker() {
// Likely unintentional BUG if Stop() was not called before this point.
DCHECK(!web_contents());
}
void WebContentsTracker::Start(int render_process_id, int main_render_frame_id,
const ChangeCallback& callback) {
DCHECK(!task_runner_ || task_runner_->BelongsToCurrentThread());
task_runner_ = base::ThreadTaskRunnerHandle::Get();
DCHECK(task_runner_);
callback_ = callback;
if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
StartObservingWebContents(render_process_id, main_render_frame_id);
} else {
base::PostTask(
FROM_HERE, {BrowserThread::UI},
base::BindOnce(&WebContentsTracker::StartObservingWebContents, this,
render_process_id, main_render_frame_id));
}
}
void WebContentsTracker::Stop() {
DCHECK(task_runner_->BelongsToCurrentThread());
callback_.Reset();
resize_callback_.Reset();
if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
WebContentsObserver::Observe(nullptr);
} else {
base::PostTask(FROM_HERE, {BrowserThread::UI},
base::BindOnce(&WebContentsTracker::Observe, this,
static_cast<WebContents*>(nullptr)));
}
}
RenderWidgetHostView* WebContentsTracker::GetTargetView() const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
WebContents* const wc = web_contents();
if (!wc)
return nullptr;
if (auto* view = wc->GetRenderWidgetHostView()) {
// Make sure the RWHV is still associated with a RWH before considering the
// view "alive." This is because a null RWH indicates the RWHV has had its
// Destroy() method called.
if (view->GetRenderWidgetHost())
return view;
}
return nullptr;
}
void WebContentsTracker::SetResizeChangeCallback(
const base::Closure& callback) {
DCHECK(!task_runner_ || task_runner_->BelongsToCurrentThread());
resize_callback_ = callback;
}
void WebContentsTracker::OnPossibleTargetChange(bool force_callback_run) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
RenderWidgetHostView* const rwhv = GetTargetView();
if (rwhv == last_target_view_ && !force_callback_run) {
DVLOG(1) << "No target view change (RenderWidgetHostView@" << rwhv << ')';
return;
}
DVLOG(1) << "Will report target change from RenderWidgetHostView@"
<< last_target_view_ << " to RenderWidgetHostView@" << rwhv;
last_target_view_ = rwhv;
if (task_runner_->BelongsToCurrentThread()) {
MaybeDoCallback(is_still_tracking());
return;
}
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&WebContentsTracker::MaybeDoCallback, this,
is_still_tracking()));
}
void WebContentsTracker::MaybeDoCallback(bool was_still_tracking) {
DCHECK(task_runner_->BelongsToCurrentThread());
// Notify of a size change just before notifying of a new target. This allows
// the downstream implementation to capture the first frame from the new
// target at the correct resolution. http://crbug.com/704277
if (was_still_tracking)
MaybeDoResizeCallback();
if (!callback_.is_null())
callback_.Run(was_still_tracking);
}
void WebContentsTracker::MaybeDoResizeCallback() {
DCHECK(task_runner_->BelongsToCurrentThread());
if (!resize_callback_.is_null())
resize_callback_.Run();
}
void WebContentsTracker::StartObservingWebContents(int render_process_id,
int main_render_frame_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
Observe(WebContents::FromRenderFrameHost(RenderFrameHost::FromID(
render_process_id, main_render_frame_id)));
DVLOG_IF(1, !web_contents())
<< "Could not find WebContents associated with main RenderFrameHost "
<< "referenced by render_process_id=" << render_process_id
<< ", routing_id=" << main_render_frame_id;
OnPossibleTargetChange(true);
}
void WebContentsTracker::RenderFrameCreated(
RenderFrameHost* render_frame_host) {
DVLOG(1) << "RenderFrameCreated(rfh=" << render_frame_host << ')';
OnPossibleTargetChange(false);
}
void WebContentsTracker::RenderFrameDeleted(
RenderFrameHost* render_frame_host) {
DVLOG(1) << "RenderFrameDeleted(rfh=" << render_frame_host << ')';
OnPossibleTargetChange(false);
}
void WebContentsTracker::RenderFrameHostChanged(RenderFrameHost* old_host,
RenderFrameHost* new_host) {
DVLOG(1) << "RenderFrameHostChanged(old=" << old_host << ", new=" << new_host
<< ')';
OnPossibleTargetChange(false);
}
void WebContentsTracker::MainFrameWasResized(bool width_changed) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (task_runner_->BelongsToCurrentThread()) {
MaybeDoResizeCallback();
return;
}
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&WebContentsTracker::MaybeDoResizeCallback, this));
}
void WebContentsTracker::WebContentsDestroyed() {
DVLOG(1) << "WebContentsDestroyed()";
Observe(nullptr);
OnPossibleTargetChange(true);
}
void WebContentsTracker::DidShowFullscreenWidget() {
DVLOG(1) << "DidShowFullscreenWidget()";
OnPossibleTargetChange(false);
}
void WebContentsTracker::DidDestroyFullscreenWidget() {
DVLOG(1) << "DidDestroyFullscreenWidget()";
OnPossibleTargetChange(false);
}
} // namespace content