OLD | NEW |
---|---|
(Empty) | |
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 "chromecast/browser/cast_web_view.h" | |
6 | |
7 #include "base/logging.h" | |
8 #include "base/threading/thread_task_runner_handle.h" | |
9 #include "chromecast/base/metrics/cast_metrics_helper.h" | |
10 #include "content/public/browser/navigation_handle.h" | |
11 #include "content/public/browser/render_frame_host.h" | |
12 #include "content/public/browser/render_view_host.h" | |
13 #include "content/public/browser/render_widget_host.h" | |
14 #include "content/public/browser/render_widget_host_view.h" | |
15 #include "content/public/common/renderer_preferences.h" | |
16 #include "ipc/ipc_message.h" | |
17 #include "net/base/net_errors.h" | |
18 #include "ui/display/display.h" | |
19 #include "ui/display/screen.h" | |
20 #include "url/gurl.h" | |
21 | |
22 #if defined(OS_ANDROID) | |
23 #include "chromecast/browser/android/cast_web_contents_activity.h" | |
24 #endif // defined(OS_ANDROID) | |
25 | |
26 #if defined(USE_AURA) | |
27 #include "ui/aura/window.h" | |
28 #endif | |
29 | |
30 namespace chromecast { | |
31 | |
32 namespace { | |
33 // The time (in milliseconds) we wait for after a page is closed (i.e. | |
34 // after an app is stopped) before we delete the corresponding WebContents. | |
35 constexpr int kWebContentsDestructionDelayInMs = 50; | |
36 | |
37 std::unique_ptr<content::WebContents> CreateWebContents( | |
38 content::BrowserContext* browser_context, | |
39 scoped_refptr<content::SiteInstance> site_instance) { | |
40 CHECK(display::Screen::GetScreen()); | |
41 gfx::Size display_size = | |
42 display::Screen::GetScreen()->GetPrimaryDisplay().size(); | |
43 | |
44 content::WebContents::CreateParams create_params(browser_context, NULL); | |
45 create_params.routing_id = MSG_ROUTING_NONE; | |
46 create_params.initial_size = display_size; | |
47 create_params.site_instance = site_instance; | |
48 content::WebContents* web_contents = | |
49 content::WebContents::Create(create_params); | |
50 | |
51 #if defined(USE_AURA) | |
52 // Resize window | |
53 aura::Window* content_window = web_contents->GetNativeView(); | |
54 content_window->SetBounds( | |
55 gfx::Rect(display_size.width(), display_size.height())); | |
56 #endif | |
57 | |
58 #if defined(OS_ANDROID) | |
59 content::RendererPreferences* prefs = web_contents->GetMutableRendererPrefs(); | |
60 prefs->use_video_overlay_for_embedded_encrypted_video = true; | |
61 web_contents->GetRenderViewHost()->SyncRendererPrefs(); | |
62 #endif | |
63 | |
64 return base::WrapUnique(web_contents); | |
65 } | |
66 | |
67 } // namespace | |
68 | |
69 CastWebView::CastWebView(Delegate* delegate, | |
70 content::BrowserContext* browser_context, | |
71 scoped_refptr<content::SiteInstance> site_instance, | |
72 bool transparent) | |
73 : delegate_(delegate), | |
74 browser_context_(browser_context), | |
75 site_instance_(std::move(site_instance)), | |
76 transparent_(transparent), | |
77 window_(shell::CastContentWindow::Create(delegate)), | |
78 web_contents_(CreateWebContents(browser_context_, site_instance_)), | |
79 weak_factory_(this) { | |
80 DCHECK(delegate_); | |
81 DCHECK(browser_context_); | |
82 DCHECK(window_); | |
83 content::WebContentsObserver::Observe(web_contents_.get()); | |
84 web_contents_->SetDelegate(this); | |
85 | |
86 if (transparent_) | |
87 window_->SetTransparent(); | |
88 } | |
89 | |
90 CastWebView::~CastWebView() {} | |
halliwell
2017/02/20 22:09:11
do we need WebContentsObserver::Observe(nullptr),
derekjchow1
2017/02/22 00:50:41
Looks like it's taken care of in the WebContentsOb
| |
91 | |
92 void CastWebView::LoadUrl(GURL url) { | |
93 web_contents_->GetController().LoadURL(url, content::Referrer(), | |
94 ui::PAGE_TRANSITION_TYPED, ""); | |
95 } | |
96 | |
97 void CastWebView::ClosePage() { | |
98 content::WebContentsObserver::Observe(nullptr); | |
99 web_contents_->ClosePage(); | |
100 } | |
101 | |
102 void CastWebView::CloseContents(content::WebContents* source) { | |
103 DCHECK_EQ(source, web_contents_.get()); | |
104 | |
105 // We need to delay the deletion of web_contents_ (currently for 50ms) to | |
halliwell
2017/02/20 22:09:11
nit, delete 'currently for 50ms' ... just risks be
derekjchow1
2017/02/22 00:50:41
Done.
| |
106 // give (and guarantee) the renderer enough time to finish 'onunload' | |
107 // handler (but we don't want to wait any longer than that to delay the | |
108 // starting of next app). | |
109 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | |
110 FROM_HERE, base::Bind(&CastWebView::DelayedCloseContents, | |
111 weak_factory_.GetWeakPtr()), | |
112 base::TimeDelta::FromMilliseconds(kWebContentsDestructionDelayInMs)); | |
113 } | |
114 | |
115 void CastWebView::DelayedCloseContents() { | |
116 // Delete the WebContents object here so that the gfx surface will be | |
117 // deleted as part of destroying RenderWidgetHostViewCast object. | |
118 // We want to delete the surface before we start the next app because | |
119 // the next app could be an external one whose Start() function would | |
120 // destroy the primary gfx plane. | |
121 web_contents_.reset(); | |
122 delegate_->OnPageStopped(net::OK); | |
123 } | |
124 | |
125 void CastWebView::Show(CastWindowManager* window_manager) { | |
126 DCHECK(window_manager); | |
127 window_->ShowWebContents(web_contents_.get(), window_manager); | |
128 web_contents_->Focus(); | |
129 } | |
130 | |
131 content::WebContents* CastWebView::OpenURLFromTab( | |
132 content::WebContents* source, | |
133 const content::OpenURLParams& params) { | |
134 LOG(INFO) << "Change url: " << params.url; | |
135 // If source is NULL which means current tab, use web_contents_ of this class. | |
136 if (!source) | |
137 source = web_contents_.get(); | |
138 DCHECK_EQ(source, web_contents_.get()); | |
139 // We don't want to create another web_contents. Load url only when source is | |
140 // specified. | |
141 if (source) { | |
halliwell
2017/02/20 22:09:11
in what scenario is source null here?
derekjchow1
2017/02/22 00:50:41
I don't think it's ever null. I don't think web_co
| |
142 source->GetController().LoadURL(params.url, params.referrer, | |
143 params.transition, params.extra_headers); | |
144 } | |
145 return source; | |
146 } | |
147 | |
148 void CastWebView::LoadingStateChanged(content::WebContents* source, | |
149 bool to_different_document) { | |
150 delegate_->OnLoadingStateChanged(source->IsLoading()); | |
151 } | |
152 | |
153 void CastWebView::ActivateContents(content::WebContents* contents) { | |
154 DCHECK_EQ(contents, web_contents_.get()); | |
155 contents->GetRenderViewHost()->GetWidget()->Focus(); | |
156 } | |
157 | |
158 #if defined(OS_ANDROID) | |
159 base::android::ScopedJavaLocalRef<jobject> | |
160 CastWebView::GetContentVideoViewEmbedder() { | |
161 DCHECK(web_contents_); | |
162 auto activity = shell::CastWebContentsActivity::Get(web_contents_.get()); | |
163 return activity->GetContentVideoViewEmbedder(); | |
164 } | |
165 #endif // defined(OS_ANDROID) | |
166 | |
167 void CastWebView::RenderProcessGone(base::TerminationStatus status) { | |
168 LOG(INFO) << "APP_ERROR_CHILD_PROCESS_CRASHED"; | |
169 delegate_->OnPageStopped(net::ERR_UNEXPECTED); | |
170 } | |
171 | |
172 void CastWebView::RenderViewCreated(content::RenderViewHost* render_view_host) { | |
173 content::RenderWidgetHostView* view = | |
174 render_view_host->GetWidget()->GetView(); | |
175 if (view) { | |
176 view->SetBackgroundColor(transparent_ ? SK_ColorTRANSPARENT | |
177 : SK_ColorBLACK); | |
178 } | |
179 } | |
180 | |
181 void CastWebView::DidFinishNavigation( | |
182 content::NavigationHandle* navigation_handle) { | |
183 // If the navigation was not committed, it means either the page was a | |
184 // download or error 204/205, or the navigation never left the previous | |
185 // URL. Ignore these navigations. | |
186 if (!navigation_handle->HasCommitted()) { | |
187 LOG(WARNING) << "Navigation did not commit: url=" | |
188 << navigation_handle->GetURL(); | |
189 return; | |
190 } | |
191 | |
192 net::Error error_code = navigation_handle->GetNetErrorCode(); | |
193 if (!navigation_handle->IsErrorPage()) | |
194 return; | |
195 | |
196 // If we abort errors in an iframe, it can create a really confusing | |
197 // and fragile user experience. Rather than create a list of errors | |
198 // that are most likely to occur, we ignore all of them for now. | |
199 if (!navigation_handle->IsInMainFrame()) { | |
200 LOG(ERROR) << "Got error on sub-iframe: url=" << navigation_handle->GetURL() | |
201 << ", error=" << error_code | |
202 << ", description=" << net::ErrorToShortString(error_code); | |
203 return; | |
204 } | |
205 | |
206 LOG(ERROR) << "Got error on navigation: url=" << navigation_handle->GetURL() | |
207 << ", error_code=" << error_code | |
208 << ", description= " << net::ErrorToShortString(error_code); | |
209 delegate_->OnPageStopped(error_code); | |
210 } | |
211 | |
212 void CastWebView::DidFailLoad(content::RenderFrameHost* render_frame_host, | |
213 const GURL& validated_url, | |
214 int error_code, | |
215 const base::string16& error_description, | |
216 bool was_ignored_by_handler) { | |
217 // Only report an error if we are the main frame. See b/8433611. | |
218 if (render_frame_host->GetParent()) { | |
219 LOG(ERROR) << "Got error on sub-iframe: url=" << validated_url.spec() | |
220 << ", error=" << error_code; | |
221 } else if (error_code == net::ERR_ABORTED) { | |
222 // ERR_ABORTED means download was aborted by the app, typically this happens | |
223 // when flinging URL for direct playback, the initial URLRequest gets | |
224 // cancelled/aborted and then the same URL is requested via the buffered | |
225 // data source for media::Pipeline playback. | |
226 LOG(INFO) << "Load canceled: url=" << validated_url.spec(); | |
227 } else { | |
228 LOG(ERROR) << "Got error on load: url=" << validated_url.spec() | |
229 << ", error_code=" << error_code; | |
230 delegate_->OnPageStopped(error_code); | |
231 } | |
232 } | |
233 | |
234 void CastWebView::DidFirstVisuallyNonEmptyPaint() { | |
235 metrics::CastMetricsHelper::GetInstance()->LogTimeToFirstPaint(); | |
236 } | |
237 | |
238 void CastWebView::DidStartNavigation( | |
239 content::NavigationHandle* navigation_handle) { | |
240 #if defined(USE_AURA) | |
241 // Resize window | |
242 gfx::Size display_size = | |
243 display::Screen::GetScreen()->GetPrimaryDisplay().size(); | |
244 aura::Window* content_window = web_contents()->GetNativeView(); | |
245 content_window->SetBounds( | |
246 gfx::Rect(display_size.width(), display_size.height())); | |
247 #endif | |
248 } | |
249 | |
250 void CastWebView::MediaStartedPlaying(const MediaPlayerInfo& media_info, | |
251 const MediaPlayerId& id) { | |
252 metrics::CastMetricsHelper::GetInstance()->LogMediaPlay(); | |
253 } | |
254 | |
255 void CastWebView::MediaStoppedPlaying(const MediaPlayerInfo& media_info, | |
256 const MediaPlayerId& id) { | |
257 metrics::CastMetricsHelper::GetInstance()->LogMediaPause(); | |
258 } | |
259 | |
260 } // namespace chromecast | |
OLD | NEW |