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