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/web_contents.h"
31 #include "content/public/common/content_switches.h"
32 #include "jni/WebContentsImpl_jni.h"
33 #include "net/android/network_library.h"
34 #include "ui/accessibility/ax_node_data.h"
35 #include "ui/gfx/android/device_display_info.h"
37 using base::android::AttachCurrentThread
;
38 using base::android::ConvertJavaStringToUTF8
;
39 using base::android::ConvertJavaStringToUTF16
;
40 using base::android::ConvertUTF8ToJavaString
;
41 using base::android::ConvertUTF16ToJavaString
;
42 using base::android::ScopedJavaGlobalRef
;
43 using base::android::ToJavaIntArray
;
49 // Track all WebContentsAndroid objects here so that we don't deserialize a
50 // destroyed WebContents object.
51 base::LazyInstance
<base::hash_set
<WebContentsAndroid
*> >::Leaky
52 g_allocated_web_contents_androids
= LAZY_INSTANCE_INITIALIZER
;
54 void JavaScriptResultCallback(const ScopedJavaGlobalRef
<jobject
>& callback
,
55 const base::Value
* result
) {
56 JNIEnv
* env
= base::android::AttachCurrentThread();
58 base::JSONWriter::Write(*result
, &json
);
59 ScopedJavaLocalRef
<jstring
> j_json
= ConvertUTF8ToJavaString(env
, json
);
60 Java_WebContentsImpl_onEvaluateJavaScriptResult(
61 env
, j_json
.obj(), callback
.obj());
64 ScopedJavaLocalRef
<jobject
> WalkAXTreeDepthFirst(JNIEnv
* env
,
65 BrowserAccessibilityAndroid
* node
, float scale_factor
,
66 float y_offset
, float x_scroll
) {
67 ScopedJavaLocalRef
<jstring
> j_text
=
68 ConvertUTF16ToJavaString(env
, node
->GetText());
69 ScopedJavaLocalRef
<jstring
> j_class
=
70 ConvertUTF8ToJavaString(env
, node
->GetClassName());
71 const gfx::Rect
& location
= node
->GetLocalBoundsRect();
72 // The style attributes exists and valid if size attribute exists. Otherwise,
73 // they are not. Use a negative size information to indicate the existence
74 // of style information.
79 if (node
->HasFloatAttribute(ui::AX_ATTR_FONT_SIZE
)) {
80 color
= node
->GetIntAttribute(ui::AX_ATTR_COLOR
);
81 bgcolor
= node
->GetIntAttribute(ui::AX_ATTR_BACKGROUND_COLOR
);
82 size
= node
->GetFloatAttribute(ui::AX_ATTR_FONT_SIZE
);
83 text_style
= node
->GetIntAttribute(ui::AX_ATTR_TEXT_STYLE
);
85 ScopedJavaLocalRef
<jobject
> j_node
=
86 Java_WebContentsImpl_createAccessibilitySnapshotNode(env
,
87 scale_factor
* location
.x() - x_scroll
,
88 scale_factor
* location
.y() + y_offset
,
89 scale_factor
* node
->GetScrollX(), scale_factor
* node
->GetScrollY(),
90 scale_factor
* location
.width(), scale_factor
* location
.height(),
91 j_text
.obj(), color
, bgcolor
, scale_factor
* size
, text_style
,
94 for(uint32 i
= 0; i
< node
->PlatformChildCount(); i
++) {
95 BrowserAccessibilityAndroid
* child
=
96 static_cast<BrowserAccessibilityAndroid
*>(
97 node
->PlatformGetChild(i
));
98 Java_WebContentsImpl_addAccessibilityNodeAsChild(env
,
99 j_node
.obj(), WalkAXTreeDepthFirst(env
, child
, scale_factor
, y_offset
,
105 // Walks over the AXTreeUpdate and creates a light weight snapshot.
106 void AXTreeSnapshotCallback(const ScopedJavaGlobalRef
<jobject
>& callback
,
110 const ui::AXTreeUpdate
& result
) {
111 JNIEnv
* env
= base::android::AttachCurrentThread();
112 if (result
.nodes
.empty()) {
113 Java_WebContentsImpl_onAccessibilitySnapshot(env
, nullptr, callback
.obj());
116 scoped_ptr
<BrowserAccessibilityManagerAndroid
> manager(
117 static_cast<BrowserAccessibilityManagerAndroid
*>(
118 BrowserAccessibilityManager::Create(result
, nullptr)));
119 manager
->set_prune_tree_for_screen_reader(false);
120 BrowserAccessibilityAndroid
* root
=
121 static_cast<BrowserAccessibilityAndroid
*>(manager
->GetRoot());
122 ScopedJavaLocalRef
<jobject
> j_root
=
123 WalkAXTreeDepthFirst(env
, root
, scale_factor
, y_offset
, x_scroll
);
124 Java_WebContentsImpl_onAccessibilitySnapshot(
125 env
, j_root
.obj(), callback
.obj());
128 void ReleaseAllMediaPlayers(WebContents
* web_contents
,
129 RenderFrameHost
* render_frame_host
) {
130 BrowserMediaPlayerManager
* manager
=
131 static_cast<WebContentsImpl
*>(web_contents
)->
132 media_web_contents_observer()->GetMediaPlayerManager(
135 manager
->ReleaseAllMediaPlayers();
141 WebContents
* WebContents::FromJavaWebContents(
142 jobject jweb_contents_android
) {
143 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
144 if (!jweb_contents_android
)
147 WebContentsAndroid
* web_contents_android
=
148 reinterpret_cast<WebContentsAndroid
*>(
149 Java_WebContentsImpl_getNativePointer(AttachCurrentThread(),
150 jweb_contents_android
));
151 if (!web_contents_android
)
153 return web_contents_android
->web_contents();
157 static void DestroyWebContents(JNIEnv
* env
,
159 jlong jweb_contents_android_ptr
) {
160 WebContentsAndroid
* web_contents_android
=
161 reinterpret_cast<WebContentsAndroid
*>(jweb_contents_android_ptr
);
162 if (!web_contents_android
)
165 WebContents
* web_contents
= web_contents_android
->web_contents();
173 jobject
FromNativePtr(JNIEnv
* env
,
175 jlong web_contents_ptr
) {
176 WebContentsAndroid
* web_contents_android
=
177 reinterpret_cast<WebContentsAndroid
*>(web_contents_ptr
);
179 if (!web_contents_android
)
182 // Check to make sure this object hasn't been destroyed.
183 if (g_allocated_web_contents_androids
.Get().find(web_contents_android
) ==
184 g_allocated_web_contents_androids
.Get().end()) {
188 return web_contents_android
->GetJavaObject().Release();
192 bool WebContentsAndroid::Register(JNIEnv
* env
) {
193 return RegisterNativesImpl(env
);
196 WebContentsAndroid::WebContentsAndroid(WebContents
* web_contents
)
197 : web_contents_(web_contents
),
198 navigation_controller_(&(web_contents
->GetController())),
199 weak_factory_(this) {
200 g_allocated_web_contents_androids
.Get().insert(this);
201 JNIEnv
* env
= AttachCurrentThread();
203 Java_WebContentsImpl_create(
205 reinterpret_cast<intptr_t>(this),
206 navigation_controller_
.GetJavaObject().obj()).obj());
207 RendererPreferences
* prefs
= web_contents_
->GetMutableRendererPrefs();
208 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
209 prefs
->network_contry_iso
=
210 command_line
->HasSwitch(switches::kNetworkCountryIso
) ?
211 command_line
->GetSwitchValueASCII(switches::kNetworkCountryIso
)
212 : net::android::GetTelephonyNetworkCountryIso();
215 WebContentsAndroid::~WebContentsAndroid() {
216 DCHECK(g_allocated_web_contents_androids
.Get().find(this) !=
217 g_allocated_web_contents_androids
.Get().end());
218 g_allocated_web_contents_androids
.Get().erase(this);
219 Java_WebContentsImpl_clearNativePtr(AttachCurrentThread(), obj_
.obj());
222 base::android::ScopedJavaLocalRef
<jobject
>
223 WebContentsAndroid::GetJavaObject() {
224 return base::android::ScopedJavaLocalRef
<jobject
>(obj_
);
227 ScopedJavaLocalRef
<jstring
> WebContentsAndroid::GetTitle(
228 JNIEnv
* env
, jobject obj
) const {
229 return base::android::ConvertUTF16ToJavaString(env
,
230 web_contents_
->GetTitle());
233 ScopedJavaLocalRef
<jstring
> WebContentsAndroid::GetVisibleURL(
234 JNIEnv
* env
, jobject obj
) const {
235 return base::android::ConvertUTF8ToJavaString(
236 env
, web_contents_
->GetVisibleURL().spec());
239 bool WebContentsAndroid::IsLoading(JNIEnv
* env
, jobject obj
) const {
240 return web_contents_
->IsLoading();
243 bool WebContentsAndroid::IsLoadingToDifferentDocument(JNIEnv
* env
,
245 return web_contents_
->IsLoadingToDifferentDocument();
248 void WebContentsAndroid::Stop(JNIEnv
* env
, jobject obj
) {
249 web_contents_
->Stop();
252 void WebContentsAndroid::Cut(JNIEnv
* env
, jobject obj
) {
253 web_contents_
->Cut();
256 void WebContentsAndroid::Copy(JNIEnv
* env
, jobject obj
) {
257 web_contents_
->Copy();
260 void WebContentsAndroid::Paste(JNIEnv
* env
, jobject obj
) {
261 web_contents_
->Paste();
264 void WebContentsAndroid::SelectAll(JNIEnv
* env
, jobject obj
) {
265 web_contents_
->SelectAll();
268 void WebContentsAndroid::Unselect(JNIEnv
* env
, jobject obj
) {
269 web_contents_
->Unselect();
272 void WebContentsAndroid::InsertCSS(
273 JNIEnv
* env
, jobject jobj
, jstring jcss
) {
274 web_contents_
->InsertCSS(base::android::ConvertJavaStringToUTF8(env
, jcss
));
277 RenderWidgetHostViewAndroid
*
278 WebContentsAndroid::GetRenderWidgetHostViewAndroid() {
279 RenderWidgetHostView
* rwhv
= NULL
;
280 rwhv
= web_contents_
->GetRenderWidgetHostView();
281 if (web_contents_
->ShowingInterstitialPage()) {
282 rwhv
= web_contents_
->GetInterstitialPage()
284 ->GetRenderViewHost()
287 return static_cast<RenderWidgetHostViewAndroid
*>(rwhv
);
290 jint
WebContentsAndroid::GetBackgroundColor(JNIEnv
* env
, jobject obj
) {
291 RenderWidgetHostViewAndroid
* rwhva
= GetRenderWidgetHostViewAndroid();
293 return SK_ColorWHITE
;
294 return rwhva
->GetCachedBackgroundColor();
297 ScopedJavaLocalRef
<jstring
> WebContentsAndroid::GetURL(JNIEnv
* env
,
299 return ConvertUTF8ToJavaString(env
, web_contents_
->GetURL().spec());
302 ScopedJavaLocalRef
<jstring
> WebContentsAndroid::GetLastCommittedURL(
305 return ConvertUTF8ToJavaString(env
,
306 web_contents_
->GetLastCommittedURL().spec());
310 jboolean
WebContentsAndroid::IsIncognito(JNIEnv
* env
, jobject obj
) {
311 return web_contents_
->GetBrowserContext()->IsOffTheRecord();
314 void WebContentsAndroid::ResumeLoadingCreatedWebContents(JNIEnv
* env
,
316 web_contents_
->ResumeLoadingCreatedWebContents();
319 void WebContentsAndroid::OnHide(JNIEnv
* env
, jobject obj
) {
320 web_contents_
->WasHidden();
323 void WebContentsAndroid::OnShow(JNIEnv
* env
, jobject obj
) {
324 web_contents_
->WasShown();
327 void WebContentsAndroid::ReleaseMediaPlayers(JNIEnv
* env
, jobject jobj
) {
328 #if defined(ENABLE_BROWSER_CDMS)
329 web_contents_
->ForEachFrame(
330 base::Bind(&ReleaseAllMediaPlayers
, base::Unretained(web_contents_
)));
331 #endif // defined(ENABLE_BROWSER_CDMS)
334 void WebContentsAndroid::ShowInterstitialPage(
338 jlong delegate_ptr
) {
339 GURL
url(base::android::ConvertJavaStringToUTF8(env
, jurl
));
340 InterstitialPageDelegateAndroid
* delegate
=
341 reinterpret_cast<InterstitialPageDelegateAndroid
*>(delegate_ptr
);
342 InterstitialPage
* interstitial
= InterstitialPage::Create(
343 web_contents_
, false, url
, delegate
);
344 delegate
->set_interstitial_page(interstitial
);
345 interstitial
->Show();
348 jboolean
WebContentsAndroid::IsShowingInterstitialPage(JNIEnv
* env
,
350 return web_contents_
->ShowingInterstitialPage();
353 jboolean
WebContentsAndroid::IsRenderWidgetHostViewReady(
356 RenderWidgetHostViewAndroid
* view
= GetRenderWidgetHostViewAndroid();
357 return view
&& view
->HasValidFrame();
360 void WebContentsAndroid::ExitFullscreen(JNIEnv
* env
, jobject obj
) {
361 web_contents_
->ExitFullscreen();
364 void WebContentsAndroid::UpdateTopControlsState(
370 RenderViewHost
* host
= web_contents_
->GetRenderViewHost();
373 host
->Send(new ViewMsg_UpdateTopControlsState(host
->GetRoutingID(),
379 void WebContentsAndroid::ShowImeIfNeeded(JNIEnv
* env
, jobject obj
) {
380 RenderViewHost
* host
= web_contents_
->GetRenderViewHost();
383 host
->Send(new ViewMsg_ShowImeIfNeeded(host
->GetRoutingID()));
386 void WebContentsAndroid::ScrollFocusedEditableNodeIntoView(
389 RenderViewHost
* host
= web_contents_
->GetRenderViewHost();
392 host
->Send(new InputMsg_ScrollFocusedEditableNodeIntoRect(
393 host
->GetRoutingID(), gfx::Rect()));
396 void WebContentsAndroid::SelectWordAroundCaret(JNIEnv
* env
, jobject obj
) {
397 RenderViewHost
* host
= web_contents_
->GetRenderViewHost();
400 host
->SelectWordAroundCaret();
403 void WebContentsAndroid::AdjustSelectionByCharacterOffset(JNIEnv
* env
,
407 web_contents_
->AdjustSelectionByCharacterOffset(start_adjust
, end_adjust
);
410 void WebContentsAndroid::EvaluateJavaScript(JNIEnv
* env
,
414 RenderViewHost
* rvh
= web_contents_
->GetRenderViewHost();
417 if (!rvh
->IsRenderViewLive()) {
418 if (!static_cast<WebContentsImpl
*>(web_contents_
)->
419 CreateRenderViewForInitialEmptyDocument()) {
420 LOG(ERROR
) << "Failed to create RenderView in EvaluateJavaScript";
426 // No callback requested.
427 web_contents_
->GetMainFrame()->ExecuteJavaScript(
428 ConvertJavaStringToUTF16(env
, script
));
432 // Secure the Java callback in a scoped object and give ownership of it to the
434 ScopedJavaGlobalRef
<jobject
> j_callback
;
435 j_callback
.Reset(env
, callback
);
436 RenderFrameHost::JavaScriptResultCallback js_callback
=
437 base::Bind(&JavaScriptResultCallback
, j_callback
);
439 web_contents_
->GetMainFrame()->ExecuteJavaScript(
440 ConvertJavaStringToUTF16(env
, script
), js_callback
);
443 void WebContentsAndroid::EvaluateJavaScriptForTests(JNIEnv
* env
,
447 RenderViewHost
* rvh
= web_contents_
->GetRenderViewHost();
450 if (!rvh
->IsRenderViewLive()) {
451 if (!static_cast<WebContentsImpl
*>(web_contents_
)->
452 CreateRenderViewForInitialEmptyDocument()) {
453 LOG(ERROR
) << "Failed to create RenderView in EvaluateJavaScriptForTests";
459 // No callback requested.
460 web_contents_
->GetMainFrame()->ExecuteJavaScriptForTests(
461 ConvertJavaStringToUTF16(env
, script
));
465 // Secure the Java callback in a scoped object and give ownership of it to the
467 ScopedJavaGlobalRef
<jobject
> j_callback
;
468 j_callback
.Reset(env
, callback
);
469 RenderFrameHost::JavaScriptResultCallback js_callback
=
470 base::Bind(&JavaScriptResultCallback
, j_callback
);
472 web_contents_
->GetMainFrame()->ExecuteJavaScriptForTests(
473 ConvertJavaStringToUTF16(env
, script
), js_callback
);
476 void WebContentsAndroid::AddMessageToDevToolsConsole(JNIEnv
* env
,
481 DCHECK_LE(level
, CONSOLE_MESSAGE_LEVEL_LAST
);
483 web_contents_
->GetMainFrame()->AddMessageToConsole(
484 static_cast<ConsoleMessageLevel
>(level
),
485 ConvertJavaStringToUTF8(env
, message
));
488 jboolean
WebContentsAndroid::HasAccessedInitialDocument(
491 return static_cast<WebContentsImpl
*>(web_contents_
)->
492 HasAccessedInitialDocument();
495 jint
WebContentsAndroid::GetThemeColor(JNIEnv
* env
, jobject obj
) {
496 return web_contents_
->GetThemeColor();
499 void WebContentsAndroid::RequestAccessibilitySnapshot(JNIEnv
* env
,
504 // Secure the Java callback in a scoped object and give ownership of it to the
506 ScopedJavaGlobalRef
<jobject
> j_callback
;
507 j_callback
.Reset(env
, callback
);
508 gfx::DeviceDisplayInfo device_info
;
509 ContentViewCoreImpl
* contentViewCore
=
510 ContentViewCoreImpl::FromWebContents(web_contents_
);
511 WebContentsImpl::AXTreeSnapshotCallback snapshot_callback
=
512 base::Bind(&AXTreeSnapshotCallback
, j_callback
,
513 contentViewCore
->GetScaleFactor(), y_offset
, x_scroll
);
514 static_cast<WebContentsImpl
*>(web_contents_
)->RequestAXTreeSnapshot(
518 void WebContentsAndroid::ResumeMediaSession(JNIEnv
* env
, jobject obj
) {
519 web_contents_
->ResumeMediaSession();
522 void WebContentsAndroid::SuspendMediaSession(JNIEnv
* env
, jobject obj
) {
523 web_contents_
->SuspendMediaSession();
526 } // namespace content