// 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/threading/thread_task_runner_handle.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(bool track_fullscreen_rwhv) : track_fullscreen_rwhv_(track_fullscreen_rwhv), 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 { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, 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 { BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::BindOnce(&WebContentsTracker::Observe, this, static_cast(nullptr))); } } RenderWidgetHostView* WebContentsTracker::GetTargetView() const { DCHECK_CURRENTLY_ON(BrowserThread::UI); WebContents* const wc = web_contents(); if (!wc) return nullptr; if (track_fullscreen_rwhv_) { if (auto* view = wc->GetFullscreenRenderWidgetHostView()) return view; } 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