1// Copyright 2014 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/shell/browser/android/cast_window_android.h"
6
7#include "base/message_loop/message_loop_proxy.h"
8#include "base/path_service.h"
9#include "chromecast/shell/browser/android/cast_window_manager.h"
10#include "content/public/browser/devtools_agent_host.h"
11#include "content/public/browser/navigation_controller.h"
12#include "content/public/browser/navigation_entry.h"
13#include "content/public/browser/render_view_host.h"
14#include "content/public/common/renderer_preferences.h"
15#include "jni/CastWindowAndroid_jni.h"
16
17namespace chromecast {
18namespace shell {
19
20namespace {
21
22// The time (in milliseconds) we wait for after a page is closed (i.e.
23// after an app is stopped) before we delete the corresponding WebContents.
24const int kWebContentsDestructionDelayInMs = 50;
25
26}  // namespace
27
28// static
29bool CastWindowAndroid::RegisterJni(JNIEnv* env) {
30  return RegisterNativesImpl(env);
31}
32
33CastWindowAndroid::CastWindowAndroid(content::WebContents* web_contents)
34    : content::WebContentsObserver(web_contents),
35      weak_factory_(this) {
36}
37
38CastWindowAndroid::~CastWindowAndroid() {
39}
40
41// static
42CastWindowAndroid* CastWindowAndroid::CreateNewWindow(
43    content::BrowserContext* browser_context,
44    const GURL& url) {
45  content::WebContents::CreateParams create_params(browser_context);
46  create_params.routing_id = MSG_ROUTING_NONE;
47  content::WebContents* web_contents =
48      content::WebContents::Create(create_params);
49  CastWindowAndroid* shell = CreateCastWindowAndroid(
50      web_contents,
51      create_params.initial_size);
52  if (!url.is_empty())
53    shell->LoadURL(url);
54  return shell;
55}
56
57// static
58CastWindowAndroid* CastWindowAndroid::CreateCastWindowAndroid(
59    content::WebContents* web_contents,
60    const gfx::Size& initial_size) {
61  CastWindowAndroid* shell = new CastWindowAndroid(web_contents);
62
63  JNIEnv* env = base::android::AttachCurrentThread();
64  base::android::ScopedJavaLocalRef<jobject> shell_android(
65      CreateCastWindowView(shell));
66
67  shell->java_object_.Reset(env, shell_android.Release());
68  shell->web_contents_.reset(web_contents);
69  web_contents->SetDelegate(shell);
70
71  Java_CastWindowAndroid_initFromNativeWebContents(
72      env, shell->java_object_.obj(), reinterpret_cast<jint>(web_contents));
73
74  // Enabling hole-punching also requires runtime renderer preference
75  web_contents->GetMutableRendererPrefs()->
76      use_video_overlay_for_embedded_encrypted_video = true;
77  web_contents->GetRenderViewHost()->SyncRendererPrefs();
78
79  return shell;
80}
81
82void CastWindowAndroid::Close() {
83  // Close page first, which fires the window.unload event. The WebContents
84  // itself will be destroyed after browser-process has received renderer
85  // notification that the page is closed.
86  web_contents_->GetRenderViewHost()->ClosePage();
87}
88
89void CastWindowAndroid::Destroy() {
90  // Note: if multiple windows becomes supported, this may close other devtools
91  // sessions.
92  content::DevToolsAgentHost::DetachAllClients();
93  CloseCastWindowView(java_object_.obj());
94  delete this;
95}
96
97void CastWindowAndroid::LoadURL(const GURL& url) {
98  content::NavigationController::LoadURLParams params(url);
99  params.transition_type = ui::PageTransitionFromInt(
100      ui::PAGE_TRANSITION_TYPED |
101      ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
102  web_contents_->GetController().LoadURLWithParams(params);
103  web_contents_->Focus();
104}
105
106void CastWindowAndroid::AddNewContents(content::WebContents* source,
107                                       content::WebContents* new_contents,
108                                       WindowOpenDisposition disposition,
109                                       const gfx::Rect& initial_pos,
110                                       bool user_gesture,
111                                       bool* was_blocked) {
112  NOTIMPLEMENTED();
113  if (was_blocked) {
114    *was_blocked = true;
115  }
116}
117
118void CastWindowAndroid::CloseContents(content::WebContents* source) {
119  DCHECK_EQ(source, web_contents_.get());
120
121  // We need to delay the deletion of web_contents_ (currently for 50ms) to
122  // give (and guarantee) the renderer enough time to finish 'onunload'
123  // handler (but we don't want to wait any longer than that to delay the
124  // starting of next app).
125
126  if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType)) {
127    // When shutting down in a test context, the last remaining WebContents
128    // is torn down at browser-thread shutdown time. Call Destroy directly to
129    // avoid losing the last posted task to delete this object.
130    // TODO(gunsch): This could probably be avoided by using a
131    // CompletionCallback in StopCurrentApp to wait until the app is completely
132    // stopped. This might require a separate message loop and might only be
133    // appropriate for test contexts or during shutdown, since it triggers a
134    // wait on the main thread.
135    Destroy();
136    return;
137  }
138
139  base::MessageLoopProxy::current()->PostDelayedTask(
140      FROM_HERE,
141      base::Bind(&CastWindowAndroid::Destroy, weak_factory_.GetWeakPtr()),
142      base::TimeDelta::FromMilliseconds(kWebContentsDestructionDelayInMs));
143}
144
145bool CastWindowAndroid::CanOverscrollContent() const {
146  return false;
147}
148
149bool CastWindowAndroid::AddMessageToConsole(content::WebContents* source,
150                                            int32 level,
151                                            const base::string16& message,
152                                            int32 line_no,
153                                            const base::string16& source_id) {
154  return false;
155}
156
157void CastWindowAndroid::ActivateContents(content::WebContents* contents) {
158  DCHECK_EQ(contents, web_contents_.get());
159  contents->GetRenderViewHost()->Focus();
160}
161
162void CastWindowAndroid::DeactivateContents(content::WebContents* contents) {
163  DCHECK_EQ(contents, web_contents_.get());
164  contents->GetRenderViewHost()->Blur();
165}
166
167void CastWindowAndroid::RenderProcessGone(base::TerminationStatus status) {
168  LOG(ERROR) << "Render process gone: status=" << status;
169  Destroy();
170}
171
172}  // namespace shell
173}  // namespace chromecast
174