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