Home | History | Annotate | Download | only in android
      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).
     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 
     38 WebContentsObserverAndroid::~WebContentsObserverAndroid() {
     39 }
     40 
     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 
     51 void WebContentsObserverAndroid::Destroy(JNIEnv* env, jobject obj) {
     52   delete this;
     53 }
     54 
     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 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    321 bool RegisterWebContentsObserverAndroid(JNIEnv* env) {
    322   return RegisterNativesImpl(env);
    323 }
    324 }  // namespace content
    325