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.
5 #include "content/browser/web_contents/web_contents_android.h"
7 #include "base/android/jni_android.h"
8 #include "base/android/jni_array.h"
9 #include "base/android/jni_string.h"
10 #include "base/command_line.h"
11 #include "base/json/json_writer.h"
12 #include "base/logging.h"
13 #include "content/browser/accessibility/browser_accessibility_android.h"
14 #include "content/browser/accessibility/browser_accessibility_manager.h"
15 #include "content/browser/android/interstitial_page_delegate_android.h"
16 #include "content/browser/frame_host/interstitial_page_impl.h"
17 #include "content/browser/media/android/browser_media_player_manager.h"
18 #include "content/browser/media/media_web_contents_observer.h"
19 #include "content/browser/renderer_host/render_view_host_impl.h"
20 #include "content/browser/web_contents/web_contents_impl.h"
21 #include "content/common/devtools_messages.h"
22 #include "content/common/frame_messages.h"
23 #include "content/common/input_messages.h"
24 #include "content/common/view_messages.h"
25 #include "content/public/browser/browser_context.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "content/public/browser/web_contents.h"
28 #include "content/public/common/content_switches.h"
29 #include "jni/WebContentsImpl_jni.h"
30 #include "net/android/network_library.h"
31 #include "ui/accessibility/ax_node_data.h"
33 using base::android::AttachCurrentThread
;
34 using base::android::ConvertJavaStringToUTF8
;
35 using base::android::ConvertJavaStringToUTF16
;
36 using base::android::ConvertUTF8ToJavaString
;
37 using base::android::ConvertUTF16ToJavaString
;
38 using base::android::ScopedJavaGlobalRef
;
39 using base::android::ToJavaIntArray
;
45 void JavaScriptResultCallback(const ScopedJavaGlobalRef
<jobject
>& callback
,
46 const base::Value
* result
) {
47 JNIEnv
* env
= base::android::AttachCurrentThread();
49 base::JSONWriter::Write(result
, &json
);
50 ScopedJavaLocalRef
<jstring
> j_json
= ConvertUTF8ToJavaString(env
, json
);
51 Java_WebContentsImpl_onEvaluateJavaScriptResult(
52 env
, j_json
.obj(), callback
.obj());
55 ScopedJavaLocalRef
<jobject
> WalkAXTreeDepthFirst(JNIEnv
* env
,
56 BrowserAccessibilityAndroid
* node
) {
58 ScopedJavaLocalRef
<jstring
> j_text
=
59 ConvertUTF16ToJavaString(env
, node
->GetText());
60 ScopedJavaLocalRef
<jstring
> j_class
=
61 ConvertUTF8ToJavaString(env
, node
->GetClassName());
62 const gfx::Rect
& location
= node
->GetLocation();
63 ScopedJavaLocalRef
<jobject
> j_node
=
64 Java_WebContentsImpl_createAccessibilitySnapshotNode(env
,
65 location
.x(), location
.y(), node
->GetScrollX(),
66 node
->GetScrollY(), location
.width(), location
.height(),
67 j_text
.obj(), j_class
.obj());
69 for(uint32 i
= 0; i
< node
->PlatformChildCount(); i
++) {
70 BrowserAccessibilityAndroid
* child
=
71 static_cast<BrowserAccessibilityAndroid
*>(
72 node
->PlatformGetChild(i
));
73 Java_WebContentsImpl_addAccessibilityNodeAsChild(env
,
74 j_node
.obj(), WalkAXTreeDepthFirst(env
, child
).obj());
79 // Walks over the AXTreeUpdate and creates a light weight snapshot.
80 void AXTreeSnapshotCallback(const ScopedJavaGlobalRef
<jobject
>& callback
,
81 const ui::AXTreeUpdate
& result
) {
82 JNIEnv
* env
= base::android::AttachCurrentThread();
83 if (result
.nodes
.empty()) {
84 Java_WebContentsImpl_onAccessibilitySnapshot(env
, nullptr, callback
.obj());
87 scoped_ptr
<BrowserAccessibilityManager
> manager(
88 BrowserAccessibilityManager::Create(result
, nullptr));
89 BrowserAccessibilityAndroid
* root
=
90 static_cast<BrowserAccessibilityAndroid
*>(manager
->GetRoot());
91 ScopedJavaLocalRef
<jobject
> j_root
= WalkAXTreeDepthFirst(env
, root
);
92 Java_WebContentsImpl_onAccessibilitySnapshot(
93 env
, j_root
.obj(), callback
.obj());
96 void ReleaseAllMediaPlayers(WebContents
* web_contents
,
97 RenderFrameHost
* render_frame_host
) {
98 BrowserMediaPlayerManager
* manager
=
99 static_cast<WebContentsImpl
*>(web_contents
)->
100 media_web_contents_observer()->GetMediaPlayerManager(
103 manager
->ReleaseAllMediaPlayers();
109 WebContents
* WebContents::FromJavaWebContents(
110 jobject jweb_contents_android
) {
111 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
112 if (!jweb_contents_android
)
115 WebContentsAndroid
* web_contents_android
=
116 reinterpret_cast<WebContentsAndroid
*>(
117 Java_WebContentsImpl_getNativePointer(AttachCurrentThread(),
118 jweb_contents_android
));
119 if (!web_contents_android
)
121 return web_contents_android
->web_contents();
125 static void DestroyWebContents(JNIEnv
* env
,
127 jlong jweb_contents_android_ptr
) {
128 WebContentsAndroid
* web_contents_android
=
129 reinterpret_cast<WebContentsAndroid
*>(jweb_contents_android_ptr
);
130 if (!web_contents_android
)
133 WebContents
* web_contents
= web_contents_android
->web_contents();
141 bool WebContentsAndroid::Register(JNIEnv
* env
) {
142 return RegisterNativesImpl(env
);
145 WebContentsAndroid::WebContentsAndroid(WebContents
* web_contents
)
146 : web_contents_(web_contents
),
147 navigation_controller_(&(web_contents
->GetController())),
148 weak_factory_(this) {
149 JNIEnv
* env
= AttachCurrentThread();
151 Java_WebContentsImpl_create(
153 reinterpret_cast<intptr_t>(this),
154 navigation_controller_
.GetJavaObject().obj()).obj());
155 RendererPreferences
* prefs
= web_contents_
->GetMutableRendererPrefs();
156 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
157 prefs
->network_contry_iso
=
158 command_line
->HasSwitch(switches::kNetworkCountryIso
) ?
159 command_line
->GetSwitchValueASCII(switches::kNetworkCountryIso
)
160 : net::android::GetTelephonyNetworkCountryIso();
163 WebContentsAndroid::~WebContentsAndroid() {
164 Java_WebContentsImpl_clearNativePtr(AttachCurrentThread(), obj_
.obj());
167 base::android::ScopedJavaLocalRef
<jobject
>
168 WebContentsAndroid::GetJavaObject() {
169 return base::android::ScopedJavaLocalRef
<jobject
>(obj_
);
172 ScopedJavaLocalRef
<jstring
> WebContentsAndroid::GetTitle(
173 JNIEnv
* env
, jobject obj
) const {
174 return base::android::ConvertUTF16ToJavaString(env
,
175 web_contents_
->GetTitle());
178 ScopedJavaLocalRef
<jstring
> WebContentsAndroid::GetVisibleURL(
179 JNIEnv
* env
, jobject obj
) const {
180 return base::android::ConvertUTF8ToJavaString(
181 env
, web_contents_
->GetVisibleURL().spec());
184 bool WebContentsAndroid::IsLoading(JNIEnv
* env
, jobject obj
) const {
185 return web_contents_
->IsLoading();
188 bool WebContentsAndroid::IsLoadingToDifferentDocument(JNIEnv
* env
,
190 return web_contents_
->IsLoadingToDifferentDocument();
193 void WebContentsAndroid::Stop(JNIEnv
* env
, jobject obj
) {
194 web_contents_
->Stop();
197 void WebContentsAndroid::InsertCSS(
198 JNIEnv
* env
, jobject jobj
, jstring jcss
) {
199 web_contents_
->InsertCSS(base::android::ConvertJavaStringToUTF8(env
, jcss
));
202 RenderWidgetHostViewAndroid
*
203 WebContentsAndroid::GetRenderWidgetHostViewAndroid() {
204 RenderWidgetHostView
* rwhv
= NULL
;
205 rwhv
= web_contents_
->GetRenderWidgetHostView();
206 if (web_contents_
->ShowingInterstitialPage()) {
207 rwhv
= web_contents_
->GetInterstitialPage()
209 ->GetRenderViewHost()
212 return static_cast<RenderWidgetHostViewAndroid
*>(rwhv
);
215 jint
WebContentsAndroid::GetBackgroundColor(JNIEnv
* env
, jobject obj
) {
216 RenderWidgetHostViewAndroid
* rwhva
= GetRenderWidgetHostViewAndroid();
218 return SK_ColorWHITE
;
219 return rwhva
->GetCachedBackgroundColor();
222 ScopedJavaLocalRef
<jstring
> WebContentsAndroid::GetURL(JNIEnv
* env
,
224 return ConvertUTF8ToJavaString(env
, web_contents_
->GetURL().spec());
227 ScopedJavaLocalRef
<jstring
> WebContentsAndroid::GetLastCommittedURL(
230 return ConvertUTF8ToJavaString(env
,
231 web_contents_
->GetLastCommittedURL().spec());
235 jboolean
WebContentsAndroid::IsIncognito(JNIEnv
* env
, jobject obj
) {
236 return web_contents_
->GetBrowserContext()->IsOffTheRecord();
239 void WebContentsAndroid::ResumeResponseDeferredAtStart(JNIEnv
* env
,
241 static_cast<WebContentsImpl
*>(web_contents_
)->ResumeResponseDeferredAtStart();
244 void WebContentsAndroid::ResumeLoadingCreatedWebContents(JNIEnv
* env
,
246 web_contents_
->ResumeLoadingCreatedWebContents();
249 void WebContentsAndroid::SetHasPendingNavigationTransitionForTesting(
252 base::CommandLine::ForCurrentProcess()->AppendSwitch(
253 switches::kEnableExperimentalWebPlatformFeatures
);
254 RenderFrameHost
* frame
=
255 static_cast<WebContentsImpl
*>(web_contents_
)->GetMainFrame();
256 BrowserThread::PostTask(
260 &TransitionRequestManager::AddPendingTransitionRequestDataForTesting
,
261 base::Unretained(TransitionRequestManager::GetInstance()),
262 frame
->GetProcess()->GetID(),
263 frame
->GetRoutingID()));
266 void WebContentsAndroid::SetupTransitionView(JNIEnv
* env
,
269 web_contents_
->GetMainFrame()->Send(new FrameMsg_SetupTransitionView(
270 web_contents_
->GetMainFrame()->GetRoutingID(),
271 ConvertJavaStringToUTF8(env
, markup
)));
274 void WebContentsAndroid::BeginExitTransition(JNIEnv
* env
,
276 jstring css_selector
,
277 jboolean exit_to_native_app
) {
278 web_contents_
->GetMainFrame()->Send(new FrameMsg_BeginExitTransition(
279 web_contents_
->GetMainFrame()->GetRoutingID(),
280 ConvertJavaStringToUTF8(env
, css_selector
),
281 exit_to_native_app
));
284 void WebContentsAndroid::RevertExitTransition(JNIEnv
* env
,
286 web_contents_
->GetMainFrame()->Send(new FrameMsg_RevertExitTransition(
287 web_contents_
->GetMainFrame()->GetRoutingID()));
290 void WebContentsAndroid::HideTransitionElements(JNIEnv
* env
,
292 jstring css_selector
) {
293 web_contents_
->GetMainFrame()->Send(
294 new FrameMsg_HideTransitionElements(
295 web_contents_
->GetMainFrame()->GetRoutingID(),
296 ConvertJavaStringToUTF8(env
, css_selector
)));
299 void WebContentsAndroid::ShowTransitionElements(JNIEnv
* env
,
301 jstring css_selector
) {
302 web_contents_
->GetMainFrame()->Send(
303 new FrameMsg_ShowTransitionElements(
304 web_contents_
->GetMainFrame()->GetRoutingID(),
305 ConvertJavaStringToUTF8(env
, css_selector
)));
309 void WebContentsAndroid::ClearNavigationTransitionData(JNIEnv
* env
,
311 static_cast<WebContentsImpl
*>(web_contents_
)->ClearNavigationTransitionData();
314 void WebContentsAndroid::FetchTransitionElements(JNIEnv
* env
,
317 GURL
url(base::android::ConvertJavaStringToUTF8(env
, jurl
));
318 RenderFrameHost
* frame
= web_contents_
->GetMainFrame();
320 scoped_ptr
<TransitionLayerData
> transition_data(new TransitionLayerData());
321 BrowserThread::PostTaskAndReplyWithResult(
324 base::Bind(&TransitionRequestManager::GetPendingTransitionRequest
,
325 base::Unretained(TransitionRequestManager::GetInstance()),
326 frame
->GetProcess()->GetID(),
327 frame
->GetRoutingID(),
329 transition_data
.get()),
330 base::Bind(&WebContentsAndroid::OnTransitionElementsFetched
,
331 weak_factory_
.GetWeakPtr(),
332 base::Passed(&transition_data
)));
335 void WebContentsAndroid::OnTransitionElementsFetched(
336 scoped_ptr
<const TransitionLayerData
> transition_data
,
337 bool has_transition_data
) {
338 // FetchTransitionElements is called after the navigation transition state
339 // machine starts, which means there must be transition data.
340 DCHECK(has_transition_data
);
341 JNIEnv
* env
= AttachCurrentThread();
343 std::vector
<TransitionElement
>::const_iterator it
=
344 transition_data
->elements
.begin();
345 for (; it
!= transition_data
->elements
.end(); ++it
) {
346 ScopedJavaLocalRef
<jstring
> jstring_name(ConvertUTF8ToJavaString(env
,
348 Java_WebContentsImpl_addNavigationTransitionElements(
349 env
, obj_
.obj(), jstring_name
.obj(),
350 it
->rect
.x(), it
->rect
.y(), it
->rect
.width(), it
->rect
.height());
353 ScopedJavaLocalRef
<jstring
> jstring_css_selector(
354 ConvertUTF8ToJavaString(env
, transition_data
->css_selector
));
355 Java_WebContentsImpl_onTransitionElementsFetched(
356 env
, obj_
.obj(), jstring_css_selector
.obj());
359 void WebContentsAndroid::OnHide(JNIEnv
* env
, jobject obj
) {
360 web_contents_
->WasHidden();
363 void WebContentsAndroid::OnShow(JNIEnv
* env
, jobject obj
) {
364 web_contents_
->WasShown();
367 void WebContentsAndroid::ReleaseMediaPlayers(JNIEnv
* env
, jobject jobj
) {
368 #if defined(ENABLE_BROWSER_CDMS)
369 web_contents_
->ForEachFrame(
370 base::Bind(&ReleaseAllMediaPlayers
, base::Unretained(web_contents_
)));
371 #endif // defined(ENABLE_BROWSER_CDMS)
374 void WebContentsAndroid::AddStyleSheetByURL(
378 web_contents_
->GetMainFrame()->Send(new FrameMsg_AddStyleSheetByURL(
379 web_contents_
->GetMainFrame()->GetRoutingID(),
380 ConvertJavaStringToUTF8(env
, url
)));
383 void WebContentsAndroid::ShowInterstitialPage(
387 jlong delegate_ptr
) {
388 GURL
url(base::android::ConvertJavaStringToUTF8(env
, jurl
));
389 InterstitialPageDelegateAndroid
* delegate
=
390 reinterpret_cast<InterstitialPageDelegateAndroid
*>(delegate_ptr
);
391 InterstitialPage
* interstitial
= InterstitialPage::Create(
392 web_contents_
, false, url
, delegate
);
393 delegate
->set_interstitial_page(interstitial
);
394 interstitial
->Show();
397 jboolean
WebContentsAndroid::IsShowingInterstitialPage(JNIEnv
* env
,
399 return web_contents_
->ShowingInterstitialPage();
402 jboolean
WebContentsAndroid::IsRenderWidgetHostViewReady(
405 RenderWidgetHostViewAndroid
* view
= GetRenderWidgetHostViewAndroid();
406 return view
&& view
->HasValidFrame();
409 void WebContentsAndroid::ExitFullscreen(JNIEnv
* env
, jobject obj
) {
410 web_contents_
->ExitFullscreen();
413 void WebContentsAndroid::UpdateTopControlsState(
419 RenderViewHost
* host
= web_contents_
->GetRenderViewHost();
422 host
->Send(new ViewMsg_UpdateTopControlsState(host
->GetRoutingID(),
428 void WebContentsAndroid::ShowImeIfNeeded(JNIEnv
* env
, jobject obj
) {
429 RenderViewHost
* host
= web_contents_
->GetRenderViewHost();
432 host
->Send(new ViewMsg_ShowImeIfNeeded(host
->GetRoutingID()));
435 void WebContentsAndroid::ScrollFocusedEditableNodeIntoView(
438 RenderViewHost
* host
= web_contents_
->GetRenderViewHost();
441 host
->Send(new InputMsg_ScrollFocusedEditableNodeIntoRect(
442 host
->GetRoutingID(), gfx::Rect()));
445 void WebContentsAndroid::SelectWordAroundCaret(JNIEnv
* env
, jobject obj
) {
446 RenderViewHost
* host
= web_contents_
->GetRenderViewHost();
449 host
->SelectWordAroundCaret();
452 bool WebContentsAndroid::WillHandleDeferAfterResponseStarted() {
453 JNIEnv
* env
= AttachCurrentThread();
454 return Java_WebContentsImpl_willHandleDeferAfterResponseStarted(env
,
458 void WebContentsAndroid::DidDeferAfterResponseStarted(
459 const TransitionLayerData
& transition_data
) {
460 JNIEnv
* env
= AttachCurrentThread();
461 std::vector
<GURL
> entering_stylesheets
;
462 std::string transition_color
;
463 if (transition_data
.response_headers
.get()) {
464 TransitionRequestManager::ParseTransitionStylesheetsFromHeaders(
465 transition_data
.response_headers
,
466 entering_stylesheets
,
467 transition_data
.request_url
);
469 transition_data
.response_headers
->EnumerateHeader(
470 NULL
, "X-Transition-Entering-Color", &transition_color
);
473 ScopedJavaLocalRef
<jstring
> jstring_markup(
474 ConvertUTF8ToJavaString(env
, transition_data
.markup
));
476 ScopedJavaLocalRef
<jstring
> jstring_css_selector(
477 ConvertUTF8ToJavaString(env
, transition_data
.css_selector
));
479 ScopedJavaLocalRef
<jstring
> jstring_transition_color(
480 ConvertUTF8ToJavaString(env
, transition_color
));
482 Java_WebContentsImpl_didDeferAfterResponseStarted(
485 jstring_markup
.obj(),
486 jstring_css_selector
.obj(),
487 jstring_transition_color
.obj());
489 std::vector
<GURL
>::const_iterator iter
= entering_stylesheets
.begin();
490 for (; iter
!= entering_stylesheets
.end(); ++iter
) {
491 ScopedJavaLocalRef
<jstring
> jstring_url(
492 ConvertUTF8ToJavaString(env
, iter
->spec()));
493 Java_WebContentsImpl_addEnteringStylesheetToTransition(
494 env
, obj_
.obj(), jstring_url
.obj());
498 void WebContentsAndroid::DidStartNavigationTransitionForFrame(int64 frame_id
) {
499 JNIEnv
* env
= AttachCurrentThread();
500 Java_WebContentsImpl_didStartNavigationTransitionForFrame(
501 env
, obj_
.obj(), frame_id
);
504 void WebContentsAndroid::EvaluateJavaScript(JNIEnv
* env
,
508 RenderViewHost
* rvh
= web_contents_
->GetRenderViewHost();
511 if (!rvh
->IsRenderViewLive()) {
512 if (!static_cast<WebContentsImpl
*>(web_contents_
)->
513 CreateRenderViewForInitialEmptyDocument()) {
514 LOG(ERROR
) << "Failed to create RenderView in EvaluateJavaScript";
520 // No callback requested.
521 web_contents_
->GetMainFrame()->ExecuteJavaScript(
522 ConvertJavaStringToUTF16(env
, script
));
526 // Secure the Java callback in a scoped object and give ownership of it to the
528 ScopedJavaGlobalRef
<jobject
> j_callback
;
529 j_callback
.Reset(env
, callback
);
530 RenderFrameHost::JavaScriptResultCallback js_callback
=
531 base::Bind(&JavaScriptResultCallback
, j_callback
);
533 web_contents_
->GetMainFrame()->ExecuteJavaScript(
534 ConvertJavaStringToUTF16(env
, script
), js_callback
);
537 void WebContentsAndroid::AddMessageToDevToolsConsole(JNIEnv
* env
,
542 DCHECK_LE(level
, CONSOLE_MESSAGE_LEVEL_LAST
);
544 web_contents_
->GetMainFrame()->Send(new DevToolsAgentMsg_AddMessageToConsole(
545 web_contents_
->GetMainFrame()->GetRoutingID(),
546 static_cast<ConsoleMessageLevel
>(level
),
547 ConvertJavaStringToUTF8(env
, message
)));
550 jboolean
WebContentsAndroid::HasAccessedInitialDocument(
553 return static_cast<WebContentsImpl
*>(web_contents_
)->
554 HasAccessedInitialDocument();
557 jint
WebContentsAndroid::GetThemeColor(JNIEnv
* env
, jobject obj
) {
558 return web_contents_
->GetThemeColor();
561 void WebContentsAndroid::RequestAccessibilitySnapshot(JNIEnv
* env
,
564 // Secure the Java callback in a scoped object and give ownership of it to the
566 ScopedJavaGlobalRef
<jobject
> j_callback
;
567 j_callback
.Reset(env
, callback
);
568 WebContentsImpl::AXTreeSnapshotCallback snapshot_callback
=
569 base::Bind(&AXTreeSnapshotCallback
, j_callback
);
571 static_cast<WebContentsImpl
*>(web_contents_
)->RequestAXTreeSnapshot(
575 } // namespace content