blob: e6393192a0668b49b68e782e1e2dc37c19eed521 [file] [log] [blame]
// 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_TAB_H_
#define CHROME_BROWSER_EXTENSIONS_API_TAB_CAPTURE_OFFSCREEN_TAB_H_
#include <stdint.h>
#include <string>
#include <vector>
#include "base/macros.h"
#include "base/time/time.h"
#include "base/timer/timer.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"
class Profile;
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
: protected 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);
};
// 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:
//
// 1. When its WebContents::GetCapturerCount() returns to zero, indicating
// there are no more consumers of its captured content (e.g., when all
// MediaStreams have been closed). OffscreenTab will auto-detect this case
// and self-destruct.
// 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.
//
// This class operates exclusively on the UI thread and so is not thread-safe.
class OffscreenTab : protected content::WebContentsDelegate,
protected content::WebContentsObserver {
public:
~OffscreenTab() final;
// The WebContents instance hosting the rendering engine for this
// OffscreenTab.
content::WebContents* web_contents() const {
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.
void Start(const GURL& start_url,
const gfx::Size& initial_size,
const std::string& optional_presentation_id);
// content::WebContentsDelegate overrides to provide the desired behaviors.
void CloseContents(content::WebContents* source) final;
bool ShouldSuppressDialogs(content::WebContents* source) final;
bool ShouldFocusLocationBarByDefault(content::WebContents* source) final;
bool ShouldFocusPageAfterCrash() final;
void CanDownload(const GURL& url,
const std::string& request_method,
const base::Callback<void(bool)>& callback) final;
bool HandleContextMenu(const content::ContextMenuParams& params) final;
bool PreHandleKeyboardEvent(content::WebContents* source,
const content::NativeWebKeyboardEvent& event,
bool* is_keyboard_shortcut) final;
bool PreHandleGestureEvent(content::WebContents* source,
const blink::WebGestureEvent& event) final;
bool CanDragEnter(content::WebContents* source,
const content::DropData& data,
blink::WebDragOperationsMask operations_allowed) final;
bool ShouldCreateWebContents(
content::WebContents* contents,
int32_t route_id,
int32_t main_frame_route_id,
int32_t main_frame_widget_route_id,
WindowContainerType window_container_type,
const std::string& frame_name,
const GURL& target_url,
const std::string& partition_id,
content::SessionStorageNamespace* session_storage_namespace) final;
bool EmbedsFullscreenWidget() const final;
void EnterFullscreenModeForTab(content::WebContents* contents,
const GURL& origin) final;
void ExitFullscreenModeForTab(content::WebContents* contents) final;
bool IsFullscreenForTabOrPending(
const content::WebContents* contents) const final;
blink::WebDisplayMode GetDisplayMode(
const content::WebContents* contents) const final;
void RequestMediaAccessPermission(
content::WebContents* contents,
const content::MediaStreamRequest& request,
const content::MediaResponseCallback& callback) final;
bool CheckMediaAccessPermission(content::WebContents* contents,
const GURL& security_origin,
content::MediaStreamType type) final;
// content::WebContentsObserver overrides
void DidShowFullscreenWidget() final;
private:
bool in_fullscreen_mode() const {
return !non_fullscreen_size_.IsEmpty();
}
// Called by |capture_poll_timer_| to automatically destroy this OffscreenTab
// when the capturer count returns to zero.
void DieIfContentCaptureEnded();
OffscreenTabsOwner* const owner_;
// The initial navigation URL, which may or may not match the current URL if
// page-initiated navigations have occurred.
GURL start_url_;
// A non-shared off-the-record profile based on the profile of the extension
// background page.
const std::unique_ptr<Profile> profile_;
// The WebContents containing the off-screen tab's page.
std::unique_ptr<content::WebContents> offscreen_tab_web_contents_;
// The time at which Start() finished creating |offscreen_tab_web_contents_|.
base::TimeTicks start_time_;
// Set to the original size of the renderer just before entering fullscreen
// mode. When not in fullscreen mode, this is an empty size.
gfx::Size non_fullscreen_size_;
// Poll timer to monitor the capturer count on |offscreen_tab_web_contents_|.
// When the capturer count returns to zero, this OffscreenTab is automatically
// destroyed.
//
// TODO(miu): Add a method to WebContentsObserver to report capturer count
// changes and get rid of this polling-based approach.
// http://crbug.com/540965
base::Timer capture_poll_timer_;
// This is false until after the Start() method is called, and capture of the
// |offscreen_tab_web_contents_| is first detected.
bool content_capture_was_detected_;
DISALLOW_COPY_AND_ASSIGN(OffscreenTab);
};
} // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_API_TAB_CAPTURE_OFFSCREEN_TAB_H_