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/containers/hash_tables.h"
12 #include "base/json/json_writer.h"
13 #include "base/lazy_instance.h"
14 #include "base/logging.h"
15 #include "content/browser/accessibility/browser_accessibility_android.h"
16 #include "content/browser/accessibility/browser_accessibility_manager_android.h"
17 #include "content/browser/android/content_view_core_impl.h"
18 #include "content/browser/android/interstitial_page_delegate_android.h"
19 #include "content/browser/frame_host/interstitial_page_impl.h"
20 #include "content/browser/media/android/browser_media_player_manager.h"
21 #include "content/browser/media/media_web_contents_observer.h"
22 #include "content/browser/renderer_host/render_view_host_impl.h"
23 #include "content/browser/web_contents/web_contents_impl.h"
24 #include "content/common/devtools_messages.h"
25 #include "content/common/frame_messages.h"
26 #include "content/common/input_messages.h"
27 #include "content/common/view_messages.h"
28 #include "content/public/browser/browser_context.h"
29 #include "content/public/browser/browser_thread.h"
30 #include "content/public/browser/message_port_provider.h"
31 #include "content/public/browser/web_contents.h"
32 #include "content/public/common/content_switches.h"
33 #include "jni/WebContentsImpl_jni.h"
34 #include "net/android/network_library.h"
35 #include "ui/accessibility/ax_node_data.h"
36 #include "ui/gfx/android/device_display_info.h"
38 using base::android::AttachCurrentThread
;
39 using base::android::ConvertJavaStringToUTF8
;
40 using base::android::ConvertJavaStringToUTF16
;
41 using base::android::ConvertUTF8ToJavaString
;
42 using base::android::ConvertUTF16ToJavaString
;
43 using base::android::ScopedJavaGlobalRef
;
44 using base::android::ToJavaIntArray
;
50 // Track all WebContentsAndroid objects here so that we don't deserialize a
51 // destroyed WebContents object.
52 base::LazyInstance
<base::hash_set
<WebContentsAndroid
*> >::Leaky
53 g_allocated_web_contents_androids
= LAZY_INSTANCE_INITIALIZER
;
55 void JavaScriptResultCallback(const ScopedJavaGlobalRef
<jobject
>& callback
,
56 const base::Value
* result
) {
57 JNIEnv
* env
= base::android::AttachCurrentThread();
59 base::JSONWriter::Write(*result
, &json
);
60 ScopedJavaLocalRef
<jstring
> j_json
= ConvertUTF8ToJavaString(env
, json
);
61 Java_WebContentsImpl_onEvaluateJavaScriptResult(
62 env
, j_json
.obj(), callback
.obj());
65 ScopedJavaLocalRef
<jobject
> WalkAXTreeDepthFirst(JNIEnv
* env
,
66 BrowserAccessibilityAndroid
* node
, float scale_factor
,
67 float y_offset
, float x_scroll
) {
68 ScopedJavaLocalRef
<jstring
> j_text
=
69 ConvertUTF16ToJavaString(env
, node
->GetText());
70 ScopedJavaLocalRef
<jstring
> j_class
=
71 ConvertUTF8ToJavaString(env
, node
->GetClassName());
72 const gfx::Rect
& location
= node
->GetLocalBoundsRect();
73 // The style attributes exists and valid if size attribute exists. Otherwise,
74 // they are not. Use a negative size information to indicate the existence
75 // of style information.
80 if (node
->HasFloatAttribute(ui::AX_ATTR_FONT_SIZE
)) {
81 color
= node
->GetIntAttribute(ui::AX_ATTR_COLOR
);
82 bgcolor
= node
->GetIntAttribute(ui::AX_ATTR_BACKGROUND_COLOR
);
83 size
= node
->GetFloatAttribute(ui::AX_ATTR_FONT_SIZE
);
84 text_style
= node
->GetIntAttribute(ui::AX_ATTR_TEXT_STYLE
);
86 ScopedJavaLocalRef
<jobject
> j_node
=
87 Java_WebContentsImpl_createAccessibilitySnapshotNode(env
,
88 scale_factor
* location
.x() - x_scroll
,
89 scale_factor
* location
.y() + y_offset
,
90 scale_factor
* node
->GetScrollX(), scale_factor
* node
->GetScrollY(),
91 scale_factor
* location
.width(), scale_factor
* location
.height(),
92 j_text
.obj(), color
, bgcolor
, scale_factor
* size
, text_style
,
95 for(uint32 i
= 0; i
< node
->PlatformChildCount(); i
++) {
96 BrowserAccessibilityAndroid
* child
=
97 static_cast<BrowserAccessibilityAndroid
*>(
98 node
->PlatformGetChild(i
));
99 Java_WebContentsImpl_addAccessibilityNodeAsChild(env
,
100 j_node
.obj(), WalkAXTreeDepthFirst(env
, child
, scale_factor
, y_offset
,
106 // Walks over the AXTreeUpdate and creates a light weight snapshot.
107 void AXTreeSnapshotCallback(const ScopedJavaGlobalRef
<jobject
>& callback
,
111 const ui::AXTreeUpdate
<ui::AXNodeData
>& result
) {
112 JNIEnv
* env
= base::android::AttachCurrentThread();
113 if (result
.nodes
.empty()) {
114 Java_WebContentsImpl_onAccessibilitySnapshot(env
, nullptr, callback
.obj());
117 scoped_ptr
<BrowserAccessibilityManagerAndroid
> manager(
118 static_cast<BrowserAccessibilityManagerAndroid
*>(
119 BrowserAccessibilityManager::Create(result
, nullptr)));
120 manager
->set_prune_tree_for_screen_reader(false);
121 BrowserAccessibilityAndroid
* root
=
122 static_cast<BrowserAccessibilityAndroid
*>(manager
->GetRoot());
123 ScopedJavaLocalRef
<jobject
> j_root
=
124 WalkAXTreeDepthFirst(env
, root
, scale_factor
, y_offset
, x_scroll
);
125 Java_WebContentsImpl_onAccessibilitySnapshot(
126 env
, j_root
.obj(), callback
.obj());
129 void ReleaseAllMediaPlayers(WebContents
* web_contents
,
130 RenderFrameHost
* render_frame_host
) {
131 BrowserMediaPlayerManager
* manager
=
132 static_cast<WebContentsImpl
*>(web_contents
)->
133 media_web_contents_observer()->GetMediaPlayerManager(
136 manager
->ReleaseAllMediaPlayers();
142 WebContents
* WebContents::FromJavaWebContents(
143 jobject jweb_contents_android
) {
144 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
145 if (!jweb_contents_android
)
148 WebContentsAndroid
* web_contents_android
=
149 reinterpret_cast<WebContentsAndroid
*>(
150 Java_WebContentsImpl_getNativePointer(AttachCurrentThread(),
151 jweb_contents_android
));
152 if (!web_contents_android
)
154 return web_contents_android
->web_contents();
158 static void DestroyWebContents(JNIEnv
* env
,
160 jlong jweb_contents_android_ptr
) {
161 WebContentsAndroid
* web_contents_android
=
162 reinterpret_cast<WebContentsAndroid
*>(jweb_contents_android_ptr
);
163 if (!web_contents_android
)
166 WebContents
* web_contents
= web_contents_android
->web_contents();
174 jobject
FromNativePtr(JNIEnv
* env
,
176 jlong web_contents_ptr
) {
177 WebContentsAndroid
* web_contents_android
=
178 reinterpret_cast<WebContentsAndroid
*>(web_contents_ptr
);
180 if (!web_contents_android
)
183 // Check to make sure this object hasn't been destroyed.
184 if (g_allocated_web_contents_androids
.Get().find(web_contents_android
) ==
185 g_allocated_web_contents_androids
.Get().end()) {
189 return web_contents_android
->GetJavaObject().Release();
193 bool WebContentsAndroid::Register(JNIEnv
* env
) {
194 return RegisterNativesImpl(env
);
197 WebContentsAndroid::WebContentsAndroid(WebContents
* web_contents
)
198 : web_contents_(web_contents
),
199 navigation_controller_(&(web_contents
->GetController())),
200 weak_factory_(this) {
201 g_allocated_web_contents_androids
.Get().insert(this);
202 JNIEnv
* env
= AttachCurrentThread();
204 Java_WebContentsImpl_create(
206 reinterpret_cast<intptr_t>(this),
207 navigation_controller_
.GetJavaObject().obj()).obj());
208 RendererPreferences
* prefs
= web_contents_
->GetMutableRendererPrefs();
209 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
210 prefs
->network_contry_iso
=
211 command_line
->HasSwitch(switches::kNetworkCountryIso
) ?
212 command_line
->GetSwitchValueASCII(switches::kNetworkCountryIso
)
213 : net::android::GetTelephonyNetworkCountryIso();
216 WebContentsAndroid::~WebContentsAndroid() {
217 DCHECK(g_allocated_web_contents_androids
.Get().find(this) !=
218 g_allocated_web_contents_androids
.Get().end());
219 g_allocated_web_contents_androids
.Get().erase(this);
220 Java_WebContentsImpl_clearNativePtr(AttachCurrentThread(), obj_
.obj());
223 base::android::ScopedJavaLocalRef
<jobject
>
224 WebContentsAndroid::GetJavaObject() {
225 return base::android::ScopedJavaLocalRef
<jobject
>(obj_
);
228 ScopedJavaLocalRef
<jstring
> WebContentsAndroid::GetTitle(
229 JNIEnv
* env
, jobject obj
) const {
230 return base::android::ConvertUTF16ToJavaString(env
,
231 web_contents_
->GetTitle());
234 ScopedJavaLocalRef
<jstring
> WebContentsAndroid::GetVisibleURL(
235 JNIEnv
* env
, jobject obj
) const {
236 return base::android::ConvertUTF8ToJavaString(
237 env
, web_contents_
->GetVisibleURL().spec());
240 bool WebContentsAndroid::IsLoading(JNIEnv
* env
, jobject obj
) const {
241 return web_contents_
->IsLoading();
244 bool WebContentsAndroid::IsLoadingToDifferentDocument(JNIEnv
* env
,
246 return web_contents_
->IsLoadingToDifferentDocument();
249 void WebContentsAndroid::Stop(JNIEnv
* env
, jobject obj
) {
250 web_contents_
->Stop();
253 void WebContentsAndroid::Cut(JNIEnv
* env
, jobject obj
) {
254 web_contents_
->Cut();
257 void WebContentsAndroid::Copy(JNIEnv
* env
, jobject obj
) {
258 web_contents_
->Copy();
261 void WebContentsAndroid::Paste(JNIEnv
* env
, jobject obj
) {
262 web_contents_
->Paste();
265 void WebContentsAndroid::SelectAll(JNIEnv
* env
, jobject obj
) {
266 web_contents_
->SelectAll();
269 void WebContentsAndroid::Unselect(JNIEnv
* env
, jobject obj
) {
270 web_contents_
->Unselect();
273 void WebContentsAndroid::InsertCSS(
274 JNIEnv
* env
, jobject jobj
, jstring jcss
) {
275 web_contents_
->InsertCSS(base::android::ConvertJavaStringToUTF8(env
, jcss
));
278 RenderWidgetHostViewAndroid
*
279 WebContentsAndroid::GetRenderWidgetHostViewAndroid() {
280 RenderWidgetHostView
* rwhv
= NULL
;
281 rwhv
= web_contents_
->GetRenderWidgetHostView();
282 if (web_contents_
->ShowingInterstitialPage()) {
283 rwhv
= web_contents_
->GetInterstitialPage()
285 ->GetRenderViewHost()
288 return static_cast<RenderWidgetHostViewAndroid
*>(rwhv
);
291 jint
WebContentsAndroid::GetBackgroundColor(JNIEnv
* env
, jobject obj
) {
292 RenderWidgetHostViewAndroid
* rwhva
= GetRenderWidgetHostViewAndroid();
294 return SK_ColorWHITE
;
295 return rwhva
->GetCachedBackgroundColor();
298 ScopedJavaLocalRef
<jstring
> WebContentsAndroid::GetURL(JNIEnv
* env
,
300 return ConvertUTF8ToJavaString(env
, web_contents_
->GetURL().spec());
303 ScopedJavaLocalRef
<jstring
> WebContentsAndroid::GetLastCommittedURL(
306 return ConvertUTF8ToJavaString(env
,
307 web_contents_
->GetLastCommittedURL().spec());
311 jboolean
WebContentsAndroid::IsIncognito(JNIEnv
* env
, jobject obj
) {
312 return web_contents_
->GetBrowserContext()->IsOffTheRecord();
315 void WebContentsAndroid::ResumeLoadingCreatedWebContents(JNIEnv
* env
,
317 web_contents_
->ResumeLoadingCreatedWebContents();
320 void WebContentsAndroid::OnHide(JNIEnv
* env
, jobject obj
) {
321 web_contents_
->WasHidden();
324 void WebContentsAndroid::OnShow(JNIEnv
* env
, jobject obj
) {
325 web_contents_
->WasShown();
328 void WebContentsAndroid::ReleaseMediaPlayers(JNIEnv
* env
, jobject jobj
) {
329 #if defined(ENABLE_BROWSER_CDMS)
330 web_contents_
->ForEachFrame(
331 base::Bind(&ReleaseAllMediaPlayers
, base::Unretained(web_contents_
)));
332 #endif // defined(ENABLE_BROWSER_CDMS)
335 void WebContentsAndroid::ShowInterstitialPage(
339 jlong delegate_ptr
) {
340 GURL
url(base::android::ConvertJavaStringToUTF8(env
, jurl
));
341 InterstitialPageDelegateAndroid
* delegate
=
342 reinterpret_cast<InterstitialPageDelegateAndroid
*>(delegate_ptr
);
343 InterstitialPage
* interstitial
= InterstitialPage::Create(
344 web_contents_
, false, url
, delegate
);
345 delegate
->set_interstitial_page(interstitial
);
346 interstitial
->Show();
349 jboolean
WebContentsAndroid::IsShowingInterstitialPage(JNIEnv
* env
,
351 return web_contents_
->ShowingInterstitialPage();
354 jboolean
WebContentsAndroid::IsRenderWidgetHostViewReady(
357 RenderWidgetHostViewAndroid
* view
= GetRenderWidgetHostViewAndroid();
358 return view
&& view
->HasValidFrame();
361 void WebContentsAndroid::ExitFullscreen(JNIEnv
* env
, jobject obj
) {
362 web_contents_
->ExitFullscreen();
365 void WebContentsAndroid::UpdateTopControlsState(
371 RenderViewHost
* host
= web_contents_
->GetRenderViewHost();
374 host
->Send(new ViewMsg_UpdateTopControlsState(host
->GetRoutingID(),
380 void WebContentsAndroid::ShowImeIfNeeded(JNIEnv
* env
, jobject obj
) {
381 RenderViewHost
* host
= web_contents_
->GetRenderViewHost();
384 host
->Send(new ViewMsg_ShowImeIfNeeded(host
->GetRoutingID()));
387 void WebContentsAndroid::ScrollFocusedEditableNodeIntoView(
390 RenderViewHost
* host
= web_contents_
->GetRenderViewHost();
393 host
->Send(new InputMsg_ScrollFocusedEditableNodeIntoRect(
394 host
->GetRoutingID(), gfx::Rect()));
397 void WebContentsAndroid::SelectWordAroundCaret(JNIEnv
* env
, jobject obj
) {
398 RenderViewHost
* host
= web_contents_
->GetRenderViewHost();
401 host
->SelectWordAroundCaret();
404 void WebContentsAndroid::AdjustSelectionByCharacterOffset(JNIEnv
* env
,
408 web_contents_
->AdjustSelectionByCharacterOffset(start_adjust
, end_adjust
);
411 void WebContentsAndroid::EvaluateJavaScript(JNIEnv
* env
,
415 RenderViewHost
* rvh
= web_contents_
->GetRenderViewHost();
418 if (!rvh
->IsRenderViewLive()) {
419 if (!static_cast<WebContentsImpl
*>(web_contents_
)->
420 CreateRenderViewForInitialEmptyDocument()) {
421 LOG(ERROR
) << "Failed to create RenderView in EvaluateJavaScript";
427 // No callback requested.
428 web_contents_
->GetMainFrame()->ExecuteJavaScript(
429 ConvertJavaStringToUTF16(env
, script
));
433 // Secure the Java callback in a scoped object and give ownership of it to the
435 ScopedJavaGlobalRef
<jobject
> j_callback
;
436 j_callback
.Reset(env
, callback
);
437 RenderFrameHost::JavaScriptResultCallback js_callback
=
438 base::Bind(&JavaScriptResultCallback
, j_callback
);
440 web_contents_
->GetMainFrame()->ExecuteJavaScript(
441 ConvertJavaStringToUTF16(env
, script
), js_callback
);
444 void WebContentsAndroid::EvaluateJavaScriptForTests(JNIEnv
* env
,
448 RenderViewHost
* rvh
= web_contents_
->GetRenderViewHost();
451 if (!rvh
->IsRenderViewLive()) {
452 if (!static_cast<WebContentsImpl
*>(web_contents_
)->
453 CreateRenderViewForInitialEmptyDocument()) {
454 LOG(ERROR
) << "Failed to create RenderView in EvaluateJavaScriptForTests";
460 // No callback requested.
461 web_contents_
->GetMainFrame()->ExecuteJavaScriptForTests(
462 ConvertJavaStringToUTF16(env
, script
));
466 // Secure the Java callback in a scoped object and give ownership of it to the
468 ScopedJavaGlobalRef
<jobject
> j_callback
;
469 j_callback
.Reset(env
, callback
);
470 RenderFrameHost::JavaScriptResultCallback js_callback
=
471 base::Bind(&JavaScriptResultCallback
, j_callback
);
473 web_contents_
->GetMainFrame()->ExecuteJavaScriptForTests(
474 ConvertJavaStringToUTF16(env
, script
), js_callback
);
477 void WebContentsAndroid::AddMessageToDevToolsConsole(JNIEnv
* env
,
482 DCHECK_LE(level
, CONSOLE_MESSAGE_LEVEL_LAST
);
484 web_contents_
->GetMainFrame()->AddMessageToConsole(
485 static_cast<ConsoleMessageLevel
>(level
),
486 ConvertJavaStringToUTF8(env
, message
));
489 void WebContentsAndroid::SendMessageToFrame(JNIEnv
* env
,
493 jstring target_origin
) {
494 base::string16 source_origin
;
495 base::string16
j_target_origin(ConvertJavaStringToUTF16(env
, target_origin
));
496 base::string16
j_message(ConvertJavaStringToUTF16(env
, message
));
497 std::vector
<content::TransferredMessagePort
> ports
;
498 content::MessagePortProvider::PostMessageToFrame(
499 web_contents_
, source_origin
, j_target_origin
, j_message
, ports
);
502 jboolean
WebContentsAndroid::HasAccessedInitialDocument(
505 return static_cast<WebContentsImpl
*>(web_contents_
)->
506 HasAccessedInitialDocument();
509 jint
WebContentsAndroid::GetThemeColor(JNIEnv
* env
, jobject obj
) {
510 return web_contents_
->GetThemeColor();
513 void WebContentsAndroid::RequestAccessibilitySnapshot(JNIEnv
* env
,
518 // Secure the Java callback in a scoped object and give ownership of it to the
520 ScopedJavaGlobalRef
<jobject
> j_callback
;
521 j_callback
.Reset(env
, callback
);
522 gfx::DeviceDisplayInfo device_info
;
523 ContentViewCoreImpl
* contentViewCore
=
524 ContentViewCoreImpl::FromWebContents(web_contents_
);
525 WebContentsImpl::AXTreeSnapshotCallback snapshot_callback
=
526 base::Bind(&AXTreeSnapshotCallback
, j_callback
,
527 contentViewCore
->GetScaleFactor(), y_offset
, x_scroll
);
528 static_cast<WebContentsImpl
*>(web_contents_
)->RequestAXTreeSnapshot(
532 void WebContentsAndroid::ResumeMediaSession(JNIEnv
* env
, jobject obj
) {
533 web_contents_
->ResumeMediaSession();
536 void WebContentsAndroid::SuspendMediaSession(JNIEnv
* env
, jobject obj
) {
537 web_contents_
->SuspendMediaSession();
540 ScopedJavaLocalRef
<jstring
> WebContentsAndroid::GetEncoding(
541 JNIEnv
* env
, jobject obj
) const {
542 return base::android::ConvertUTF8ToJavaString(env
,
543 web_contents_
->GetEncoding());
546 } // namespace content