1 // Copyright (c) 2012 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/android/web_contents_observer_android.h"
6
7 #include <string>
8
9 #include <jni.h>
10
11 #include "base/android/jni_android.h"
12 #include "base/android/jni_string.h"
13 #include "base/android/scoped_java_ref.h"
14 #include "content/browser/renderer_host/render_widget_host_impl.h"
15 #include "content/browser/web_contents/web_contents_impl.h"
16 #include "content/public/browser/navigation_details.h"
17 #include "content/public/browser/navigation_entry.h"
18 #include "jni/WebContentsObserverAndroid_jni.h"
19
20 using base::android::AttachCurrentThread;
21 using base::android::ScopedJavaLocalRef;
22 using base::android::ConvertUTF8ToJavaString;
23 using base::android::ConvertUTF16ToJavaString;
24
25 namespace content {
26
27 // TODO(dcheng): File a bug. This class incorrectly passes just a frame ID,
28 // which is not sufficient to identify a frame (since frame IDs are scoped per
29 // render process, and so may collide).
WebContentsObserverAndroid(JNIEnv * env,jobject obj,WebContents * web_contents)30 WebContentsObserverAndroid::WebContentsObserverAndroid(
31 JNIEnv* env,
32 jobject obj,
33 WebContents* web_contents)
34 : WebContentsObserver(web_contents),
35 weak_java_observer_(env, obj){
36 }
37
~WebContentsObserverAndroid()38 WebContentsObserverAndroid::~WebContentsObserverAndroid() {
39 }
40
Init(JNIEnv * env,jobject obj,jobject java_web_contents)41 jlong Init(JNIEnv* env, jobject obj, jobject java_web_contents) {
42 WebContents* web_contents =
43 WebContents::FromJavaWebContents(java_web_contents);
44 CHECK(web_contents);
45
46 WebContentsObserverAndroid* native_observer = new WebContentsObserverAndroid(
47 env, obj, web_contents);
48 return reinterpret_cast<intptr_t>(native_observer);
49 }
50
Destroy(JNIEnv * env,jobject obj)51 void WebContentsObserverAndroid::Destroy(JNIEnv* env, jobject obj) {
52 delete this;
53 }
54
WebContentsDestroyed()55 void WebContentsObserverAndroid::WebContentsDestroyed() {
56 JNIEnv* env = AttachCurrentThread();
57 ScopedJavaLocalRef<jobject> obj(weak_java_observer_.get(env));
58 if (obj.is_null()) {
59 delete this;
60 } else {
61 // The java side will destroy |this|
62 Java_WebContentsObserverAndroid_detachFromWebContents(env, obj.obj());
63 }
64 }
65
RenderProcessGone(base::TerminationStatus termination_status)66 void WebContentsObserverAndroid::RenderProcessGone(
67 base::TerminationStatus termination_status) {
68 JNIEnv* env = AttachCurrentThread();
69 ScopedJavaLocalRef<jobject> obj(weak_java_observer_.get(env));
70 if (obj.is_null())
71 return;
72 jboolean was_oom_protected =
73 termination_status == base::TERMINATION_STATUS_OOM_PROTECTED;
74 Java_WebContentsObserverAndroid_renderProcessGone(
75 env, obj.obj(), was_oom_protected);
76 }
77
DidStartLoading(RenderViewHost * render_view_host)78 void WebContentsObserverAndroid::DidStartLoading(
79 RenderViewHost* render_view_host) {
80 JNIEnv* env = AttachCurrentThread();
81 ScopedJavaLocalRef<jobject> obj(weak_java_observer_.get(env));
82 if (obj.is_null())
83 return;
84 ScopedJavaLocalRef<jstring> jstring_url(ConvertUTF8ToJavaString(
85 env, web_contents()->GetVisibleURL().spec()));
86 Java_WebContentsObserverAndroid_didStartLoading(
87 env, obj.obj(), jstring_url.obj());
88 }
89
DidStopLoading(RenderViewHost * render_view_host)90 void WebContentsObserverAndroid::DidStopLoading(
91 RenderViewHost* render_view_host) {
92 JNIEnv* env = AttachCurrentThread();
93 ScopedJavaLocalRef<jobject> obj(weak_java_observer_.get(env));
94 if (obj.is_null())
95 return;
96 ScopedJavaLocalRef<jstring> jstring_url(ConvertUTF8ToJavaString(
97 env, web_contents()->GetLastCommittedURL().spec()));
98 Java_WebContentsObserverAndroid_didStopLoading(
99 env, obj.obj(), jstring_url.obj());
100 }
101
DidFailProvisionalLoad(RenderFrameHost * render_frame_host,const GURL & validated_url,int error_code,const base::string16 & error_description)102 void WebContentsObserverAndroid::DidFailProvisionalLoad(
103 RenderFrameHost* render_frame_host,
104 const GURL& validated_url,
105 int error_code,
106 const base::string16& error_description) {
107 DidFailLoadInternal(true,
108 !render_frame_host->GetParent(),
109 error_code,
110 error_description,
111 validated_url);
112 }
113
DidFailLoad(RenderFrameHost * render_frame_host,const GURL & validated_url,int error_code,const base::string16 & error_description)114 void WebContentsObserverAndroid::DidFailLoad(
115 RenderFrameHost* render_frame_host,
116 const GURL& validated_url,
117 int error_code,
118 const base::string16& error_description) {
119 DidFailLoadInternal(false,
120 !render_frame_host->GetParent(),
121 error_code,
122 error_description,
123 validated_url);
124 }
125
DidNavigateMainFrame(const LoadCommittedDetails & details,const FrameNavigateParams & params)126 void WebContentsObserverAndroid::DidNavigateMainFrame(
127 const LoadCommittedDetails& details,
128 const FrameNavigateParams& params) {
129 JNIEnv* env = AttachCurrentThread();
130 ScopedJavaLocalRef<jobject> obj(weak_java_observer_.get(env));
131 if (obj.is_null())
132 return;
133 ScopedJavaLocalRef<jstring> jstring_url(
134 ConvertUTF8ToJavaString(env, params.url.spec()));
135 ScopedJavaLocalRef<jstring> jstring_base_url(
136 ConvertUTF8ToJavaString(env, params.base_url.spec()));
137
138 // See http://crbug.com/251330 for why it's determined this way.
139 url::Replacements<char> replacements;
140 replacements.ClearRef();
141 bool urls_same_ignoring_fragment =
142 params.url.ReplaceComponents(replacements) ==
143 details.previous_url.ReplaceComponents(replacements);
144
145 // is_fragment_navigation is indicative of the intent of this variable.
146 // However, there isn't sufficient information here to determine whether this
147 // is actually a fragment navigation, or a history API navigation to a URL
148 // that would also be valid for a fragment navigation.
149 bool is_fragment_navigation = urls_same_ignoring_fragment &&
150 (details.type == NAVIGATION_TYPE_IN_PAGE || details.is_in_page);
151 Java_WebContentsObserverAndroid_didNavigateMainFrame(
152 env, obj.obj(), jstring_url.obj(), jstring_base_url.obj(),
153 details.is_navigation_to_different_page(), is_fragment_navigation,
154 details.http_status_code);
155 }
156
DidNavigateAnyFrame(const LoadCommittedDetails & details,const FrameNavigateParams & params)157 void WebContentsObserverAndroid::DidNavigateAnyFrame(
158 const LoadCommittedDetails& details,
159 const FrameNavigateParams& params) {
160 JNIEnv* env = AttachCurrentThread();
161 ScopedJavaLocalRef<jobject> obj(weak_java_observer_.get(env));
162 if (obj.is_null())
163 return;
164 ScopedJavaLocalRef<jstring> jstring_url(
165 ConvertUTF8ToJavaString(env, params.url.spec()));
166 ScopedJavaLocalRef<jstring> jstring_base_url(
167 ConvertUTF8ToJavaString(env, params.base_url.spec()));
168 jboolean jboolean_is_reload = ui::PageTransitionCoreTypeIs(
169 params.transition, ui::PAGE_TRANSITION_RELOAD);
170
171 Java_WebContentsObserverAndroid_didNavigateAnyFrame(
172 env, obj.obj(), jstring_url.obj(), jstring_base_url.obj(),
173 jboolean_is_reload);
174 }
175
DidStartProvisionalLoadForFrame(RenderFrameHost * render_frame_host,const GURL & validated_url,bool is_error_page,bool is_iframe_srcdoc)176 void WebContentsObserverAndroid::DidStartProvisionalLoadForFrame(
177 RenderFrameHost* render_frame_host,
178 const GURL& validated_url,
179 bool is_error_page,
180 bool is_iframe_srcdoc) {
181 JNIEnv* env = AttachCurrentThread();
182 ScopedJavaLocalRef<jobject> obj(weak_java_observer_.get(env));
183 if (obj.is_null())
184 return;
185 ScopedJavaLocalRef<jstring> jstring_url(
186 ConvertUTF8ToJavaString(env, validated_url.spec()));
187 // TODO(dcheng): Does Java really need the parent frame ID? It doesn't appear
188 // to be used at all, and it just adds complexity here.
189 Java_WebContentsObserverAndroid_didStartProvisionalLoadForFrame(
190 env,
191 obj.obj(),
192 render_frame_host->GetRoutingID(),
193 render_frame_host->GetParent()
194 ? render_frame_host->GetParent()->GetRoutingID()
195 : -1,
196 !render_frame_host->GetParent(),
197 jstring_url.obj(),
198 is_error_page,
199 is_iframe_srcdoc);
200 }
201
DidCommitProvisionalLoadForFrame(RenderFrameHost * render_frame_host,const GURL & url,ui::PageTransition transition_type)202 void WebContentsObserverAndroid::DidCommitProvisionalLoadForFrame(
203 RenderFrameHost* render_frame_host,
204 const GURL& url,
205 ui::PageTransition transition_type) {
206 JNIEnv* env = AttachCurrentThread();
207 ScopedJavaLocalRef<jobject> obj(weak_java_observer_.get(env));
208 if (obj.is_null())
209 return;
210 ScopedJavaLocalRef<jstring> jstring_url(
211 ConvertUTF8ToJavaString(env, url.spec()));
212 Java_WebContentsObserverAndroid_didCommitProvisionalLoadForFrame(
213 env,
214 obj.obj(),
215 render_frame_host->GetRoutingID(),
216 !render_frame_host->GetParent(),
217 jstring_url.obj(),
218 transition_type);
219 }
220
DidFinishLoad(RenderFrameHost * render_frame_host,const GURL & validated_url)221 void WebContentsObserverAndroid::DidFinishLoad(
222 RenderFrameHost* render_frame_host,
223 const GURL& validated_url) {
224 JNIEnv* env = AttachCurrentThread();
225 ScopedJavaLocalRef<jobject> obj(weak_java_observer_.get(env));
226 if (obj.is_null())
227 return;
228
229 std::string url_string = validated_url.spec();
230 NavigationEntry* entry =
231 web_contents()->GetController().GetLastCommittedEntry();
232 // Note that GetBaseURLForDataURL is only used by the Android WebView.
233 if (entry && !entry->GetBaseURLForDataURL().is_empty())
234 url_string = entry->GetBaseURLForDataURL().possibly_invalid_spec();
235
236 ScopedJavaLocalRef<jstring> jstring_url(
237 ConvertUTF8ToJavaString(env, url_string));
238 Java_WebContentsObserverAndroid_didFinishLoad(
239 env,
240 obj.obj(),
241 render_frame_host->GetRoutingID(),
242 jstring_url.obj(),
243 !render_frame_host->GetParent());
244 }
245
DocumentLoadedInFrame(RenderFrameHost * render_frame_host)246 void WebContentsObserverAndroid::DocumentLoadedInFrame(
247 RenderFrameHost* render_frame_host) {
248 JNIEnv* env = AttachCurrentThread();
249 ScopedJavaLocalRef<jobject> obj(weak_java_observer_.get(env));
250 if (obj.is_null())
251 return;
252 Java_WebContentsObserverAndroid_documentLoadedInFrame(
253 env, obj.obj(), render_frame_host->GetRoutingID());
254 }
255
NavigationEntryCommitted(const LoadCommittedDetails & load_details)256 void WebContentsObserverAndroid::NavigationEntryCommitted(
257 const LoadCommittedDetails& load_details) {
258 JNIEnv* env = AttachCurrentThread();
259 ScopedJavaLocalRef<jobject> obj(weak_java_observer_.get(env));
260 if (obj.is_null())
261 return;
262 Java_WebContentsObserverAndroid_navigationEntryCommitted(env, obj.obj());
263 }
264
DidAttachInterstitialPage()265 void WebContentsObserverAndroid::DidAttachInterstitialPage() {
266 JNIEnv* env = AttachCurrentThread();
267 ScopedJavaLocalRef<jobject> obj(weak_java_observer_.get(env));
268 if (obj.is_null())
269 return;
270 Java_WebContentsObserverAndroid_didAttachInterstitialPage(env, obj.obj());
271 }
272
DidDetachInterstitialPage()273 void WebContentsObserverAndroid::DidDetachInterstitialPage() {
274 JNIEnv* env = AttachCurrentThread();
275 ScopedJavaLocalRef<jobject> obj(weak_java_observer_.get(env));
276 if (obj.is_null())
277 return;
278 Java_WebContentsObserverAndroid_didDetachInterstitialPage(env, obj.obj());
279 }
280
DidChangeThemeColor(SkColor color)281 void WebContentsObserverAndroid::DidChangeThemeColor(SkColor color) {
282 JNIEnv* env = AttachCurrentThread();
283 ScopedJavaLocalRef<jobject> obj(weak_java_observer_.get(env));
284 if (obj.is_null())
285 return;
286 Java_WebContentsObserverAndroid_didChangeThemeColor(env, obj.obj(), color);
287 }
288
DidFailLoadInternal(bool is_provisional_load,bool is_main_frame,int error_code,const base::string16 & description,const GURL & url)289 void WebContentsObserverAndroid::DidFailLoadInternal(
290 bool is_provisional_load,
291 bool is_main_frame,
292 int error_code,
293 const base::string16& description,
294 const GURL& url) {
295 JNIEnv* env = AttachCurrentThread();
296 ScopedJavaLocalRef<jobject> obj(weak_java_observer_.get(env));
297 if (obj.is_null())
298 return;
299 ScopedJavaLocalRef<jstring> jstring_error_description(
300 ConvertUTF16ToJavaString(env, description));
301 ScopedJavaLocalRef<jstring> jstring_url(
302 ConvertUTF8ToJavaString(env, url.spec()));
303
304 Java_WebContentsObserverAndroid_didFailLoad(
305 env, obj.obj(),
306 is_provisional_load,
307 is_main_frame,
308 error_code,
309 jstring_error_description.obj(), jstring_url.obj());
310 }
311
DidFirstVisuallyNonEmptyPaint()312 void WebContentsObserverAndroid::DidFirstVisuallyNonEmptyPaint() {
313 JNIEnv* env = AttachCurrentThread();
314 ScopedJavaLocalRef<jobject> obj(weak_java_observer_.get(env));
315 if (obj.is_null())
316 return;
317 Java_WebContentsObserverAndroid_didFirstVisuallyNonEmptyPaint(
318 env, obj.obj());
319 }
320
RegisterWebContentsObserverAndroid(JNIEnv * env)321 bool RegisterWebContentsObserverAndroid(JNIEnv* env) {
322 return RegisterNativesImpl(env);
323 }
324 } // namespace content
325