// 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 #include #include #include "base/macros.h" #include "base/memory/scoped_vector.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 { 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; explicit OffscreenTabsOwner(content::WebContents* extension_web_contents); content::WebContents* const extension_web_contents_; ScopedVector 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& 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(int routing_id) 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 scoped_ptr profile_; // The WebContents containing the off-screen tab's page. scoped_ptr 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_