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.h"
17 #include "content/browser/android/interstitial_page_delegate_android.h"
18 #include "content/browser/frame_host/interstitial_page_impl.h"
19 #include "content/browser/media/android/browser_media_player_manager.h"
20 #include "content/browser/media/media_web_contents_observer.h"
21 #include "content/browser/renderer_host/render_view_host_impl.h"
22 #include "content/browser/web_contents/web_contents_impl.h"
23 #include "content/common/devtools_messages.h"
24 #include "content/common/frame_messages.h"
25 #include "content/common/input_messages.h"
26 #include "content/common/view_messages.h"
27 #include "content/public/browser/browser_context.h"
28 #include "content/public/browser/browser_thread.h"
29 #include "content/public/browser/web_contents.h"
30 #include "content/public/common/content_switches.h"
31 #include "jni/WebContentsImpl_jni.h"
32 #include "net/android/network_library.h"
33 #include "ui/accessibility/ax_node_data.h"
35 using base::android::AttachCurrentThread
;
36 using base::android::ConvertJavaStringToUTF8
;
37 using base::android::ConvertJavaStringToUTF16
;
38 using base::android::ConvertUTF8ToJavaString
;
39 using base::android::ConvertUTF16ToJavaString
;
40 using base::android::ScopedJavaGlobalRef
;
41 using base::android::ToJavaIntArray
;
47 // Track all WebContentsAndroid objects here so that we don't deserialize a
48 // destroyed WebContents object.
49 base::LazyInstance
<base::hash_set
<WebContentsAndroid
*> >::Leaky
50 g_allocated_web_contents_androids
= LAZY_INSTANCE_INITIALIZER
;
52 void JavaScriptResultCallback(const ScopedJavaGlobalRef
<jobject
>& callback
,
53 const base::Value
* result
) {
54 JNIEnv
* env
= base::android::AttachCurrentThread();
56 base::JSONWriter::Write(*result
, &json
);
57 ScopedJavaLocalRef
<jstring
> j_json
= ConvertUTF8ToJavaString(env
, json
);
58 Java_WebContentsImpl_onEvaluateJavaScriptResult(
59 env
, j_json
.obj(), callback
.obj());
62 ScopedJavaLocalRef
<jobject
> WalkAXTreeDepthFirst(JNIEnv
* env
,
63 BrowserAccessibilityAndroid
* node
) {
65 ScopedJavaLocalRef
<jstring
> j_text
=
66 ConvertUTF16ToJavaString(env
, node
->GetText());
67 ScopedJavaLocalRef
<jstring
> j_class
=
68 ConvertUTF8ToJavaString(env
, node
->GetClassName());
69 const gfx::Rect
& location
= node
->GetLocation();
70 // The style attributes exists and valid if size attribute exists. Otherwise,
71 // they are not. Use a negative size information to indicate the existence
72 // of style information.
77 if (node
->HasFloatAttribute(ui::AX_ATTR_FONT_SIZE
)) {
78 color
= node
->GetIntAttribute(ui::AX_ATTR_COLOR
);
79 bgcolor
= node
->GetIntAttribute(ui::AX_ATTR_BACKGROUND_COLOR
);
80 size
= node
->GetFloatAttribute(ui::AX_ATTR_FONT_SIZE
);
81 text_style
= node
->GetIntAttribute(ui::AX_ATTR_TEXT_STYLE
);
84 ScopedJavaLocalRef
<jobject
> j_node
=
85 Java_WebContentsImpl_createAccessibilitySnapshotNode(env
,
86 location
.x(), location
.y(), node
->GetScrollX(),
87 node
->GetScrollY(), location
.width(), location
.height(),
88 j_text
.obj(), color
, bgcolor
, size
, text_style
, j_class
.obj());
90 for(uint32 i
= 0; i
< node
->PlatformChildCount(); i
++) {
91 BrowserAccessibilityAndroid
* child
=
92 static_cast<BrowserAccessibilityAndroid
*>(
93 node
->PlatformGetChild(i
));
94 Java_WebContentsImpl_addAccessibilityNodeAsChild(env
,
95 j_node
.obj(), WalkAXTreeDepthFirst(env
, child
).obj());
100 // Walks over the AXTreeUpdate and creates a light weight snapshot.
101 void AXTreeSnapshotCallback(const ScopedJavaGlobalRef
<jobject
>& callback
,
102 const ui::AXTreeUpdate
& result
) {
103 JNIEnv
* env
= base::android::AttachCurrentThread();
104 if (result
.nodes
.empty()) {
105 Java_WebContentsImpl_onAccessibilitySnapshot(env
, nullptr, callback
.obj());
108 scoped_ptr
<BrowserAccessibilityManager
> manager(
109 BrowserAccessibilityManager::Create(result
, nullptr));
110 BrowserAccessibilityAndroid
* root
=
111 static_cast<BrowserAccessibilityAndroid
*>(manager
->GetRoot());
112 ScopedJavaLocalRef
<jobject
> j_root
= WalkAXTreeDepthFirst(env
, root
);
113 Java_WebContentsImpl_onAccessibilitySnapshot(
114 env
, j_root
.obj(), callback
.obj());
117 void ReleaseAllMediaPlayers(WebContents
* web_contents
,
118 RenderFrameHost
* render_frame_host
) {
119 BrowserMediaPlayerManager
* manager
=
120 static_cast<WebContentsImpl
*>(web_contents
)->
121 media_web_contents_observer()->GetMediaPlayerManager(
124 manager
->ReleaseAllMediaPlayers();
130 WebContents
* WebContents::FromJavaWebContents(
131 jobject jweb_contents_android
) {
132 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
133 if (!jweb_contents_android
)
136 WebContentsAndroid
* web_contents_android
=
137 reinterpret_cast<WebContentsAndroid
*>(
138 Java_WebContentsImpl_getNativePointer(AttachCurrentThread(),
139 jweb_contents_android
));
140 if (!web_contents_android
)
142 return web_contents_android
->web_contents();
146 static void DestroyWebContents(JNIEnv
* env
,
148 jlong jweb_contents_android_ptr
) {
149 WebContentsAndroid
* web_contents_android
=
150 reinterpret_cast<WebContentsAndroid
*>(jweb_contents_android_ptr
);
151 if (!web_contents_android
)
154 WebContents
* web_contents
= web_contents_android
->web_contents();
162 jobject
FromNativePtr(JNIEnv
* env
,
164 jlong web_contents_ptr
) {
165 WebContentsAndroid
* web_contents_android
=
166 reinterpret_cast<WebContentsAndroid
*>(web_contents_ptr
);
168 if (!web_contents_android
)
171 // Check to make sure this object hasn't been destroyed.
172 if (g_allocated_web_contents_androids
.Get().find(web_contents_android
) ==
173 g_allocated_web_contents_androids
.Get().end()) {
177 return web_contents_android
->GetJavaObject().Release();
181 bool WebContentsAndroid::Register(JNIEnv
* env
) {
182 return RegisterNativesImpl(env
);
185 WebContentsAndroid::WebContentsAndroid(WebContents
* web_contents
)
186 : web_contents_(web_contents
),
187 navigation_controller_(&(web_contents
->GetController())),
188 weak_factory_(this) {
189 g_allocated_web_contents_androids
.Get().insert(this);
190 JNIEnv
* env
= AttachCurrentThread();
192 Java_WebContentsImpl_create(
194 reinterpret_cast<intptr_t>(this),
195 navigation_controller_
.GetJavaObject().obj()).obj());
196 RendererPreferences
* prefs
= web_contents_
->GetMutableRendererPrefs();
197 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
198 prefs
->network_contry_iso
=
199 command_line
->HasSwitch(switches::kNetworkCountryIso
) ?
200 command_line
->GetSwitchValueASCII(switches::kNetworkCountryIso
)
201 : net::android::GetTelephonyNetworkCountryIso();
204 WebContentsAndroid::~WebContentsAndroid() {
205 DCHECK(g_allocated_web_contents_androids
.Get().find(this) !=
206 g_allocated_web_contents_androids
.Get().end());
207 g_allocated_web_contents_androids
.Get().erase(this);
208 Java_WebContentsImpl_clearNativePtr(AttachCurrentThread(), obj_
.obj());
211 base::android::ScopedJavaLocalRef
<jobject
>
212 WebContentsAndroid::GetJavaObject() {
213 return base::android::ScopedJavaLocalRef
<jobject
>(obj_
);
216 ScopedJavaLocalRef
<jstring
> WebContentsAndroid::GetTitle(
217 JNIEnv
* env
, jobject obj
) const {
218 return base::android::ConvertUTF16ToJavaString(env
,
219 web_contents_
->GetTitle());
222 ScopedJavaLocalRef
<jstring
> WebContentsAndroid::GetVisibleURL(
223 JNIEnv
* env
, jobject obj
) const {
224 return base::android::ConvertUTF8ToJavaString(
225 env
, web_contents_
->GetVisibleURL().spec());
228 bool WebContentsAndroid::IsLoading(JNIEnv
* env
, jobject obj
) const {
229 return web_contents_
->IsLoading();
232 bool WebContentsAndroid::IsLoadingToDifferentDocument(JNIEnv
* env
,
234 return web_contents_
->IsLoadingToDifferentDocument();
237 void WebContentsAndroid::Stop(JNIEnv
* env
, jobject obj
) {
238 web_contents_
->Stop();
241 void WebContentsAndroid::InsertCSS(
242 JNIEnv
* env
, jobject jobj
, jstring jcss
) {
243 web_contents_
->InsertCSS(base::android::ConvertJavaStringToUTF8(env
, jcss
));
246 RenderWidgetHostViewAndroid
*
247 WebContentsAndroid::GetRenderWidgetHostViewAndroid() {
248 RenderWidgetHostView
* rwhv
= NULL
;
249 rwhv
= web_contents_
->GetRenderWidgetHostView();
250 if (web_contents_
->ShowingInterstitialPage()) {
251 rwhv
= web_contents_
->GetInterstitialPage()
253 ->GetRenderViewHost()
256 return static_cast<RenderWidgetHostViewAndroid
*>(rwhv
);
259 jint
WebContentsAndroid::GetBackgroundColor(JNIEnv
* env
, jobject obj
) {
260 RenderWidgetHostViewAndroid
* rwhva
= GetRenderWidgetHostViewAndroid();
262 return SK_ColorWHITE
;
263 return rwhva
->GetCachedBackgroundColor();
266 ScopedJavaLocalRef
<jstring
> WebContentsAndroid::GetURL(JNIEnv
* env
,
268 return ConvertUTF8ToJavaString(env
, web_contents_
->GetURL().spec());
271 ScopedJavaLocalRef
<jstring
> WebContentsAndroid::GetLastCommittedURL(
274 return ConvertUTF8ToJavaString(env
,
275 web_contents_
->GetLastCommittedURL().spec());
279 jboolean
WebContentsAndroid::IsIncognito(JNIEnv
* env
, jobject obj
) {
280 return web_contents_
->GetBrowserContext()->IsOffTheRecord();
283 void WebContentsAndroid::ResumeLoadingCreatedWebContents(JNIEnv
* env
,
285 web_contents_
->ResumeLoadingCreatedWebContents();
288 void WebContentsAndroid::OnHide(JNIEnv
* env
, jobject obj
) {
289 web_contents_
->WasHidden();
292 void WebContentsAndroid::OnShow(JNIEnv
* env
, jobject obj
) {
293 web_contents_
->WasShown();
296 void WebContentsAndroid::ReleaseMediaPlayers(JNIEnv
* env
, jobject jobj
) {
297 #if defined(ENABLE_BROWSER_CDMS)
298 web_contents_
->ForEachFrame(
299 base::Bind(&ReleaseAllMediaPlayers
, base::Unretained(web_contents_
)));
300 #endif // defined(ENABLE_BROWSER_CDMS)
303 void WebContentsAndroid::ShowInterstitialPage(
307 jlong delegate_ptr
) {
308 GURL
url(base::android::ConvertJavaStringToUTF8(env
, jurl
));
309 InterstitialPageDelegateAndroid
* delegate
=
310 reinterpret_cast<InterstitialPageDelegateAndroid
*>(delegate_ptr
);
311 InterstitialPage
* interstitial
= InterstitialPage::Create(
312 web_contents_
, false, url
, delegate
);
313 delegate
->set_interstitial_page(interstitial
);
314 interstitial
->Show();
317 jboolean
WebContentsAndroid::IsShowingInterstitialPage(JNIEnv
* env
,
319 return web_contents_
->ShowingInterstitialPage();
322 jboolean
WebContentsAndroid::IsRenderWidgetHostViewReady(
325 RenderWidgetHostViewAndroid
* view
= GetRenderWidgetHostViewAndroid();
326 return view
&& view
->HasValidFrame();
329 void WebContentsAndroid::ExitFullscreen(JNIEnv
* env
, jobject obj
) {
330 web_contents_
->ExitFullscreen();
333 void WebContentsAndroid::UpdateTopControlsState(
339 RenderViewHost
* host
= web_contents_
->GetRenderViewHost();
342 host
->Send(new ViewMsg_UpdateTopControlsState(host
->GetRoutingID(),
348 void WebContentsAndroid::ShowImeIfNeeded(JNIEnv
* env
, jobject obj
) {
349 RenderViewHost
* host
= web_contents_
->GetRenderViewHost();
352 host
->Send(new ViewMsg_ShowImeIfNeeded(host
->GetRoutingID()));
355 void WebContentsAndroid::ScrollFocusedEditableNodeIntoView(
358 RenderViewHost
* host
= web_contents_
->GetRenderViewHost();
361 host
->Send(new InputMsg_ScrollFocusedEditableNodeIntoRect(
362 host
->GetRoutingID(), gfx::Rect()));
365 void WebContentsAndroid::SelectWordAroundCaret(JNIEnv
* env
, jobject obj
) {
366 RenderViewHost
* host
= web_contents_
->GetRenderViewHost();
369 host
->SelectWordAroundCaret();
372 void WebContentsAndroid::EvaluateJavaScript(JNIEnv
* env
,
376 RenderViewHost
* rvh
= web_contents_
->GetRenderViewHost();
379 if (!rvh
->IsRenderViewLive()) {
380 if (!static_cast<WebContentsImpl
*>(web_contents_
)->
381 CreateRenderViewForInitialEmptyDocument()) {
382 LOG(ERROR
) << "Failed to create RenderView in EvaluateJavaScript";
388 // No callback requested.
389 web_contents_
->GetMainFrame()->ExecuteJavaScript(
390 ConvertJavaStringToUTF16(env
, script
));
394 // Secure the Java callback in a scoped object and give ownership of it to the
396 ScopedJavaGlobalRef
<jobject
> j_callback
;
397 j_callback
.Reset(env
, callback
);
398 RenderFrameHost::JavaScriptResultCallback js_callback
=
399 base::Bind(&JavaScriptResultCallback
, j_callback
);
401 web_contents_
->GetMainFrame()->ExecuteJavaScript(
402 ConvertJavaStringToUTF16(env
, script
), js_callback
);
405 void WebContentsAndroid::AddMessageToDevToolsConsole(JNIEnv
* env
,
410 DCHECK_LE(level
, CONSOLE_MESSAGE_LEVEL_LAST
);
412 web_contents_
->GetMainFrame()->AddMessageToConsole(
413 static_cast<ConsoleMessageLevel
>(level
),
414 ConvertJavaStringToUTF8(env
, message
));
417 jboolean
WebContentsAndroid::HasAccessedInitialDocument(
420 return static_cast<WebContentsImpl
*>(web_contents_
)->
421 HasAccessedInitialDocument();
424 jint
WebContentsAndroid::GetThemeColor(JNIEnv
* env
, jobject obj
) {
425 return web_contents_
->GetThemeColor();
428 void WebContentsAndroid::RequestAccessibilitySnapshot(JNIEnv
* env
,
431 // Secure the Java callback in a scoped object and give ownership of it to the
433 ScopedJavaGlobalRef
<jobject
> j_callback
;
434 j_callback
.Reset(env
, callback
);
435 WebContentsImpl::AXTreeSnapshotCallback snapshot_callback
=
436 base::Bind(&AXTreeSnapshotCallback
, j_callback
);
438 static_cast<WebContentsImpl
*>(web_contents_
)->RequestAXTreeSnapshot(
442 } // namespace content