1 // Copyright 2013 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 "content/browser/web_contents/web_contents_android.h"
6
7 #include "base/android/jni_android.h"
8 #include "base/android/jni_string.h"
9 #include "base/command_line.h"
10 #include "base/json/json_writer.h"
11 #include "base/logging.h"
12 #include "content/browser/android/interstitial_page_delegate_android.h"
13 #include "content/browser/frame_host/interstitial_page_impl.h"
14 #include "content/browser/media/android/browser_media_player_manager.h"
15 #include "content/browser/media/media_web_contents_observer.h"
16 #include "content/browser/renderer_host/render_view_host_impl.h"
17 #include "content/browser/web_contents/web_contents_impl.h"
18 #include "content/common/frame_messages.h"
19 #include "content/common/input_messages.h"
20 #include "content/common/view_messages.h"
21 #include "content/public/browser/browser_context.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/web_contents.h"
24 #include "content/public/common/content_switches.h"
25 #include "jni/WebContentsImpl_jni.h"
26
27 using base::android::AttachCurrentThread;
28 using base::android::ConvertJavaStringToUTF8;
29 using base::android::ConvertJavaStringToUTF16;
30 using base::android::ConvertUTF8ToJavaString;
31 using base::android::ScopedJavaGlobalRef;
32
33 namespace {
34
JavaScriptResultCallback(const ScopedJavaGlobalRef<jobject> & callback,const base::Value * result)35 void JavaScriptResultCallback(const ScopedJavaGlobalRef<jobject>& callback,
36 const base::Value* result) {
37 JNIEnv* env = base::android::AttachCurrentThread();
38 std::string json;
39 base::JSONWriter::Write(result, &json);
40 ScopedJavaLocalRef<jstring> j_json = ConvertUTF8ToJavaString(env, json);
41 content::Java_WebContentsImpl_onEvaluateJavaScriptResult(
42 env, j_json.obj(), callback.obj());
43 }
44
45 } // namespace
46
47 namespace content {
48
49 // static
FromJavaWebContents(jobject jweb_contents_android)50 WebContents* WebContents::FromJavaWebContents(
51 jobject jweb_contents_android) {
52 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
53 if (!jweb_contents_android)
54 return NULL;
55
56 WebContentsAndroid* web_contents_android =
57 reinterpret_cast<WebContentsAndroid*>(
58 Java_WebContentsImpl_getNativePointer(AttachCurrentThread(),
59 jweb_contents_android));
60 if (!web_contents_android)
61 return NULL;
62 return web_contents_android->web_contents();
63 }
64
65 // static
Register(JNIEnv * env)66 bool WebContentsAndroid::Register(JNIEnv* env) {
67 return RegisterNativesImpl(env);
68 }
69
WebContentsAndroid(WebContents * web_contents)70 WebContentsAndroid::WebContentsAndroid(WebContents* web_contents)
71 : web_contents_(web_contents),
72 navigation_controller_(&(web_contents->GetController())) {
73 JNIEnv* env = AttachCurrentThread();
74 obj_.Reset(env,
75 Java_WebContentsImpl_create(
76 env,
77 reinterpret_cast<intptr_t>(this),
78 navigation_controller_.GetJavaObject().obj()).obj());
79 }
80
~WebContentsAndroid()81 WebContentsAndroid::~WebContentsAndroid() {
82 Java_WebContentsImpl_destroy(AttachCurrentThread(), obj_.obj());
83 }
84
85 base::android::ScopedJavaLocalRef<jobject>
GetJavaObject()86 WebContentsAndroid::GetJavaObject() {
87 return base::android::ScopedJavaLocalRef<jobject>(obj_);
88 }
89
GetTitle(JNIEnv * env,jobject obj) const90 ScopedJavaLocalRef<jstring> WebContentsAndroid::GetTitle(
91 JNIEnv* env, jobject obj) const {
92 return base::android::ConvertUTF16ToJavaString(env,
93 web_contents_->GetTitle());
94 }
95
GetVisibleURL(JNIEnv * env,jobject obj) const96 ScopedJavaLocalRef<jstring> WebContentsAndroid::GetVisibleURL(
97 JNIEnv* env, jobject obj) const {
98 return base::android::ConvertUTF8ToJavaString(
99 env, web_contents_->GetVisibleURL().spec());
100 }
101
Stop(JNIEnv * env,jobject obj)102 void WebContentsAndroid::Stop(JNIEnv* env, jobject obj) {
103 web_contents_->Stop();
104 }
105
InsertCSS(JNIEnv * env,jobject jobj,jstring jcss)106 void WebContentsAndroid::InsertCSS(
107 JNIEnv* env, jobject jobj, jstring jcss) {
108 web_contents_->InsertCSS(base::android::ConvertJavaStringToUTF8(env, jcss));
109 }
110
111 RenderWidgetHostViewAndroid*
GetRenderWidgetHostViewAndroid()112 WebContentsAndroid::GetRenderWidgetHostViewAndroid() {
113 RenderWidgetHostView* rwhv = NULL;
114 rwhv = web_contents_->GetRenderWidgetHostView();
115 if (web_contents_->ShowingInterstitialPage()) {
116 rwhv = static_cast<InterstitialPageImpl*>(
117 web_contents_->GetInterstitialPage())->
118 GetRenderViewHost()->GetView();
119 }
120 return static_cast<RenderWidgetHostViewAndroid*>(rwhv);
121 }
122
GetBackgroundColor(JNIEnv * env,jobject obj)123 jint WebContentsAndroid::GetBackgroundColor(JNIEnv* env, jobject obj) {
124 RenderWidgetHostViewAndroid* rwhva = GetRenderWidgetHostViewAndroid();
125 if (!rwhva)
126 return SK_ColorWHITE;
127 return rwhva->GetCachedBackgroundColor();
128 }
129
GetURL(JNIEnv * env,jobject obj) const130 ScopedJavaLocalRef<jstring> WebContentsAndroid::GetURL(JNIEnv* env,
131 jobject obj) const {
132 return ConvertUTF8ToJavaString(env, web_contents_->GetURL().spec());
133 }
134
IsIncognito(JNIEnv * env,jobject obj)135 jboolean WebContentsAndroid::IsIncognito(JNIEnv* env, jobject obj) {
136 return web_contents_->GetBrowserContext()->IsOffTheRecord();
137 }
138
ResumeResponseDeferredAtStart(JNIEnv * env,jobject obj)139 void WebContentsAndroid::ResumeResponseDeferredAtStart(JNIEnv* env,
140 jobject obj) {
141 static_cast<WebContentsImpl*>(web_contents_)->ResumeResponseDeferredAtStart();
142 }
143
SetHasPendingNavigationTransitionForTesting(JNIEnv * env,jobject obj)144 void WebContentsAndroid::SetHasPendingNavigationTransitionForTesting(
145 JNIEnv* env,
146 jobject obj) {
147 CommandLine::ForCurrentProcess()->AppendSwitch(
148 switches::kEnableExperimentalWebPlatformFeatures);
149 RenderFrameHost* frame =
150 static_cast<WebContentsImpl*>(web_contents_)->GetMainFrame();
151 BrowserThread::PostTask(
152 BrowserThread::IO,
153 FROM_HERE,
154 base::Bind(&TransitionRequestManager::AddPendingTransitionRequestData,
155 base::Unretained(TransitionRequestManager::GetInstance()),
156 frame->GetProcess()->GetID(),
157 frame->GetRoutingID(),
158 "*",
159 "",
160 ""));
161 }
162
SetupTransitionView(JNIEnv * env,jobject jobj,jstring markup)163 void WebContentsAndroid::SetupTransitionView(JNIEnv* env,
164 jobject jobj,
165 jstring markup) {
166 web_contents_->GetMainFrame()->Send(new FrameMsg_SetupTransitionView(
167 web_contents_->GetMainFrame()->GetRoutingID(),
168 ConvertJavaStringToUTF8(env, markup)));
169 }
170
BeginExitTransition(JNIEnv * env,jobject jobj,jstring css_selector)171 void WebContentsAndroid::BeginExitTransition(JNIEnv* env,
172 jobject jobj,
173 jstring css_selector) {
174 web_contents_->GetMainFrame()->Send(new FrameMsg_BeginExitTransition(
175 web_contents_->GetMainFrame()->GetRoutingID(),
176 ConvertJavaStringToUTF8(env, css_selector)));
177 }
178
OnHide(JNIEnv * env,jobject obj)179 void WebContentsAndroid::OnHide(JNIEnv* env, jobject obj) {
180 web_contents_->WasHidden();
181 }
182
OnShow(JNIEnv * env,jobject obj)183 void WebContentsAndroid::OnShow(JNIEnv* env, jobject obj) {
184 web_contents_->WasShown();
185 }
186
ReleaseMediaPlayers(JNIEnv * env,jobject jobj)187 void WebContentsAndroid::ReleaseMediaPlayers(JNIEnv* env, jobject jobj) {
188 #if defined(ENABLE_BROWSER_CDMS)
189 RenderViewHostImpl* rvhi = static_cast<RenderViewHostImpl*>(
190 web_contents_->GetRenderViewHost());
191 if (!rvhi || !rvhi->GetMainFrame())
192 return;
193
194 BrowserMediaPlayerManager* manager =
195 rvhi->media_web_contents_observer()->GetMediaPlayerManager(
196 rvhi->GetMainFrame());
197 if (manager)
198 manager->ReleaseAllMediaPlayers();
199 #endif // defined(ENABLE_BROWSER_CDMS)
200 }
201
AddStyleSheetByURL(JNIEnv * env,jobject obj,jstring url)202 void WebContentsAndroid::AddStyleSheetByURL(
203 JNIEnv* env,
204 jobject obj,
205 jstring url) {
206 web_contents_->GetMainFrame()->Send(new FrameMsg_AddStyleSheetByURL(
207 web_contents_->GetMainFrame()->GetRoutingID(),
208 ConvertJavaStringToUTF8(env, url)));
209 }
210
ShowInterstitialPage(JNIEnv * env,jobject obj,jstring jurl,jlong delegate_ptr)211 void WebContentsAndroid::ShowInterstitialPage(
212 JNIEnv* env,
213 jobject obj,
214 jstring jurl,
215 jlong delegate_ptr) {
216 GURL url(base::android::ConvertJavaStringToUTF8(env, jurl));
217 InterstitialPageDelegateAndroid* delegate =
218 reinterpret_cast<InterstitialPageDelegateAndroid*>(delegate_ptr);
219 InterstitialPage* interstitial = InterstitialPage::Create(
220 web_contents_, false, url, delegate);
221 delegate->set_interstitial_page(interstitial);
222 interstitial->Show();
223 }
224
IsShowingInterstitialPage(JNIEnv * env,jobject obj)225 jboolean WebContentsAndroid::IsShowingInterstitialPage(JNIEnv* env,
226 jobject obj) {
227 return web_contents_->ShowingInterstitialPage();
228 }
229
IsRenderWidgetHostViewReady(JNIEnv * env,jobject obj)230 jboolean WebContentsAndroid::IsRenderWidgetHostViewReady(
231 JNIEnv* env,
232 jobject obj) {
233 RenderWidgetHostViewAndroid* view = GetRenderWidgetHostViewAndroid();
234 return view && view->HasValidFrame();
235 }
236
ExitFullscreen(JNIEnv * env,jobject obj)237 void WebContentsAndroid::ExitFullscreen(JNIEnv* env, jobject obj) {
238 RenderViewHost* host = web_contents_->GetRenderViewHost();
239 if (!host)
240 return;
241 host->ExitFullscreen();
242 }
243
UpdateTopControlsState(JNIEnv * env,jobject obj,bool enable_hiding,bool enable_showing,bool animate)244 void WebContentsAndroid::UpdateTopControlsState(
245 JNIEnv* env,
246 jobject obj,
247 bool enable_hiding,
248 bool enable_showing,
249 bool animate) {
250 RenderViewHost* host = web_contents_->GetRenderViewHost();
251 if (!host)
252 return;
253 host->Send(new ViewMsg_UpdateTopControlsState(host->GetRoutingID(),
254 enable_hiding,
255 enable_showing,
256 animate));
257 }
258
ShowImeIfNeeded(JNIEnv * env,jobject obj)259 void WebContentsAndroid::ShowImeIfNeeded(JNIEnv* env, jobject obj) {
260 RenderViewHost* host = web_contents_->GetRenderViewHost();
261 if (!host)
262 return;
263 host->Send(new ViewMsg_ShowImeIfNeeded(host->GetRoutingID()));
264 }
265
ScrollFocusedEditableNodeIntoView(JNIEnv * env,jobject obj)266 void WebContentsAndroid::ScrollFocusedEditableNodeIntoView(
267 JNIEnv* env,
268 jobject obj) {
269 RenderViewHost* host = web_contents_->GetRenderViewHost();
270 if (!host)
271 return;
272 host->Send(new InputMsg_ScrollFocusedEditableNodeIntoRect(
273 host->GetRoutingID(), gfx::Rect()));
274 }
275
SelectWordAroundCaret(JNIEnv * env,jobject obj)276 void WebContentsAndroid::SelectWordAroundCaret(JNIEnv* env, jobject obj) {
277 RenderViewHost* host = web_contents_->GetRenderViewHost();
278 if (!host)
279 return;
280 host->SelectWordAroundCaret();
281 }
282
WillHandleDeferAfterResponseStarted()283 bool WebContentsAndroid::WillHandleDeferAfterResponseStarted() {
284 JNIEnv* env = AttachCurrentThread();
285 return Java_WebContentsImpl_willHandleDeferAfterResponseStarted(env,
286 obj_.obj());
287 }
288
DidDeferAfterResponseStarted(const TransitionLayerData & transition_data)289 void WebContentsAndroid::DidDeferAfterResponseStarted(
290 const TransitionLayerData& transition_data) {
291 JNIEnv* env = AttachCurrentThread();
292 std::vector<GURL> entering_stylesheets;
293 std::string transition_color;
294 if (transition_data.response_headers) {
295 TransitionRequestManager::ParseTransitionStylesheetsFromHeaders(
296 transition_data.response_headers,
297 entering_stylesheets,
298 transition_data.request_url);
299
300 transition_data.response_headers->EnumerateHeader(
301 NULL, "X-Transition-Entering-Color", &transition_color);
302 }
303
304 ScopedJavaLocalRef<jstring> jstring_markup(
305 ConvertUTF8ToJavaString(env, transition_data.markup));
306
307 ScopedJavaLocalRef<jstring> jstring_css_selector(
308 ConvertUTF8ToJavaString(env, transition_data.css_selector));
309
310 ScopedJavaLocalRef<jstring> jstring_transition_color(
311 ConvertUTF8ToJavaString(env, transition_color));
312
313 Java_WebContentsImpl_didDeferAfterResponseStarted(
314 env,
315 obj_.obj(),
316 jstring_markup.obj(),
317 jstring_css_selector.obj(),
318 jstring_transition_color.obj());
319
320 std::vector<GURL>::const_iterator iter = entering_stylesheets.begin();
321 for (; iter != entering_stylesheets.end(); ++iter) {
322 ScopedJavaLocalRef<jstring> jstring_url(
323 ConvertUTF8ToJavaString(env, iter->spec()));
324 Java_WebContentsImpl_addEnteringStylesheetToTransition(
325 env, obj_.obj(), jstring_url.obj());
326 }
327 }
328
DidStartNavigationTransitionForFrame(int64 frame_id)329 void WebContentsAndroid::DidStartNavigationTransitionForFrame(int64 frame_id) {
330 JNIEnv* env = AttachCurrentThread();
331 Java_WebContentsImpl_didStartNavigationTransitionForFrame(
332 env, obj_.obj(), frame_id);
333 }
334
EvaluateJavaScript(JNIEnv * env,jobject obj,jstring script,jobject callback)335 void WebContentsAndroid::EvaluateJavaScript(JNIEnv* env,
336 jobject obj,
337 jstring script,
338 jobject callback) {
339 RenderViewHost* rvh = web_contents_->GetRenderViewHost();
340 DCHECK(rvh);
341
342 if (!rvh->IsRenderViewLive()) {
343 if (!static_cast<WebContentsImpl*>(web_contents_)->
344 CreateRenderViewForInitialEmptyDocument()) {
345 LOG(ERROR) << "Failed to create RenderView in EvaluateJavaScript";
346 return;
347 }
348 }
349
350 if (!callback) {
351 // No callback requested.
352 web_contents_->GetMainFrame()->ExecuteJavaScript(
353 ConvertJavaStringToUTF16(env, script));
354 return;
355 }
356
357 // Secure the Java callback in a scoped object and give ownership of it to the
358 // base::Callback.
359 ScopedJavaGlobalRef<jobject> j_callback;
360 j_callback.Reset(env, callback);
361 content::RenderFrameHost::JavaScriptResultCallback js_callback =
362 base::Bind(&JavaScriptResultCallback, j_callback);
363
364 web_contents_->GetMainFrame()->ExecuteJavaScript(
365 ConvertJavaStringToUTF16(env, script), js_callback);
366 }
367
368 } // namespace content
369