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