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