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 "android_webview/native/aw_web_contents_delegate.h" 6 7 #include "android_webview/browser/aw_javascript_dialog_manager.h" 8 #include "android_webview/browser/find_helper.h" 9 #include "android_webview/native/aw_contents.h" 10 #include "android_webview/native/aw_contents_io_thread_client_impl.h" 11 #include "android_webview/native/permission/media_access_permission_request.h" 12 #include "android_webview/native/permission/permission_request_handler.h" 13 #include "base/android/jni_array.h" 14 #include "base/android/jni_string.h" 15 #include "base/android/scoped_java_ref.h" 16 #include "base/lazy_instance.h" 17 #include "base/message_loop/message_loop.h" 18 #include "base/strings/string_util.h" 19 #include "content/public/browser/render_process_host.h" 20 #include "content/public/browser/render_view_host.h" 21 #include "content/public/browser/web_contents.h" 22 #include "content/public/common/file_chooser_params.h" 23 #include "content/public/common/media_stream_request.h" 24 #include "jni/AwWebContentsDelegate_jni.h" 25 #include "ui/shell_dialogs/selected_file_info.h" 26 27 using base::android::AttachCurrentThread; 28 using base::android::ConvertUTF16ToJavaString; 29 using base::android::ConvertUTF8ToJavaString; 30 using base::android::ScopedJavaLocalRef; 31 using content::FileChooserParams; 32 using content::WebContents; 33 34 namespace android_webview { 35 36 namespace { 37 38 // WARNING: these constants are exposed in the public interface Java side, so 39 // must remain in sync with what clients are expecting. 40 const int kFileChooserModeOpenMultiple = 1 << 0; 41 const int kFileChooserModeOpenFolder = 1 << 1; 42 43 base::LazyInstance<AwJavaScriptDialogManager>::Leaky 44 g_javascript_dialog_manager = LAZY_INSTANCE_INITIALIZER; 45 } 46 47 AwWebContentsDelegate::AwWebContentsDelegate( 48 JNIEnv* env, 49 jobject obj) 50 : WebContentsDelegateAndroid(env, obj), 51 is_fullscreen_(false) { 52 } 53 54 AwWebContentsDelegate::~AwWebContentsDelegate() { 55 } 56 57 content::JavaScriptDialogManager* 58 AwWebContentsDelegate::GetJavaScriptDialogManager() { 59 return g_javascript_dialog_manager.Pointer(); 60 } 61 62 void AwWebContentsDelegate::FindReply(WebContents* web_contents, 63 int request_id, 64 int number_of_matches, 65 const gfx::Rect& selection_rect, 66 int active_match_ordinal, 67 bool final_update) { 68 AwContents* aw_contents = AwContents::FromWebContents(web_contents); 69 if (!aw_contents) 70 return; 71 72 aw_contents->GetFindHelper()->HandleFindReply(request_id, 73 number_of_matches, 74 active_match_ordinal, 75 final_update); 76 } 77 78 void AwWebContentsDelegate::CanDownload( 79 content::RenderViewHost* source, 80 const GURL& url, 81 const std::string& request_method, 82 const base::Callback<void(bool)>& callback) { 83 // Android webview intercepts download in its resource dispatcher host 84 // delegate, so should not reach here. 85 NOTREACHED(); 86 callback.Run(false); 87 } 88 89 void AwWebContentsDelegate::RunFileChooser(WebContents* web_contents, 90 const FileChooserParams& params) { 91 JNIEnv* env = AttachCurrentThread(); 92 ScopedJavaLocalRef<jobject> java_delegate = GetJavaDelegate(env); 93 if (!java_delegate.obj()) 94 return; 95 96 int mode_flags = 0; 97 if (params.mode == FileChooserParams::OpenMultiple) { 98 mode_flags |= kFileChooserModeOpenMultiple; 99 } else if (params.mode == FileChooserParams::UploadFolder) { 100 // Folder implies multiple in Chrome. 101 mode_flags |= kFileChooserModeOpenMultiple | kFileChooserModeOpenFolder; 102 } else if (params.mode == FileChooserParams::Save) { 103 // Save not supported, so cancel it. 104 web_contents->GetRenderViewHost()->FilesSelectedInChooser( 105 std::vector<ui::SelectedFileInfo>(), 106 params.mode); 107 return; 108 } else { 109 DCHECK_EQ(FileChooserParams::Open, params.mode); 110 } 111 Java_AwWebContentsDelegate_runFileChooser(env, 112 java_delegate.obj(), 113 web_contents->GetRenderProcessHost()->GetID(), 114 web_contents->GetRenderViewHost()->GetRoutingID(), 115 mode_flags, 116 ConvertUTF16ToJavaString(env, 117 JoinString(params.accept_types, ',')).obj(), 118 params.title.empty() ? NULL : 119 ConvertUTF16ToJavaString(env, params.title).obj(), 120 params.default_file_name.empty() ? NULL : 121 ConvertUTF8ToJavaString(env, params.default_file_name.value()).obj(), 122 params.capture); 123 } 124 125 void AwWebContentsDelegate::AddNewContents(WebContents* source, 126 WebContents* new_contents, 127 WindowOpenDisposition disposition, 128 const gfx::Rect& initial_pos, 129 bool user_gesture, 130 bool* was_blocked) { 131 JNIEnv* env = AttachCurrentThread(); 132 133 bool is_dialog = disposition == NEW_POPUP; 134 ScopedJavaLocalRef<jobject> java_delegate = GetJavaDelegate(env); 135 bool create_popup = false; 136 137 if (java_delegate.obj()) { 138 create_popup = Java_AwWebContentsDelegate_addNewContents(env, 139 java_delegate.obj(), is_dialog, user_gesture); 140 } 141 142 if (create_popup) { 143 // The embedder would like to display the popup and we will receive 144 // a callback from them later with an AwContents to use to display 145 // it. The source AwContents takes ownership of the new WebContents 146 // until then, and when the callback is made we will swap the WebContents 147 // out into the new AwContents. 148 AwContents::FromWebContents(source)->SetPendingWebContentsForPopup( 149 make_scoped_ptr(new_contents)); 150 // Hide the WebContents for the pop up now, we will show it again 151 // when the user calls us back with an AwContents to use to show it. 152 new_contents->WasHidden(); 153 } else { 154 // The embedder has forgone their chance to display this popup 155 // window, so we're done with the WebContents now. We use 156 // DeleteSoon as WebContentsImpl may call methods on |new_contents| 157 // after this method returns. 158 base::MessageLoop::current()->DeleteSoon(FROM_HERE, new_contents); 159 } 160 161 if (was_blocked) { 162 *was_blocked = !create_popup; 163 } 164 } 165 166 // Notifies the delegate about the creation of a new WebContents. This 167 // typically happens when popups are created. 168 void AwWebContentsDelegate::WebContentsCreated( 169 WebContents* source_contents, 170 int opener_render_frame_id, 171 const base::string16& frame_name, 172 const GURL& target_url, 173 content::WebContents* new_contents) { 174 AwContentsIoThreadClientImpl::RegisterPendingContents(new_contents); 175 } 176 177 void AwWebContentsDelegate::CloseContents(WebContents* source) { 178 JNIEnv* env = AttachCurrentThread(); 179 180 ScopedJavaLocalRef<jobject> java_delegate = GetJavaDelegate(env); 181 if (java_delegate.obj()) { 182 Java_AwWebContentsDelegate_closeContents(env, java_delegate.obj()); 183 } 184 } 185 186 void AwWebContentsDelegate::ActivateContents(WebContents* contents) { 187 JNIEnv* env = AttachCurrentThread(); 188 189 ScopedJavaLocalRef<jobject> java_delegate = GetJavaDelegate(env); 190 if (java_delegate.obj()) { 191 Java_AwWebContentsDelegate_activateContents(env, java_delegate.obj()); 192 } 193 } 194 195 void AwWebContentsDelegate::RequestMediaAccessPermission( 196 WebContents* web_contents, 197 const content::MediaStreamRequest& request, 198 const content::MediaResponseCallback& callback) { 199 AwContents* aw_contents = AwContents::FromWebContents(web_contents); 200 if (!aw_contents) { 201 callback.Run(content::MediaStreamDevices(), 202 content::MEDIA_DEVICE_INVALID_STATE, 203 scoped_ptr<content::MediaStreamUI>().Pass()); 204 return; 205 } 206 aw_contents->GetPermissionRequestHandler()->SendRequest( 207 scoped_ptr<AwPermissionRequestDelegate>( 208 new MediaAccessPermissionRequest(request, callback))); 209 } 210 211 void AwWebContentsDelegate::ToggleFullscreenModeForTab( 212 content::WebContents* web_contents, bool enter_fullscreen) { 213 JNIEnv* env = AttachCurrentThread(); 214 215 ScopedJavaLocalRef<jobject> java_delegate = GetJavaDelegate(env); 216 if (java_delegate.obj()) { 217 Java_AwWebContentsDelegate_toggleFullscreenModeForTab( 218 env, java_delegate.obj(), enter_fullscreen); 219 } 220 is_fullscreen_ = enter_fullscreen; 221 web_contents->GetRenderViewHost()->WasResized(); 222 } 223 224 bool AwWebContentsDelegate::IsFullscreenForTabOrPending( 225 const content::WebContents* web_contents) const { 226 return is_fullscreen_; 227 } 228 229 230 static void FilesSelectedInChooser( 231 JNIEnv* env, jclass clazz, 232 jint process_id, jint render_id, jint mode_flags, 233 jobjectArray file_paths, jobjectArray display_names) { 234 content::RenderViewHost* rvh = content::RenderViewHost::FromID(process_id, 235 render_id); 236 if (!rvh) 237 return; 238 239 std::vector<std::string> file_path_str; 240 std::vector<std::string> display_name_str; 241 // Note file_paths maybe NULL, but this will just yield a zero-length vector. 242 base::android::AppendJavaStringArrayToStringVector(env, file_paths, 243 &file_path_str); 244 base::android::AppendJavaStringArrayToStringVector(env, display_names, 245 &display_name_str); 246 std::vector<ui::SelectedFileInfo> files; 247 files.reserve(file_path_str.size()); 248 for (size_t i = 0; i < file_path_str.size(); ++i) { 249 GURL url(file_path_str[i]); 250 if (!url.is_valid()) 251 continue; 252 base::FilePath path(url.SchemeIsFile() ? url.path() : file_path_str[i]); 253 ui::SelectedFileInfo file_info(path, base::FilePath()); 254 if (!display_name_str[i].empty()) 255 file_info.display_name = display_name_str[i]; 256 files.push_back(file_info); 257 } 258 FileChooserParams::Mode mode; 259 if (mode_flags & kFileChooserModeOpenFolder) { 260 mode = FileChooserParams::UploadFolder; 261 } else if (mode_flags & kFileChooserModeOpenMultiple) { 262 mode = FileChooserParams::OpenMultiple; 263 } else { 264 mode = FileChooserParams::Open; 265 } 266 DVLOG(0) << "File Chooser result: mode = " << mode 267 << ", file paths = " << JoinString(file_path_str, ":"); 268 rvh->FilesSelectedInChooser(files, mode); 269 } 270 271 bool RegisterAwWebContentsDelegate(JNIEnv* env) { 272 return RegisterNativesImpl(env); 273 } 274 275 } // namespace android_webview 276