Home | History | Annotate | Download | only in web_contents_delegate_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 "components/web_contents_delegate_android/web_contents_delegate_android.h"
      6 
      7 #include <android/keycodes.h>
      8 
      9 #include "base/android/jni_android.h"
     10 #include "base/android/jni_array.h"
     11 #include "base/android/jni_string.h"
     12 #include "components/web_contents_delegate_android/color_chooser_android.h"
     13 #include "content/public/browser/android/content_view_core.h"
     14 #include "content/public/browser/color_chooser.h"
     15 #include "content/public/browser/invalidate_type.h"
     16 #include "content/public/browser/native_web_keyboard_event.h"
     17 #include "content/public/browser/page_navigator.h"
     18 #include "content/public/browser/render_widget_host_view.h"
     19 #include "content/public/browser/web_contents.h"
     20 #include "content/public/common/page_transition_types.h"
     21 #include "content/public/common/referrer.h"
     22 #include "jni/WebContentsDelegateAndroid_jni.h"
     23 #include "ui/base/window_open_disposition.h"
     24 #include "ui/gfx/rect.h"
     25 #include "url/gurl.h"
     26 
     27 using base::android::AttachCurrentThread;
     28 using base::android::ConvertUTF8ToJavaString;
     29 using base::android::ConvertUTF16ToJavaString;
     30 using base::android::HasClass;
     31 using base::android::ScopedJavaLocalRef;
     32 using content::ColorChooser;
     33 using content::WebContents;
     34 using content::WebContentsDelegate;
     35 
     36 namespace web_contents_delegate_android {
     37 
     38 WebContentsDelegateAndroid::WebContentsDelegateAndroid(JNIEnv* env, jobject obj)
     39     : weak_java_delegate_(env, obj) {
     40 }
     41 
     42 WebContentsDelegateAndroid::~WebContentsDelegateAndroid() {
     43 }
     44 
     45 ScopedJavaLocalRef<jobject>
     46 WebContentsDelegateAndroid::GetJavaDelegate(JNIEnv* env) const {
     47   return weak_java_delegate_.get(env);
     48 }
     49 
     50 // ----------------------------------------------------------------------------
     51 // WebContentsDelegate methods
     52 // ----------------------------------------------------------------------------
     53 
     54 ColorChooser* WebContentsDelegateAndroid::OpenColorChooser(WebContents* source,
     55                                                            SkColor color)  {
     56   return new ColorChooserAndroid(source, color);
     57 }
     58 
     59 // OpenURLFromTab() will be called when we're performing a browser-intiated
     60 // navigation. The most common scenario for this is opening new tabs (see
     61 // RenderViewImpl::decidePolicyForNavigation for more details).
     62 WebContents* WebContentsDelegateAndroid::OpenURLFromTab(
     63     WebContents* source,
     64     const content::OpenURLParams& params) {
     65   const GURL& url = params.url;
     66   WindowOpenDisposition disposition = params.disposition;
     67   content::PageTransition transition(
     68       PageTransitionFromInt(params.transition));
     69 
     70   if (!source || (disposition != CURRENT_TAB &&
     71                   disposition != NEW_FOREGROUND_TAB &&
     72                   disposition != NEW_BACKGROUND_TAB &&
     73                   disposition != OFF_THE_RECORD)) {
     74     NOTIMPLEMENTED();
     75     return NULL;
     76   }
     77 
     78   JNIEnv* env = AttachCurrentThread();
     79   ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
     80   if (obj.is_null())
     81     return WebContentsDelegate::OpenURLFromTab(source, params);
     82 
     83   if (disposition == NEW_FOREGROUND_TAB ||
     84       disposition == NEW_BACKGROUND_TAB ||
     85       disposition == OFF_THE_RECORD) {
     86     JNIEnv* env = AttachCurrentThread();
     87     ScopedJavaLocalRef<jstring> java_url =
     88         ConvertUTF8ToJavaString(env, url.spec());
     89     ScopedJavaLocalRef<jstring> extra_headers =
     90             ConvertUTF8ToJavaString(env, params.extra_headers);
     91     ScopedJavaLocalRef<jbyteArray> post_data;
     92     if (params.uses_post &&
     93         params.browser_initiated_post_data.get() &&
     94         params.browser_initiated_post_data.get()->size()) {
     95       post_data = base::android::ToJavaByteArray(
     96           env,
     97           reinterpret_cast<const uint8*>(
     98               params.browser_initiated_post_data.get()->front()),
     99           params.browser_initiated_post_data.get()->size());
    100     }
    101     Java_WebContentsDelegateAndroid_openNewTab(env,
    102                                                obj.obj(),
    103                                                java_url.obj(),
    104                                                extra_headers.obj(),
    105                                                post_data.obj(),
    106                                                disposition);
    107     return NULL;
    108   }
    109 
    110   source->GetController().LoadURL(url, params.referrer, transition,
    111                                   std::string());
    112   return source;
    113 }
    114 
    115 void WebContentsDelegateAndroid::NavigationStateChanged(
    116     const WebContents* source, unsigned changed_flags) {
    117   JNIEnv* env = AttachCurrentThread();
    118   ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
    119   if (obj.is_null())
    120     return;
    121   Java_WebContentsDelegateAndroid_navigationStateChanged(
    122       env,
    123       obj.obj(),
    124       changed_flags);
    125 }
    126 
    127 void WebContentsDelegateAndroid::AddNewContents(
    128     WebContents* source,
    129     WebContents* new_contents,
    130     WindowOpenDisposition disposition,
    131     const gfx::Rect& initial_pos,
    132     bool user_gesture,
    133     bool* was_blocked) {
    134   JNIEnv* env = AttachCurrentThread();
    135   ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
    136   bool handled = false;
    137   if (!obj.is_null()) {
    138     handled = Java_WebContentsDelegateAndroid_addNewContents(
    139         env,
    140         obj.obj(),
    141         reinterpret_cast<jint>(source),
    142         reinterpret_cast<jint>(new_contents),
    143         static_cast<jint>(disposition),
    144         NULL,
    145         user_gesture);
    146   }
    147   if (!handled)
    148     delete new_contents;
    149 }
    150 
    151 void WebContentsDelegateAndroid::ActivateContents(WebContents* contents) {
    152   JNIEnv* env = AttachCurrentThread();
    153   ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
    154   if (obj.is_null())
    155     return;
    156   Java_WebContentsDelegateAndroid_activateContents(env, obj.obj());
    157 }
    158 
    159 void WebContentsDelegateAndroid::DeactivateContents(WebContents* contents) {
    160   // On desktop the current window is deactivated here, bringing the next window
    161   // to focus. Not implemented on Android.
    162 }
    163 
    164 void WebContentsDelegateAndroid::LoadingStateChanged(WebContents* source) {
    165   JNIEnv* env = AttachCurrentThread();
    166   ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
    167   if (obj.is_null())
    168     return;
    169   bool has_stopped = source == NULL || !source->IsLoading();
    170 
    171   if (has_stopped)
    172     Java_WebContentsDelegateAndroid_onLoadStopped(env, obj.obj());
    173   else
    174     Java_WebContentsDelegateAndroid_onLoadStarted(env, obj.obj());
    175 }
    176 
    177 void WebContentsDelegateAndroid::LoadProgressChanged(WebContents* source,
    178                                                      double progress) {
    179   JNIEnv* env = AttachCurrentThread();
    180   ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
    181   if (obj.is_null())
    182     return;
    183   Java_WebContentsDelegateAndroid_notifyLoadProgressChanged(
    184       env,
    185       obj.obj(),
    186       progress);
    187 }
    188 
    189 void WebContentsDelegateAndroid::RendererUnresponsive(WebContents* source) {
    190   JNIEnv* env = AttachCurrentThread();
    191   ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
    192   if (obj.is_null())
    193     return;
    194   Java_WebContentsDelegateAndroid_rendererUnresponsive(env, obj.obj());
    195 }
    196 
    197 void WebContentsDelegateAndroid::RendererResponsive(WebContents* source) {
    198   JNIEnv* env = AttachCurrentThread();
    199   ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
    200   if (obj.is_null())
    201     return;
    202   Java_WebContentsDelegateAndroid_rendererResponsive(env, obj.obj());
    203 }
    204 
    205 void WebContentsDelegateAndroid::CloseContents(WebContents* source) {
    206   JNIEnv* env = AttachCurrentThread();
    207   ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
    208   if (obj.is_null())
    209     return;
    210   Java_WebContentsDelegateAndroid_closeContents(env, obj.obj());
    211 }
    212 
    213 void WebContentsDelegateAndroid::MoveContents(WebContents* source,
    214                                               const gfx::Rect& pos) {
    215   // Do nothing.
    216 }
    217 
    218 bool WebContentsDelegateAndroid::AddMessageToConsole(
    219     WebContents* source,
    220     int32 level,
    221     const base::string16& message,
    222     int32 line_no,
    223     const base::string16& source_id) {
    224   JNIEnv* env = AttachCurrentThread();
    225   ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
    226   if (obj.is_null())
    227     return WebContentsDelegate::AddMessageToConsole(source, level, message,
    228                                                     line_no, source_id);
    229   ScopedJavaLocalRef<jstring> jmessage(ConvertUTF16ToJavaString(env, message));
    230   ScopedJavaLocalRef<jstring> jsource_id(
    231       ConvertUTF16ToJavaString(env, source_id));
    232   int jlevel = WEB_CONTENTS_DELEGATE_LOG_LEVEL_DEBUG;
    233   switch (level) {
    234     case logging::LOG_VERBOSE:
    235       jlevel = WEB_CONTENTS_DELEGATE_LOG_LEVEL_DEBUG;
    236       break;
    237     case logging::LOG_INFO:
    238       jlevel = WEB_CONTENTS_DELEGATE_LOG_LEVEL_LOG;
    239       break;
    240     case logging::LOG_WARNING:
    241       jlevel = WEB_CONTENTS_DELEGATE_LOG_LEVEL_WARNING;
    242       break;
    243     case logging::LOG_ERROR:
    244       jlevel = WEB_CONTENTS_DELEGATE_LOG_LEVEL_ERROR;
    245       break;
    246     default:
    247       NOTREACHED();
    248   }
    249   return Java_WebContentsDelegateAndroid_addMessageToConsole(
    250       env,
    251       GetJavaDelegate(env).obj(),
    252       jlevel,
    253       jmessage.obj(),
    254       line_no,
    255       jsource_id.obj());
    256 }
    257 
    258 // This is either called from TabContents::DidNavigateMainFramePostCommit() with
    259 // an empty GURL or responding to RenderViewHost::OnMsgUpateTargetURL(). In
    260 // Chrome, the latter is not always called, especially not during history
    261 // navigation. So we only handle the first case and pass the source TabContents'
    262 // url to Java to update the UI.
    263 void WebContentsDelegateAndroid::UpdateTargetURL(WebContents* source,
    264                                                  int32 page_id,
    265                                                  const GURL& url) {
    266   if (!url.is_empty())
    267     return;
    268   JNIEnv* env = AttachCurrentThread();
    269   ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
    270   if (obj.is_null())
    271     return;
    272   ScopedJavaLocalRef<jstring> java_url =
    273       ConvertUTF8ToJavaString(env, source->GetURL().spec());
    274   Java_WebContentsDelegateAndroid_onUpdateUrl(env,
    275                                               obj.obj(),
    276                                               java_url.obj());
    277 }
    278 
    279 void WebContentsDelegateAndroid::HandleKeyboardEvent(
    280     WebContents* source,
    281     const content::NativeWebKeyboardEvent& event) {
    282   jobject key_event = event.os_event;
    283   if (key_event) {
    284     JNIEnv* env = AttachCurrentThread();
    285     ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
    286     if (obj.is_null())
    287       return;
    288     Java_WebContentsDelegateAndroid_handleKeyboardEvent(
    289         env, obj.obj(), key_event);
    290   }
    291 }
    292 
    293 bool WebContentsDelegateAndroid::TakeFocus(WebContents* source, bool reverse) {
    294   JNIEnv* env = AttachCurrentThread();
    295   ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
    296   if (obj.is_null())
    297     return WebContentsDelegate::TakeFocus(source, reverse);
    298   return Java_WebContentsDelegateAndroid_takeFocus(
    299       env, obj.obj(), reverse);
    300 }
    301 
    302 void WebContentsDelegateAndroid::ShowRepostFormWarningDialog(
    303     WebContents* source) {
    304   JNIEnv* env = AttachCurrentThread();
    305   ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
    306   if (obj.is_null())
    307     return;
    308   ScopedJavaLocalRef<jobject> content_view_core =
    309       content::ContentViewCore::FromWebContents(source)->GetJavaObject();
    310   if (content_view_core.is_null())
    311     return;
    312   Java_WebContentsDelegateAndroid_showRepostFormWarningDialog(env, obj.obj(),
    313       content_view_core.obj());
    314 }
    315 
    316 void WebContentsDelegateAndroid::ToggleFullscreenModeForTab(
    317     WebContents* web_contents,
    318     bool enter_fullscreen) {
    319   JNIEnv* env = AttachCurrentThread();
    320   ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
    321   if (obj.is_null())
    322     return;
    323   Java_WebContentsDelegateAndroid_toggleFullscreenModeForTab(
    324       env, obj.obj(), enter_fullscreen);
    325 }
    326 
    327 bool WebContentsDelegateAndroid::IsFullscreenForTabOrPending(
    328     const WebContents* web_contents) const {
    329   JNIEnv* env = AttachCurrentThread();
    330   ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
    331   if (obj.is_null())
    332     return false;
    333   return Java_WebContentsDelegateAndroid_isFullscreenForTabOrPending(
    334       env, obj.obj());
    335 }
    336 
    337 void WebContentsDelegateAndroid::DidProgrammaticallyScroll(
    338     WebContents* web_contents, const gfx::Vector2d& scroll_point) {
    339   JNIEnv* env = AttachCurrentThread();
    340   ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
    341   if (obj.is_null())
    342     return;
    343   Java_WebContentsDelegateAndroid_didProgrammaticallyScroll(
    344       env, obj.obj(), scroll_point.x(), scroll_point.y());
    345 }
    346 
    347 // ----------------------------------------------------------------------------
    348 // Native JNI methods
    349 // ----------------------------------------------------------------------------
    350 
    351 // Register native methods
    352 
    353 bool RegisterWebContentsDelegateAndroid(JNIEnv* env) {
    354   if (!HasClass(env, kWebContentsDelegateAndroidClassPath)) {
    355     DLOG(ERROR) << "Unable to find class WebContentsDelegateAndroid!";
    356     return false;
    357   }
    358   return RegisterNativesImpl(env);
    359 }
    360 
    361 }  // namespace web_contents_delegate_android
    362