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