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