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 "chrome/browser/android/chrome_web_contents_delegate_android.h" 6 7 #include "base/android/jni_android.h" 8 #include "base/android/jni_string.h" 9 #include "base/command_line.h" 10 #include "chrome/browser/chrome_notification_types.h" 11 #include "chrome/browser/file_select_helper.h" 12 #include "chrome/browser/media/media_capture_devices_dispatcher.h" 13 #include "chrome/browser/media/protected_media_identifier_permission_context.h" 14 #include "chrome/browser/media/protected_media_identifier_permission_context_factory.h" 15 #include "chrome/browser/prerender/prerender_manager.h" 16 #include "chrome/browser/prerender/prerender_manager_factory.h" 17 #include "chrome/browser/profiles/profile.h" 18 #include "chrome/browser/ui/app_modal_dialogs/javascript_dialog_manager.h" 19 #include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h" 20 #include "chrome/browser/ui/browser_navigator.h" 21 #include "chrome/browser/ui/find_bar/find_notification_details.h" 22 #include "chrome/browser/ui/find_bar/find_tab_helper.h" 23 #include "chrome/browser/ui/tab_helpers.h" 24 #include "chrome/common/chrome_switches.h" 25 #include "content/public/browser/notification_details.h" 26 #include "content/public/browser/notification_service.h" 27 #include "content/public/browser/notification_source.h" 28 #include "content/public/browser/render_process_host.h" 29 #include "content/public/browser/render_view_host.h" 30 #include "content/public/browser/web_contents.h" 31 #include "content/public/common/file_chooser_params.h" 32 #include "jni/ChromeWebContentsDelegateAndroid_jni.h" 33 #include "third_party/WebKit/public/web/WebWindowFeatures.h" 34 #include "ui/gfx/rect.h" 35 #include "ui/gfx/rect_f.h" 36 37 #if defined(ENABLE_PLUGINS) 38 #include "chrome/browser/pepper_broker_infobar_delegate.h" 39 #endif 40 41 using base::android::AttachCurrentThread; 42 using base::android::ScopedJavaLocalRef; 43 using content::FileChooserParams; 44 using content::WebContents; 45 46 namespace { 47 48 ScopedJavaLocalRef<jobject> CreateJavaRectF( 49 JNIEnv* env, 50 const gfx::RectF& rect) { 51 return ScopedJavaLocalRef<jobject>( 52 Java_ChromeWebContentsDelegateAndroid_createRectF(env, 53 rect.x(), 54 rect.y(), 55 rect.right(), 56 rect.bottom())); 57 } 58 59 ScopedJavaLocalRef<jobject> CreateJavaRect( 60 JNIEnv* env, 61 const gfx::Rect& rect) { 62 return ScopedJavaLocalRef<jobject>( 63 Java_ChromeWebContentsDelegateAndroid_createRect( 64 env, 65 static_cast<int>(rect.x()), 66 static_cast<int>(rect.y()), 67 static_cast<int>(rect.right()), 68 static_cast<int>(rect.bottom()))); 69 } 70 71 } // anonymous namespace 72 73 namespace chrome { 74 namespace android { 75 76 ChromeWebContentsDelegateAndroid::ChromeWebContentsDelegateAndroid(JNIEnv* env, 77 jobject obj) 78 : WebContentsDelegateAndroid(env, obj) { 79 } 80 81 ChromeWebContentsDelegateAndroid::~ChromeWebContentsDelegateAndroid() { 82 notification_registrar_.RemoveAll(); 83 } 84 85 // Register native methods. 86 bool RegisterChromeWebContentsDelegateAndroid(JNIEnv* env) { 87 return RegisterNativesImpl(env); 88 } 89 90 void ChromeWebContentsDelegateAndroid::LoadingStateChanged( 91 WebContents* source, bool to_different_document) { 92 bool has_stopped = source == NULL || !source->IsLoading(); 93 WebContentsDelegateAndroid::LoadingStateChanged( 94 source, to_different_document); 95 LoadProgressChanged(source, has_stopped ? 1 : 0); 96 } 97 98 void ChromeWebContentsDelegateAndroid::RunFileChooser( 99 WebContents* web_contents, 100 const FileChooserParams& params) { 101 FileSelectHelper::RunFileChooser(web_contents, params); 102 } 103 104 void ChromeWebContentsDelegateAndroid::CloseContents( 105 WebContents* web_contents) { 106 // Prevent dangling registrations assigned to closed web contents. 107 if (notification_registrar_.IsRegistered(this, 108 chrome::NOTIFICATION_FIND_RESULT_AVAILABLE, 109 content::Source<WebContents>(web_contents))) { 110 notification_registrar_.Remove(this, 111 chrome::NOTIFICATION_FIND_RESULT_AVAILABLE, 112 content::Source<WebContents>(web_contents)); 113 } 114 115 WebContentsDelegateAndroid::CloseContents(web_contents); 116 } 117 118 void ChromeWebContentsDelegateAndroid::Observe( 119 int type, 120 const content::NotificationSource& source, 121 const content::NotificationDetails& details) { 122 switch (type) { 123 case chrome::NOTIFICATION_FIND_RESULT_AVAILABLE: 124 OnFindResultAvailable( 125 content::Source<WebContents>(source).ptr(), 126 content::Details<FindNotificationDetails>(details).ptr()); 127 break; 128 default: 129 NOTREACHED() << "Unexpected notification: " << type; 130 break; 131 } 132 } 133 134 void ChromeWebContentsDelegateAndroid::FindReply( 135 WebContents* web_contents, 136 int request_id, 137 int number_of_matches, 138 const gfx::Rect& selection_rect, 139 int active_match_ordinal, 140 bool final_update) { 141 if (!notification_registrar_.IsRegistered(this, 142 chrome::NOTIFICATION_FIND_RESULT_AVAILABLE, 143 content::Source<WebContents>(web_contents))) { 144 notification_registrar_.Add(this, 145 chrome::NOTIFICATION_FIND_RESULT_AVAILABLE, 146 content::Source<WebContents>(web_contents)); 147 } 148 149 FindTabHelper* find_tab_helper = FindTabHelper::FromWebContents(web_contents); 150 find_tab_helper->HandleFindReply(request_id, 151 number_of_matches, 152 selection_rect, 153 active_match_ordinal, 154 final_update); 155 } 156 157 void ChromeWebContentsDelegateAndroid::OnFindResultAvailable( 158 WebContents* web_contents, 159 const FindNotificationDetails* find_result) { 160 JNIEnv* env = base::android::AttachCurrentThread(); 161 ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env); 162 if (obj.is_null()) 163 return; 164 165 ScopedJavaLocalRef<jobject> selection_rect = CreateJavaRect( 166 env, find_result->selection_rect()); 167 168 // Create the details object. 169 ScopedJavaLocalRef<jobject> details_object = 170 Java_ChromeWebContentsDelegateAndroid_createFindNotificationDetails( 171 env, 172 find_result->number_of_matches(), 173 selection_rect.obj(), 174 find_result->active_match_ordinal(), 175 find_result->final_update()); 176 177 Java_ChromeWebContentsDelegateAndroid_onFindResultAvailable( 178 env, 179 obj.obj(), 180 details_object.obj()); 181 } 182 183 void ChromeWebContentsDelegateAndroid::FindMatchRectsReply( 184 WebContents* web_contents, 185 int version, 186 const std::vector<gfx::RectF>& rects, 187 const gfx::RectF& active_rect) { 188 JNIEnv* env = base::android::AttachCurrentThread(); 189 ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env); 190 if (obj.is_null()) 191 return; 192 193 // Create the details object. 194 ScopedJavaLocalRef<jobject> details_object = 195 Java_ChromeWebContentsDelegateAndroid_createFindMatchRectsDetails( 196 env, 197 version, 198 rects.size(), 199 CreateJavaRectF(env, active_rect).obj()); 200 201 // Add the rects 202 for (size_t i = 0; i < rects.size(); ++i) { 203 Java_ChromeWebContentsDelegateAndroid_setMatchRectByIndex( 204 env, 205 details_object.obj(), 206 i, 207 CreateJavaRectF(env, rects[i]).obj()); 208 } 209 210 Java_ChromeWebContentsDelegateAndroid_onFindMatchRectsAvailable( 211 env, 212 obj.obj(), 213 details_object.obj()); 214 } 215 216 content::JavaScriptDialogManager* 217 ChromeWebContentsDelegateAndroid::GetJavaScriptDialogManager() { 218 return GetJavaScriptDialogManagerInstance(); 219 } 220 221 void ChromeWebContentsDelegateAndroid::RequestMediaAccessPermission( 222 content::WebContents* web_contents, 223 const content::MediaStreamRequest& request, 224 const content::MediaResponseCallback& callback) { 225 MediaCaptureDevicesDispatcher::GetInstance()->ProcessMediaAccessRequest( 226 web_contents, request, callback, NULL); 227 } 228 229 bool ChromeWebContentsDelegateAndroid::RequestPpapiBrokerPermission( 230 WebContents* web_contents, 231 const GURL& url, 232 const base::FilePath& plugin_path, 233 const base::Callback<void(bool)>& callback) { 234 #if defined(ENABLE_PLUGINS) 235 PepperBrokerInfoBarDelegate::Create( 236 web_contents, url, plugin_path, callback); 237 return true; 238 #else 239 return false; 240 #endif 241 } 242 243 WebContents* ChromeWebContentsDelegateAndroid::OpenURLFromTab( 244 WebContents* source, 245 const content::OpenURLParams& params) { 246 WindowOpenDisposition disposition = params.disposition; 247 if (!source || (disposition != CURRENT_TAB && 248 disposition != NEW_FOREGROUND_TAB && 249 disposition != NEW_BACKGROUND_TAB && 250 disposition != OFF_THE_RECORD && 251 disposition != NEW_POPUP && 252 disposition != NEW_WINDOW)) { 253 // We can't handle this here. Give the parent a chance. 254 return WebContentsDelegateAndroid::OpenURLFromTab(source, params); 255 } 256 257 Profile* profile = Profile::FromBrowserContext(source->GetBrowserContext()); 258 chrome::NavigateParams nav_params(profile, 259 params.url, 260 params.transition); 261 FillNavigateParamsFromOpenURLParams(&nav_params, params); 262 nav_params.source_contents = source; 263 nav_params.window_action = chrome::NavigateParams::SHOW_WINDOW; 264 nav_params.user_gesture = params.user_gesture; 265 266 PopupBlockerTabHelper* popup_blocker_helper = 267 PopupBlockerTabHelper::FromWebContents(source); 268 DCHECK(popup_blocker_helper); 269 270 if ((params.disposition == NEW_POPUP || 271 params.disposition == NEW_FOREGROUND_TAB || 272 params.disposition == NEW_BACKGROUND_TAB || 273 params.disposition == NEW_WINDOW) && 274 !params.user_gesture && 275 !CommandLine::ForCurrentProcess()->HasSwitch( 276 switches::kDisablePopupBlocking)) { 277 if (popup_blocker_helper->MaybeBlockPopup(nav_params, 278 blink::WebWindowFeatures())) { 279 return NULL; 280 } 281 } 282 283 if (disposition == CURRENT_TAB) { 284 // Only prerender for a current-tab navigation to avoid session storage 285 // namespace issues. 286 nav_params.target_contents = source; 287 prerender::PrerenderManager* prerender_manager = 288 prerender::PrerenderManagerFactory::GetForProfile(profile); 289 if (prerender_manager && 290 prerender_manager->MaybeUsePrerenderedPage(params.url, &nav_params)) { 291 return nav_params.target_contents; 292 } 293 } 294 295 return WebContentsDelegateAndroid::OpenURLFromTab(source, params); 296 } 297 298 void ChromeWebContentsDelegateAndroid::AddNewContents( 299 WebContents* source, 300 WebContents* new_contents, 301 WindowOpenDisposition disposition, 302 const gfx::Rect& initial_pos, 303 bool user_gesture, 304 bool* was_blocked) { 305 // No code for this yet. 306 DCHECK_NE(disposition, SAVE_TO_DISK); 307 // Can't create a new contents for the current tab - invalid case. 308 DCHECK_NE(disposition, CURRENT_TAB); 309 310 TabHelpers::AttachTabHelpers(new_contents); 311 312 JNIEnv* env = AttachCurrentThread(); 313 ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env); 314 bool handled = false; 315 if (!obj.is_null()) { 316 handled = Java_ChromeWebContentsDelegateAndroid_addNewContents( 317 env, 318 obj.obj(), 319 reinterpret_cast<intptr_t>(source), 320 reinterpret_cast<intptr_t>(new_contents), 321 static_cast<jint>(disposition), 322 NULL, 323 user_gesture); 324 } 325 326 if (!handled) 327 delete new_contents; 328 } 329 330 void ChromeWebContentsDelegateAndroid::WebContentsCreated( 331 content::WebContents* source_contents, int opener_render_frame_id, 332 const base::string16& frame_name, const GURL& target_url, 333 content::WebContents* new_contents) { 334 JNIEnv* env = AttachCurrentThread(); 335 ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env); 336 if (obj.is_null()) 337 return; 338 Java_ChromeWebContentsDelegateAndroid_webContentsCreated(env, obj.obj(), 339 reinterpret_cast<intptr_t>(source_contents), opener_render_frame_id, 340 base::android::ConvertUTF16ToJavaString(env, frame_name).Release(), 341 base::android::ConvertUTF8ToJavaString(env, target_url.spec()).Release(), 342 reinterpret_cast<intptr_t>(new_contents)); 343 } 344 345 } // namespace android 346 } // namespace chrome 347