Chromium Code Reviews| Index: chromecast/browser/cast_web_view.cc |
| diff --git a/chromecast/browser/cast_web_view.cc b/chromecast/browser/cast_web_view.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..2259a2bbe65aed10e189a6d4be04cc901d934351 |
| --- /dev/null |
| +++ b/chromecast/browser/cast_web_view.cc |
| @@ -0,0 +1,232 @@ |
| +// Copyright 2017 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. |
| + |
| +#include "chromecast/browser/cast_web_view.h" |
| + |
| +#include "base/logging.h" |
| +#include "base/threading/thread_task_runner_handle.h" |
| +#include "chromecast/base/metrics/cast_metrics_helper.h" |
| +#include "content/public/browser/render_frame_host.h" |
| +#include "content/public/browser/render_view_host.h" |
| +#include "content/public/browser/render_widget_host.h" |
| +#include "content/public/browser/render_widget_host_view.h" |
| +#include "ipc/ipc_message.h" |
| +#include "net/base/net_errors.h" |
| +#include "ui/display/display.h" |
| +#include "ui/display/screen.h" |
| +#include "url/gurl.h" |
| + |
| +#if defined(OS_ANDROID) |
| +#include "chromecast/browser/android/cast_web_contents_activity.h" |
| +#endif // defined(OS_ANDROID) |
| + |
| +#if defined(USE_AURA) |
| +#include "ui/aura/window.h" |
| +#endif |
| + |
| +namespace chromecast { |
| + |
| +namespace { |
| +// The time (in milliseconds) we wait for after a page is closed (i.e. |
| +// after an app is stopped) before we delete the corresponding WebContents. |
| +constexpr int kWebContentsDestructionDelayInMs = 50; |
|
alokp
2017/01/19 05:26:36
this seems like a hack. How did you arrive at this
halliwell
2017/01/19 17:54:30
This is long-standing behaviour of Cast. Note tha
|
| + |
| +std::unique_ptr<content::WebContents> CreateWebContents( |
| + content::BrowserContext* browser_context) { |
| + CHECK(display::Screen::GetScreen()); |
| + gfx::Size display_size = |
| + display::Screen::GetScreen()->GetPrimaryDisplay().size(); |
| + |
| + content::WebContents::CreateParams create_params(browser_context, NULL); |
| + create_params.routing_id = MSG_ROUTING_NONE; |
| + create_params.initial_size = display_size; |
| + content::WebContents* web_contents = |
| + content::WebContents::Create(create_params); |
| + |
| +#if defined(USE_AURA) |
| + // Resize window |
| + aura::Window* content_window = web_contents->GetNativeView(); |
| + content_window->SetBounds( |
| + gfx::Rect(display_size.width(), display_size.height())); |
| +#endif |
| + |
| +#if defined(OS_ANDROID) |
| + content::RendererPreferences* prefs = web_contents->GetMutableRendererPrefs(); |
| + prefs->use_video_overlay_for_embedded_encrypted_video = true; |
| + web_contents->GetRenderViewHost()->SyncRendererPrefs(); |
| +#endif |
| + |
| + return base::WrapUnique(web_contents); |
| +} |
| + |
| +} // namespace |
| + |
| +CastWebView::CastWebView(Delegate* delegate, |
| + content::BrowserContext* browser_context, |
| + GURL url, |
| + bool transparent) |
| + : delegate_(delegate), |
| + browser_context_(browser_context), |
| + transparent_(transparent), |
| + window_(shell::CastContentWindow::Create(delegate)), |
| + web_contents_(CreateWebContents(browser_context_)), |
| + weak_factory_(this) { |
| + DCHECK(delegate_); |
| + DCHECK(browser_context_); |
| + DCHECK(window_); |
| + content::WebContentsObserver::Observe(web_contents_.get()); |
| + web_contents_->SetDelegate(this); |
| + |
| + if (transparent_) |
| + window_->SetTransparent(); |
| + |
| + web_contents_->GetController().LoadURL( |
| + url, content::Referrer(), ui::PAGE_TRANSITION_TYPED, ""); |
| +} |
| + |
| +CastWebView::~CastWebView() {} |
| + |
| +void CastWebView::ClosePage() { |
| + content::WebContentsObserver::Observe(nullptr); |
| + web_contents_->ClosePage(); |
| +} |
| + |
| +void CastWebView::CloseContents(content::WebContents* source) { |
|
alokp
2017/01/19 05:26:36
nit: please define functions in the same order as
derekjchow1
2017/01/19 21:59:16
Done (regarding reordering).
Although this functi
|
| + DCHECK_EQ(source, web_contents_.get()); |
| + |
| + // We need to delay the deletion of web_contents_ (currently for 50ms) to |
| + // give (and guarantee) the renderer enough time to finish 'onunload' |
| + // handler (but we don't want to wait any longer than that to delay the |
| + // starting of next app). |
| + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| + FROM_HERE, base::Bind(&CastWebView::DelayedCloseContents, |
|
alokp
2017/01/19 05:26:36
Hmm. I do not understand this code. You do not sta
derekjchow1
2017/01/19 21:59:16
I added an additional comment. This function gets
|
| + weak_factory_.GetWeakPtr()), |
| + base::TimeDelta::FromMilliseconds(kWebContentsDestructionDelayInMs)); |
| +} |
| + |
| +void CastWebView::DelayedCloseContents() { |
| + // Delete the WebContents object here so that the gfx surface will be |
| + // deleted as part of destroying RenderWidgetHostViewCast object. |
| + // We want to delete the surface before we start the next app because |
| + // the next app could be an external one whose Start() function would |
| + // destroy the primary gfx plane. |
| + web_contents_.reset(); |
| + delegate_->OnPageStopped(net::OK); |
| +} |
| + |
| +void CastWebView::Show() { |
| + window_->ShowWebContents(web_contents_.get()); |
| + web_contents_->Focus(); |
| +} |
| + |
| +content::WebContents* CastWebView::OpenURLFromTab( |
| + content::WebContents* source, |
| + const content::OpenURLParams& params) { |
| + LOG(INFO) << "Change url: " << params.url; |
| + // If source is NULL which means current tab, use web_contents_ of this class. |
| + if (!source) |
| + source = web_contents_.get(); |
| + DCHECK_EQ(source, web_contents_.get()); |
| + // We don't want to create another web_contents. Load url only when source is |
| + // specified. |
| + if (source) { |
| + source->GetController().LoadURL(params.url, params.referrer, |
| + params.transition, params.extra_headers); |
| + } |
| + return source; |
| +} |
| + |
| +void CastWebView::LoadingStateChanged(content::WebContents* source, |
| + bool to_different_document) { |
| + delegate_->OnLoadingStateChanged(source->IsLoading()); |
| +} |
| + |
| +void CastWebView::ActivateContents(content::WebContents* contents) { |
| + DCHECK_EQ(contents, web_contents_.get()); |
| + contents->GetRenderViewHost()->GetWidget()->Focus(); |
| +} |
| + |
| +#if defined(OS_ANDROID) |
| +base::android::ScopedJavaLocalRef<jobject> |
| +CastWebView::GetContentVideoViewEmbedder() { |
| + DCHECK(web_contents_); |
| + auto activity = shell::CastWebContentsActivity::Get(web_contents_.get()); |
| + return activity->GetContentVideoViewEmbedder(); |
| +} |
| +#endif // defined(OS_ANDROID) |
| + |
| +void CastWebView::RenderProcessGone(base::TerminationStatus status) { |
| + LOG(INFO) << "APP_ERROR_CHILD_PROCESS_CRASHED"; |
| + delegate_->OnPageStopped(net::ERR_UNEXPECTED); |
| +} |
| + |
| +void CastWebView::RenderViewCreated(content::RenderViewHost* render_view_host) { |
| + content::RenderWidgetHostView* view = |
| + render_view_host->GetWidget()->GetView(); |
| + if (view) { |
| + view->SetBackgroundColor(transparent_ ? SK_ColorTRANSPARENT |
| + : SK_ColorBLACK); |
| + } |
| +} |
| + |
| +void CastWebView::DidFailProvisionalLoad( |
| + content::RenderFrameHost* render_frame_host, |
| + const GURL& validated_url, |
| + int error_code, |
| + const base::string16& error_description, |
| + bool was_ignored_by_handler) { |
| + // If we abort errors in an iframe, it can create a really confusing |
| + // and fragile user experience. Rather than create a list of errors |
| + // that are most likely to occur, we ignore all of them for now. |
| + if (render_frame_host->GetParent()) { |
| + LOG(ERROR) << "Got error on sub-iframe: url=" |
| + << validated_url.spec() << ", error=" << error_code; |
| + return; |
| + } |
| + |
| + LOG(ERROR) << "Got error on provisional load: url=" << validated_url.spec() |
| + << ", error_code=" << error_code |
| + << "description=" << error_description; |
| + delegate_->OnPageStopped(error_code); |
| +} |
| + |
| +void CastWebView::DidFailLoad(content::RenderFrameHost* render_frame_host, |
| + const GURL& validated_url, |
| + int error_code, |
| + const base::string16& error_description, |
| + bool was_ignored_by_handler) { |
| + // Only report an error if we are the main frame. See b/8433611. |
| + if (render_frame_host->GetParent()) { |
| + LOG(ERROR) << "Got error on sub-iframe: url=" |
| + << validated_url.spec() << ", error=" << error_code; |
| + } else if (error_code == net::ERR_ABORTED) { |
| + // ERR_ABORTED means download was aborted by the app, typically this happens |
| + // when flinging URL for direct playback, the initial URLRequest gets |
| + // cancelled/aborted and then the same URL is requested via the buffered |
| + // data source for media::Pipeline playback. |
| + LOG(INFO) << "Load canceled: url=" << validated_url.spec(); |
| + } else { |
| + LOG(ERROR) << "Got error on load: url=" << validated_url.spec() |
| + << ", error_code=" << error_code; |
| + delegate_->OnPageStopped(error_code); |
| + } |
| +} |
| + |
| +void CastWebView::DidFirstVisuallyNonEmptyPaint() { |
| + metrics::CastMetricsHelper::GetInstance()->LogTimeToFirstPaint(); |
| +} |
| + |
| +void CastWebView::MediaStartedPlaying( |
| + const MediaPlayerInfo& media_info, |
| + const MediaPlayerId& id) { |
| + metrics::CastMetricsHelper::GetInstance()->LogMediaPlay(); |
| +} |
| + |
| +void CastWebView::MediaStoppedPlaying( |
| + const MediaPlayerInfo& media_info, |
| + const MediaPlayerId& id) { |
| + metrics::CastMetricsHelper::GetInstance()->LogMediaPause(); |
| +} |
| + |
| +} // namespace chromecast |