1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/ui/media_router/presentation_receiver_window_controller.h"
6 
7 #include <utility>
8 
9 #include "base/bind.h"
10 #include "base/check_op.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/profiles/profile_destroyer.h"
15 #include "chrome/browser/ui/media_router/presentation_receiver_window.h"
16 #include "components/media_router/browser/presentation/receiver_presentation_service_delegate_impl.h"
17 #include "content/public/browser/navigation_controller.h"
18 #include "content/public/browser/navigation_entry.h"
19 #include "content/public/browser/presentation_receiver_flags.h"
20 #include "content/public/browser/web_contents.h"
21 #include "ui/views/widget/widget.h"
22 
23 using content::WebContents;
24 
25 namespace {
26 
CreateWebContentsParams(Profile * profile)27 WebContents::CreateParams CreateWebContentsParams(Profile* profile) {
28   WebContents::CreateParams params(profile);
29   params.starting_sandbox_flags = content::kPresentationReceiverSandboxFlags;
30   return params;
31 }
32 
33 }  // namespace
34 
35 // static
36 std::unique_ptr<PresentationReceiverWindowController>
CreateFromOriginalProfile(Profile * profile,const gfx::Rect & bounds,base::OnceClosure termination_callback,TitleChangeCallback title_change_callback)37 PresentationReceiverWindowController::CreateFromOriginalProfile(
38     Profile* profile,
39     const gfx::Rect& bounds,
40     base::OnceClosure termination_callback,
41     TitleChangeCallback title_change_callback) {
42   DCHECK(profile);
43   DCHECK(!profile->IsOffTheRecord());
44   DCHECK(termination_callback);
45   return base::WrapUnique(new PresentationReceiverWindowController(
46       profile, bounds, std::move(termination_callback),
47       std::move(title_change_callback)));
48 }
49 
~PresentationReceiverWindowController()50 PresentationReceiverWindowController::~PresentationReceiverWindowController() {
51   DCHECK(!web_contents_);
52   DCHECK(!window_);
53 
54   if (otr_profile_) {
55     otr_profile_->RemoveObserver(this);
56     ProfileDestroyer::DestroyProfileWhenAppropriate(otr_profile_);
57   }
58 }
59 
Start(const std::string & presentation_id,const GURL & start_url)60 void PresentationReceiverWindowController::Start(
61     const std::string& presentation_id,
62     const GURL& start_url) {
63   DCHECK(window_);
64   DCHECK(web_contents_);
65 
66   media_router::ReceiverPresentationServiceDelegateImpl::CreateForWebContents(
67       web_contents_.get(), presentation_id);
68 
69   content::NavigationController::LoadURLParams load_params(start_url);
70   load_params.should_replace_current_entry = true;
71   load_params.should_clear_history_list = true;
72   web_contents_->GetController().LoadURLWithParams(load_params);
73 
74   window_->ShowInactiveFullscreen();
75 }
76 
Terminate()77 void PresentationReceiverWindowController::Terminate() {
78   if (web_contents_) {
79     web_contents_->ClosePage();
80   } else if (window_) {
81     window_->Close();
82   } else if (termination_callback_) {
83     std::move(termination_callback_).Run();
84   }
85 }
86 
ExitFullscreen()87 void PresentationReceiverWindowController::ExitFullscreen() {
88   window_->ExitFullscreen();
89 }
90 
CloseWindowForTest()91 void PresentationReceiverWindowController::CloseWindowForTest() {
92   window_->Close();
93 }
94 
IsWindowActiveForTest() const95 bool PresentationReceiverWindowController::IsWindowActiveForTest() const {
96   return window_->IsWindowActive();
97 }
98 
IsWindowFullscreenForTest() const99 bool PresentationReceiverWindowController::IsWindowFullscreenForTest() const {
100   return window_->IsWindowFullscreen();
101 }
102 
GetWindowBoundsForTest() const103 gfx::Rect PresentationReceiverWindowController::GetWindowBoundsForTest() const {
104   return window_->GetWindowBounds();
105 }
106 
web_contents() const107 WebContents* PresentationReceiverWindowController::web_contents() const {
108   return web_contents_.get();
109 }
110 
PresentationReceiverWindowController(Profile * profile,const gfx::Rect & bounds,base::OnceClosure termination_callback,TitleChangeCallback title_change_callback)111 PresentationReceiverWindowController::PresentationReceiverWindowController(
112     Profile* profile,
113     const gfx::Rect& bounds,
114     base::OnceClosure termination_callback,
115     TitleChangeCallback title_change_callback)
116     : otr_profile_(profile->GetOffTheRecordProfile(
117           Profile::OTRProfileID::CreateUniqueForMediaRouter())),
118       web_contents_(WebContents::Create(CreateWebContentsParams(otr_profile_))),
119       window_(PresentationReceiverWindow::Create(this, bounds)),
120       termination_callback_(std::move(termination_callback)),
121       title_change_callback_(std::move(title_change_callback)) {
122   DCHECK(otr_profile_);
123   DCHECK(otr_profile_->IsOffTheRecord());
124   otr_profile_->AddObserver(this);
125   content::WebContentsObserver::Observe(web_contents_.get());
126   web_contents_->SetDelegate(this);
127 }
128 
WindowClosed()129 void PresentationReceiverWindowController::WindowClosed() {
130   window_ = nullptr;
131   Terminate();
132 }
133 
OnProfileWillBeDestroyed(Profile * profile)134 void PresentationReceiverWindowController::OnProfileWillBeDestroyed(
135     Profile* profile) {
136   DCHECK(profile == otr_profile_);
137   web_contents_.reset();
138   otr_profile_ = nullptr;
139   Terminate();
140 }
141 
DidStartNavigation(content::NavigationHandle * handle)142 void PresentationReceiverWindowController::DidStartNavigation(
143     content::NavigationHandle* handle) {
144   if (!navigation_policy_.AllowNavigation(handle))
145     Terminate();
146 }
147 
TitleWasSet(content::NavigationEntry * entry)148 void PresentationReceiverWindowController::TitleWasSet(
149     content::NavigationEntry* entry) {
150   window_->UpdateWindowTitle();
151   if (entry)
152     title_change_callback_.Run(base::UTF16ToUTF8(entry->GetTitle()));
153 }
154 
NavigationStateChanged(WebContents * source,content::InvalidateTypes changed_flags)155 void PresentationReceiverWindowController::NavigationStateChanged(
156     WebContents* source,
157     content::InvalidateTypes changed_flags) {
158   window_->UpdateLocationBar();
159 }
160 
CloseContents(content::WebContents * source)161 void PresentationReceiverWindowController::CloseContents(
162     content::WebContents* source) {
163   web_contents_.reset();
164   Terminate();
165 }
166 
ShouldSuppressDialogs(content::WebContents * source)167 bool PresentationReceiverWindowController::ShouldSuppressDialogs(
168     content::WebContents* source) {
169   DCHECK_EQ(web_contents_.get(), source);
170   // Suppress all because there is no possible direct user interaction with
171   // dialogs.
172   // TODO(https://crbug.com/734191): This does not suppress window.print().
173   return true;
174 }
175 
ShouldFocusLocationBarByDefault(content::WebContents * source)176 bool PresentationReceiverWindowController::ShouldFocusLocationBarByDefault(
177     content::WebContents* source) {
178   DCHECK_EQ(web_contents_.get(), source);
179   // Indicate the location bar should be focused instead of the page, even
180   // though there is no location bar in fullscreen (the presentation's default
181   // state).  This will prevent the page from automatically receiving input
182   // focus, which is usually not intended.
183   return true;
184 }
185 
ShouldFocusPageAfterCrash()186 bool PresentationReceiverWindowController::ShouldFocusPageAfterCrash() {
187   // Never focus the page after a crash.
188   return false;
189 }
190 
CanDownload(const GURL & url,const std::string & request_method,base::OnceCallback<void (bool)> callback)191 void PresentationReceiverWindowController::CanDownload(
192     const GURL& url,
193     const std::string& request_method,
194     base::OnceCallback<void(bool)> callback) {
195   // Local presentation pages are not allowed to download files.
196   std::move(callback).Run(false);
197 }
198 
IsWebContentsCreationOverridden(content::SiteInstance * source_site_instance,content::mojom::WindowContainerType window_container_type,const GURL & opener_url,const std::string & frame_name,const GURL & target_url)199 bool PresentationReceiverWindowController::IsWebContentsCreationOverridden(
200     content::SiteInstance* source_site_instance,
201     content::mojom::WindowContainerType window_container_type,
202     const GURL& opener_url,
203     const std::string& frame_name,
204     const GURL& target_url) {
205   // Disallow creating separate WebContentses.  The WebContents implementation
206   // uses this to spawn new windows/tabs, which is also not allowed for
207   // local presentations.
208   return true;
209 }
210