Commit a8e7bbd4 authored by Xiangjun Zhang's avatar Xiangjun Zhang Committed by Commit Bot

Refactoring: Move OffscreenTab out of TabCapture API.

This CL keeps OffscreenTabOwner in TabCapture API, but moves
OffscreenTab to chrome/browser/media to allow it being used by the
mirroring service to support OffscreenTab mirroring. Implementation is
not changed at all. No functional or behavior change either.

Bug: 734672
Change-Id: Ia8de3680853b86824809d70dc96c6180e9a0c957
Reviewed-on: https://chromium-review.googlesource.com/1180588
Commit-Queue: Xiangjun Zhang <xjz@chromium.org>
Reviewed-by: default avatarLei Zhang <thestig@chromium.org>
Reviewed-by: default avatarYuri Wiitala <miu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#586101}
parent b6c5048e
......@@ -3700,6 +3700,8 @@ jumbo_split_static_library("browser") {
"media/cast_transport_host_filter.h",
"media/extension_media_access_handler.cc",
"media/extension_media_access_handler.h",
"media/offscreen_tab.cc",
"media/offscreen_tab.h",
"media/webrtc/desktop_capture_access_handler.cc",
"media/webrtc/desktop_capture_access_handler.h",
"media/webrtc/tab_capture_access_handler.cc",
......
......@@ -387,8 +387,8 @@ jumbo_static_library("extensions") {
"api/system_indicator/system_indicator_manager_factory.h",
"api/system_private/system_private_api.cc",
"api/system_private/system_private_api.h",
"api/tab_capture/offscreen_tab.cc",
"api/tab_capture/offscreen_tab.h",
"api/tab_capture/offscreen_tabs_owner.cc",
"api/tab_capture/offscreen_tabs_owner.h",
"api/tab_capture/tab_capture_api.cc",
"api/tab_capture/tab_capture_api.h",
"api/tab_capture/tab_capture_registry.cc",
......
// Copyright 2015 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 "chrome/browser/extensions/api/tab_capture/offscreen_tabs_owner.h"
#include <algorithm>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/stl_util.h"
#include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/extension_host.h"
#include "extensions/browser/process_manager.h"
using content::WebContents;
namespace {
// Upper limit on the number of simultaneous off-screen tabs per extension
// instance.
const int kMaxOffscreenTabsPerExtension = 4;
} // namespace
namespace extensions {
OffscreenTabsOwner::OffscreenTabsOwner(WebContents* extension_web_contents)
: extension_web_contents_(extension_web_contents) {
DCHECK(extension_web_contents_);
}
OffscreenTabsOwner::~OffscreenTabsOwner() {}
// static
OffscreenTabsOwner* OffscreenTabsOwner::Get(
content::WebContents* extension_web_contents) {
// CreateForWebContents() really means "create if not exists."
CreateForWebContents(extension_web_contents);
return FromWebContents(extension_web_contents);
}
OffscreenTab* OffscreenTabsOwner::OpenNewTab(
const GURL& start_url,
const gfx::Size& initial_size,
const std::string& optional_presentation_id) {
if (tabs_.size() >= kMaxOffscreenTabsPerExtension)
return nullptr; // Maximum number of offscreen tabs reached.
tabs_.emplace_back(std::make_unique<OffscreenTab>(
this, extension_web_contents_->GetBrowserContext()));
tabs_.back()->Start(start_url, initial_size, optional_presentation_id);
return tabs_.back().get();
}
void OffscreenTabsOwner::RequestMediaAccessPermission(
const content::MediaStreamRequest& request,
content::MediaResponseCallback callback) {
// This method is being called to check whether an extension is permitted to
// capture the page. Verify that the request is being made by the extension
// that spawned this OffscreenTab.
// Find the extension ID associated with the extension background page's
// WebContents.
content::BrowserContext* const extension_browser_context =
extension_web_contents_->GetBrowserContext();
const extensions::Extension* const extension =
ProcessManager::Get(extension_browser_context)
->GetExtensionForWebContents(extension_web_contents_);
const std::string extension_id = extension ? extension->id() : "";
LOG_IF(DFATAL, extension_id.empty())
<< "Extension that started this OffscreenTab was not found.";
// If verified, allow any tab capture audio/video devices that were requested.
extensions::TabCaptureRegistry* const tab_capture_registry =
extensions::TabCaptureRegistry::Get(extension_browser_context);
content::MediaStreamDevices devices;
if (tab_capture_registry &&
tab_capture_registry->VerifyRequest(
request.render_process_id, request.render_frame_id, extension_id)) {
if (request.audio_type == content::MEDIA_GUM_TAB_AUDIO_CAPTURE) {
devices.push_back(content::MediaStreamDevice(
content::MEDIA_GUM_TAB_AUDIO_CAPTURE, std::string(), std::string()));
}
if (request.video_type == content::MEDIA_GUM_TAB_VIDEO_CAPTURE) {
devices.push_back(content::MediaStreamDevice(
content::MEDIA_GUM_TAB_VIDEO_CAPTURE, std::string(), std::string()));
}
}
DVLOG(2) << "Allowing " << devices.size()
<< " capture devices for OffscreenTab content.";
std::move(callback).Run(devices,
devices.empty() ? content::MEDIA_DEVICE_INVALID_STATE
: content::MEDIA_DEVICE_OK,
nullptr);
}
void OffscreenTabsOwner::DestroyTab(OffscreenTab* tab) {
for (std::vector<std::unique_ptr<OffscreenTab>>::iterator iter =
tabs_.begin();
iter != tabs_.end(); ++iter) {
if (iter->get() == tab) {
tabs_.erase(iter);
break;
}
}
}
} // namespace extensions
// Copyright 2015 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.
#ifndef CHROME_BROWSER_EXTENSIONS_API_TAB_CAPTURE_OFFSCREEN_TABS_OWNER_H_
#define CHROME_BROWSER_EXTENSIONS_API_TAB_CAPTURE_OFFSCREEN_TABS_OWNER_H_
#include <stdint.h>
#include <memory>
#include <string>
#include <vector>
#include "base/macros.h"
#include "chrome/browser/media/offscreen_tab.h"
#include "content/public/browser/web_contents_user_data.h"
#include "ui/gfx/geometry/size.h"
class OffscreenTab;
namespace extensions {
// Creates, owns, and manages all OffscreenTab instances created by the same
// extension background page. When the extension background page's WebContents
// is about to be destroyed, its associated OffscreenTabsOwner and all of its
// OffscreenTab instances are destroyed.
//
// Usage:
//
// OffscreenTabsOwner::Get(extension_contents)
// ->OpenNewTab(start_url, size, std::string());
//
// This class operates exclusively on the UI thread and so is not thread-safe.
class OffscreenTabsOwner final
: public OffscreenTab::Owner,
public content::WebContentsUserData<OffscreenTabsOwner> {
public:
~OffscreenTabsOwner() final;
// Returns the OffscreenTabsOwner instance associated with the given extension
// background page's WebContents. Never returns nullptr.
static OffscreenTabsOwner* Get(content::WebContents* extension_web_contents);
// Instantiate a new offscreen tab and navigate it to |start_url|. The new
// tab's main frame will start out with the given |initial_size| in DIP
// coordinates. If too many offscreen tabs are already running, nothing
// happens and nullptr is returned.
//
// If |optional_presentation_id| is non-empty, the offscreen tab is registered
// for use by the Media Router (chrome/browser/media/router/...) as the
// receiving browsing context for the W3C Presentation API.
OffscreenTab* OpenNewTab(const GURL& start_url,
const gfx::Size& initial_size,
const std::string& optional_presentation_id);
private:
friend class content::WebContentsUserData<OffscreenTabsOwner>;
explicit OffscreenTabsOwner(content::WebContents* extension_web_contents);
// OffscreenTab::Owner implementation.
void RequestMediaAccessPermission(
const content::MediaStreamRequest& request,
content::MediaResponseCallback callback) override;
void DestroyTab(OffscreenTab* tab) override;
content::WebContents* const extension_web_contents_;
std::vector<std::unique_ptr<OffscreenTab>> tabs_;
DISALLOW_COPY_AND_ASSIGN(OffscreenTabsOwner);
};
} // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_API_TAB_CAPTURE_OFFSCREEN_TABS_OWNER_H_
......@@ -18,7 +18,7 @@
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "chrome/browser/extensions/api/tab_capture/offscreen_tab.h"
#include "chrome/browser/extensions/api/tab_capture/offscreen_tabs_owner.h"
#include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sessions/session_tab_helper.h"
......
......@@ -2,16 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/api/tab_capture/offscreen_tab.h"
#include "chrome/browser/media/offscreen_tab.h"
#include <algorithm>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/macros.h"
#include "base/stl_util.h"
#include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h"
#include "chrome/browser/media/router/presentation/presentation_navigation_policy.h"
#include "chrome/browser/media/router/presentation/receiver_presentation_service_delegate_impl.h" // nogncheck
#include "chrome/browser/profiles/profile.h"
......@@ -20,18 +17,12 @@
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/extension_host.h"
#include "extensions/browser/process_manager.h"
#include "third_party/blink/public/web/web_presentation_receiver_flags.h"
using content::WebContents;
namespace {
// Upper limit on the number of simultaneous off-screen tabs per extension
// instance.
const int kMaxOffscreenTabsPerExtension = 4;
// Time intervals used by the logic that detects when the capture of an
// offscreen tab has stopped, to automatically tear it down and free resources.
constexpr base::TimeDelta kMaxWaitForCapture = base::TimeDelta::FromMinutes(1);
......@@ -39,62 +30,18 @@ constexpr base::TimeDelta kPollInterval = base::TimeDelta::FromSeconds(1);
} // namespace
namespace extensions {
OffscreenTabsOwner::OffscreenTabsOwner(WebContents* extension_web_contents)
: extension_web_contents_(extension_web_contents) {
DCHECK(extension_web_contents_);
}
OffscreenTabsOwner::~OffscreenTabsOwner() {}
// static
OffscreenTabsOwner* OffscreenTabsOwner::Get(
content::WebContents* extension_web_contents) {
// CreateForWebContents() really means "create if not exists."
CreateForWebContents(extension_web_contents);
return FromWebContents(extension_web_contents);
}
OffscreenTab* OffscreenTabsOwner::OpenNewTab(
const GURL& start_url,
const gfx::Size& initial_size,
const std::string& optional_presentation_id) {
if (tabs_.size() >= kMaxOffscreenTabsPerExtension)
return nullptr; // Maximum number of offscreen tabs reached.
// OffscreenTab cannot be created with MakeUnique<OffscreenTab> since the
// constructor is protected. So create it separately, and then move it to
// |tabs_| below.
std::unique_ptr<OffscreenTab> offscreen_tab(new OffscreenTab(this));
tabs_.push_back(std::move(offscreen_tab));
tabs_.back()->Start(start_url, initial_size, optional_presentation_id);
return tabs_.back().get();
}
void OffscreenTabsOwner::DestroyTab(OffscreenTab* tab) {
for (std::vector<std::unique_ptr<OffscreenTab>>::iterator iter =
tabs_.begin();
iter != tabs_.end(); ++iter) {
if (iter->get() == tab) {
tabs_.erase(iter);
break;
}
}
}
OffscreenTab::OffscreenTab(OffscreenTabsOwner* owner)
OffscreenTab::OffscreenTab(Owner* owner, content::BrowserContext* context)
: owner_(owner),
otr_profile_registration_(
IndependentOTRProfileManager::GetInstance()
->CreateFromOriginalProfile(
Profile::FromBrowserContext(
owner->extension_web_contents()->GetBrowserContext()),
Profile::FromBrowserContext(context),
base::BindOnce(&OffscreenTab::DieIfOriginalProfileDestroyed,
base::Unretained(this)))),
content_capture_was_detected_(false),
navigation_policy_(
std::make_unique<media_router::DefaultNavigationPolicy>()) {
DCHECK(owner_);
DCHECK(otr_profile_registration_->profile());
}
......@@ -193,9 +140,10 @@ bool OffscreenTab::ShouldFocusPageAfterCrash() {
return false;
}
void OffscreenTab::CanDownload(const GURL& url,
const std::string& request_method,
const base::Callback<void(bool)>& callback) {
void OffscreenTab::CanDownload(
const GURL& url,
const std::string& request_method,
const base::RepeatingCallback<void(bool)>& callback) {
// Offscreen tab pages are not allowed to download files.
callback.Run(false);
}
......@@ -302,47 +250,7 @@ void OffscreenTab::RequestMediaAccessPermission(
const content::MediaStreamRequest& request,
content::MediaResponseCallback callback) {
DCHECK_EQ(offscreen_tab_web_contents_.get(), contents);
// This method is being called to check whether an extension is permitted to
// capture the page. Verify that the request is being made by the extension
// that spawned this OffscreenTab.
// Find the extension ID associated with the extension background page's
// WebContents.
content::BrowserContext* const extension_browser_context =
owner_->extension_web_contents()->GetBrowserContext();
const extensions::Extension* const extension =
ProcessManager::Get(extension_browser_context)->
GetExtensionForWebContents(owner_->extension_web_contents());
const std::string extension_id = extension ? extension->id() : "";
LOG_IF(DFATAL, extension_id.empty())
<< "Extension that started this OffscreenTab was not found.";
// If verified, allow any tab capture audio/video devices that were requested.
extensions::TabCaptureRegistry* const tab_capture_registry =
extensions::TabCaptureRegistry::Get(extension_browser_context);
content::MediaStreamDevices devices;
if (tab_capture_registry && tab_capture_registry->VerifyRequest(
request.render_process_id,
request.render_frame_id,
extension_id)) {
if (request.audio_type == content::MEDIA_GUM_TAB_AUDIO_CAPTURE) {
devices.push_back(content::MediaStreamDevice(
content::MEDIA_GUM_TAB_AUDIO_CAPTURE, std::string(), std::string()));
}
if (request.video_type == content::MEDIA_GUM_TAB_VIDEO_CAPTURE) {
devices.push_back(content::MediaStreamDevice(
content::MEDIA_GUM_TAB_VIDEO_CAPTURE, std::string(), std::string()));
}
}
DVLOG(2) << "Allowing " << devices.size()
<< " capture devices for OffscreenTab content.";
std::move(callback).Run(devices,
devices.empty() ? content::MEDIA_DEVICE_INVALID_STATE
: content::MEDIA_DEVICE_OK,
std::unique_ptr<content::MediaStreamUI>(nullptr));
owner_->RequestMediaAccessPermission(request, std::move(callback));
}
bool OffscreenTab::CheckMediaAccessPermission(
......@@ -398,20 +306,19 @@ void OffscreenTab::DieIfContentCaptureEnded() {
// that content capture is never going to start and die to free up
// resources.
LOG(WARNING) << "Capture of OffscreenTab content did not start "
"within timeout for start_url=" << start_url_.spec();
<< "within timeout for start_url=" << start_url_.spec();
owner_->DestroyTab(this);
return; // |this| is no longer valid.
}
// Schedule the timer to check again in a second.
capture_poll_timer_.Start(FROM_HERE, kPollInterval,
base::Bind(&OffscreenTab::DieIfContentCaptureEnded,
base::Unretained(this)));
capture_poll_timer_.Start(
FROM_HERE, kPollInterval,
base::BindRepeating(&OffscreenTab::DieIfContentCaptureEnded,
base::Unretained(this)));
}
void OffscreenTab::DieIfOriginalProfileDestroyed(Profile* profile) {
DCHECK(profile == otr_profile_registration_->profile());
owner_->DestroyTab(this);
}
} // namespace extensions
......@@ -2,14 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_EXTENSIONS_API_TAB_CAPTURE_OFFSCREEN_TAB_H_
#define CHROME_BROWSER_EXTENSIONS_API_TAB_CAPTURE_OFFSCREEN_TAB_H_
#ifndef CHROME_BROWSER_MEDIA_OFFSCREEN_TAB_H_
#define CHROME_BROWSER_MEDIA_OFFSCREEN_TAB_H_
#include <stdint.h>
#include <memory>
#include <string>
#include <vector>
#include "base/macros.h"
#include "base/time/time.h"
......@@ -17,78 +16,22 @@
#include "chrome/browser/media/router/presentation/independent_otr_profile_manager.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
#include "ui/gfx/geometry/size.h"
namespace media_router {
class NavigationPolicy;
} // namespace media_router
namespace extensions {
class OffscreenTab; // Forward declaration. See below.
// Creates, owns, and manages all OffscreenTab instances created by the same
// extension background page. When the extension background page's WebContents
// is about to be destroyed, its associated OffscreenTabsOwner and all of its
// OffscreenTab instances are destroyed.
//
// Usage:
//
// OffscreenTabsOwner::Get(extension_contents)
// ->OpenNewTab(start_url, size, std::string());
//
// This class operates exclusively on the UI thread and so is not thread-safe.
class OffscreenTabsOwner
: public content::WebContentsUserData<OffscreenTabsOwner> {
public:
~OffscreenTabsOwner() final;
// Returns the OffscreenTabsOwner instance associated with the given extension
// background page's WebContents. Never returns nullptr.
static OffscreenTabsOwner* Get(content::WebContents* extension_web_contents);
// Instantiate a new offscreen tab and navigate it to |start_url|. The new
// tab's main frame will start out with the given |initial_size| in DIP
// coordinates. If too many offscreen tabs are already running, nothing
// happens and nullptr is returned.
//
// If |optional_presentation_id| is non-empty, the offscreen tab is registered
// for use by the Media Router (chrome/browser/media/router/...) as the
// receiving browsing context for the W3C Presentation API.
OffscreenTab* OpenNewTab(const GURL& start_url,
const gfx::Size& initial_size,
const std::string& optional_presentation_id);
protected:
friend class OffscreenTab;
// Accessor to the extension background page's WebContents.
content::WebContents* extension_web_contents() const {
return extension_web_contents_;
}
// Shuts down and destroys the |tab|.
void DestroyTab(OffscreenTab* tab);
private:
friend class content::WebContentsUserData<OffscreenTabsOwner>;
explicit OffscreenTabsOwner(content::WebContents* extension_web_contents);
content::WebContents* const extension_web_contents_;
std::vector<std::unique_ptr<OffscreenTab>> tabs_;
DISALLOW_COPY_AND_ASSIGN(OffscreenTabsOwner);
};
namespace content {
class BrowserContext;
} // namespace content
// Owns and controls a sandboxed WebContents instance hosting the rendering
// engine for an offscreen tab. Since the offscreen tab does not interact with
// the user in any direct way, the WebContents is not attached to any Browser
// window/UI, and any input and focusing capabilities are blocked.
//
// OffscreenTab is instantiated by OffscreenTabsOwner. An instance is shut down
// one of three ways:
// An OffscreenTab instance is shut down one of the three ways:
//
// 1. When WebContents::IsBeingCaptured() returns false, indicating there are
// no more consumers of its captured content (e.g., when all MediaStreams
......@@ -97,13 +40,26 @@ class OffscreenTabsOwner
// 2. By the renderer, where the WebContents implementation will invoke the
// WebContentsDelegate::CloseContents() override. This occurs, for
// example, when a page calls window.close().
// 3. Automatically, when the extension background page's WebContents is
// destroyed.
// 3. Automatically, when the associated profile is destroyed.
//
// This class operates exclusively on the UI thread and so is not thread-safe.
class OffscreenTab : protected content::WebContentsDelegate,
protected content::WebContentsObserver {
public:
class Owner {
public:
virtual ~Owner() {}
// Checks whether capturing the |contents| is permitted.
virtual void RequestMediaAccessPermission(
const content::MediaStreamRequest& request,
content::MediaResponseCallback callback) = 0;
// |tab| is no longer valid after this call.
virtual void DestroyTab(OffscreenTab* tab) = 0;
};
OffscreenTab(Owner* owner, content::BrowserContext* context);
~OffscreenTab() final;
// The WebContents instance hosting the rendering engine for this
......@@ -112,15 +68,9 @@ class OffscreenTab : protected content::WebContentsDelegate,
return offscreen_tab_web_contents_.get();
}
protected:
friend class OffscreenTabsOwner;
explicit OffscreenTab(OffscreenTabsOwner* owner);
// Creates the WebContents instance containing the offscreen tab's page,
// configures it for offscreen rendering at the given |initial_size|, and
// navigates it to |start_url|. This is invoked once by OffscreenTabsOwner
// just after construction.
// navigates it to |start_url|. This is invoked once after construction.
void Start(const GURL& start_url,
const gfx::Size& initial_size,
const std::string& optional_presentation_id);
......@@ -128,6 +78,7 @@ class OffscreenTab : protected content::WebContentsDelegate,
// Closes the underlying WebContents.
void Close();
private:
// content::WebContentsDelegate overrides to provide the desired behaviors.
void CloseContents(content::WebContents* source) final;
bool ShouldSuppressDialogs(content::WebContents* source) final;
......@@ -135,7 +86,7 @@ class OffscreenTab : protected content::WebContentsDelegate,
bool ShouldFocusPageAfterCrash() final;
void CanDownload(const GURL& url,
const std::string& request_method,
const base::Callback<void(bool)>& callback) final;
const base::RepeatingCallback<void(bool)>& callback) final;
bool HandleContextMenu(const content::ContextMenuParams& params) final;
content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
content::WebContents* source,
......@@ -180,7 +131,6 @@ class OffscreenTab : protected content::WebContentsDelegate,
void DidShowFullscreenWidget() final;
void DidStartNavigation(content::NavigationHandle* navigation_handle) final;
private:
bool in_fullscreen_mode() const { return !non_fullscreen_size_.IsEmpty(); }
// Called by |capture_poll_timer_| to automatically destroy this OffscreenTab
......@@ -191,7 +141,7 @@ class OffscreenTab : protected content::WebContentsDelegate,
// and |this| therefore needs to be destroyed also.
void DieIfOriginalProfileDestroyed(Profile* profile);
OffscreenTabsOwner* const owner_;
Owner* const owner_; // Outlives this class.
// The initial navigation URL, which may or may not match the current URL if
// page-initiated navigations have occurred.
......@@ -231,6 +181,4 @@ class OffscreenTab : protected content::WebContentsDelegate,
DISALLOW_COPY_AND_ASSIGN(OffscreenTab);
};
} // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_API_TAB_CAPTURE_OFFSCREEN_TAB_H_
#endif // CHROME_BROWSER_MEDIA_OFFSCREEN_TAB_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment