1/* 2 * Copyright (C) 2012 Igalia S.L. 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Library General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Library General Public License for more details. 13 * 14 * You should have received a copy of the GNU Library General Public License 15 * along with this library; see the file COPYING.LIB. If not, write to 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 * Boston, MA 02110-1301, USA. 18 */ 19 20#include "config.h" 21#include "webkitfilechooserrequest.h" 22 23#include "FileChooser.h" 24#include "FileSystem.h" 25#include "webkitfilechooserrequestprivate.h" 26#include "webkitglobalsprivate.h" 27#include <glib/gi18n-lib.h> 28#include <wtf/gobject/GOwnPtr.h> 29#include <wtf/gobject/GRefPtr.h> 30#include <wtf/text/CString.h> 31 32using namespace WebCore; 33 34/** 35 * SECTION:webkitfilechooserrequest 36 * @Short_description: A request to open a file chooser 37 * @Title: WebKitFileChooserRequest 38 * @See_also: #WebKitWebView 39 * 40 * Whenever the user interacts with an <input type='file' /> 41 * HTML element, WebKit will need to show a dialog to choose one or 42 * more files to be uploaded to the server along with the rest of the 43 * form data. For that to happen in a general way, instead of just 44 * opening a #GtkFileChooserDialog (which might be not desirable in 45 * some cases, such as when an embedding applications prefers to use 46 * its own file chooser dialog), WebKit will fire the 47 * #WebKitWebView::run-file-chooser signal with a 48 * #WebKitFileChooserRequest object, which will allow the client 49 * application to specify the files to be selected, to inspect the 50 * details of the request (e.g. if multiple selection should be 51 * allowed) and to cancel the request, in case nothing was selected. 52 * 53 * In case the client application does not wish to handle this signal, 54 * WebKit will provide a default handler which will asynchronously run 55 * a regular #GtkFileChooserDialog for the user to interact with. 56 */ 57G_DEFINE_TYPE(WebKitFileChooserRequest, webkit_file_chooser_request, G_TYPE_OBJECT) 58 59struct _WebKitFileChooserRequestPrivate { 60 RefPtr<FileChooser> chooser; 61 GRefPtr<GtkFileFilter> filter; 62 GRefPtr<GPtrArray> mimeTypes; 63 GRefPtr<GPtrArray> selectedFiles; 64}; 65 66enum { 67 PROP_0, 68 PROP_FILTER, 69 PROP_MIME_TYPES, 70 PROP_SELECT_MULTIPLE, 71 PROP_SELECTED_FILES, 72}; 73 74static void webkit_file_chooser_request_init(WebKitFileChooserRequest* request) 75{ 76 request->priv = G_TYPE_INSTANCE_GET_PRIVATE(request, WEBKIT_TYPE_FILE_CHOOSER_REQUEST, WebKitFileChooserRequestPrivate); 77 new (request->priv) WebKitFileChooserRequestPrivate(); 78} 79 80static void webkit_file_chooser_request_finalize(GObject* object) 81{ 82 WebKitFileChooserRequest* request = WEBKIT_FILE_CHOOSER_REQUEST(object); 83 84 request->priv->~WebKitFileChooserRequestPrivate(); 85 G_OBJECT_CLASS(webkit_file_chooser_request_parent_class)->finalize(object); 86} 87 88static void webkit_file_chooser_request_get_property(GObject* object, guint propId, GValue* value, GParamSpec* paramSpec) 89{ 90 WebKitFileChooserRequest* request = WEBKIT_FILE_CHOOSER_REQUEST(object); 91 switch (propId) { 92 case PROP_FILTER: 93 g_value_set_object(value, webkit_file_chooser_request_get_mime_types_filter(request)); 94 break; 95 case PROP_MIME_TYPES: 96 g_value_set_boxed(value, webkit_file_chooser_request_get_mime_types(request)); 97 break; 98 case PROP_SELECT_MULTIPLE: 99 g_value_set_boolean(value, webkit_file_chooser_request_get_select_multiple(request)); 100 break; 101 case PROP_SELECTED_FILES: 102 g_value_set_boxed(value, webkit_file_chooser_request_get_selected_files(request)); 103 break; 104 default: 105 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, paramSpec); 106 break; 107 } 108} 109 110static void webkit_file_chooser_request_class_init(WebKitFileChooserRequestClass* requestClass) 111{ 112 GObjectClass* objectClass = G_OBJECT_CLASS(requestClass); 113 objectClass->finalize = webkit_file_chooser_request_finalize; 114 objectClass->get_property = webkit_file_chooser_request_get_property; 115 g_type_class_add_private(requestClass, sizeof(WebKitFileChooserRequestPrivate)); 116 117 /** 118 * WebKitFileChooserRequest:filter: 119 * 120 * The filter currently associated with the request. See 121 * webkit_file_chooser_request_get_mime_types_filter() for more 122 * details. 123 * 124 * Since: 1.10 125 */ 126 g_object_class_install_property(objectClass, 127 PROP_FILTER, 128 g_param_spec_object("filter", 129 _("MIME types filter"), 130 _("The filter currently associated with the request"), 131 GTK_TYPE_FILE_FILTER, 132 WEBKIT_PARAM_READABLE)); 133 /** 134 * WebKitFileChooserRequest:mime-types: 135 * 136 * A %NULL-terminated array of strings containing the list of MIME 137 * types the file chooser dialog should handle. See 138 * webkit_file_chooser_request_get_mime_types() for more details. 139 * 140 * Since: 1.10 141 */ 142 g_object_class_install_property(objectClass, 143 PROP_MIME_TYPES, 144 g_param_spec_boxed("mime-types", 145 _("MIME types"), 146 _("The list of MIME types associated with the request"), 147 G_TYPE_STRV, 148 WEBKIT_PARAM_READABLE)); /** 149 * WebKitFileChooserRequest:select-multiple: 150 * 151 * Whether the file chooser should allow selecting multiple 152 * files. See 153 * webkit_file_chooser_request_get_select_multiple() for 154 * more details. 155 * 156 * Since: 1.10 157 */ 158 g_object_class_install_property(objectClass, 159 PROP_SELECT_MULTIPLE, 160 g_param_spec_boolean("select-multiple", 161 _("Select multiple files"), 162 _("Whether the file chooser should allow selecting multiple files"), 163 FALSE, 164 WEBKIT_PARAM_READABLE)); 165 /** 166 * WebKitFileChooserRequest:selected-files: 167 * 168 * A %NULL-terminated array of strings containing the list of 169 * selected files associated to the current request. See 170 * webkit_file_chooser_request_get_selected_files() for more details. 171 * 172 * Since: 1.10 173 */ 174 g_object_class_install_property(objectClass, 175 PROP_SELECTED_FILES, 176 g_param_spec_boxed("selected-files", 177 _("Selected files"), 178 _("The list of selected files associated with the request"), 179 G_TYPE_STRV, 180 WEBKIT_PARAM_READABLE)); 181} 182 183WebKitFileChooserRequest* webkit_file_chooser_request_create(PassRefPtr<FileChooser> chooser) 184{ 185 WebKitFileChooserRequest* request = WEBKIT_FILE_CHOOSER_REQUEST(g_object_new(WEBKIT_TYPE_FILE_CHOOSER_REQUEST, NULL)); 186 request->priv->chooser = chooser; 187 return request; 188} 189 190/** 191 * webkit_file_chooser_request_get_mime_types: 192 * @request: a #WebKitFileChooserRequest 193 * 194 * Get the list of MIME types the file chooser dialog should handle, 195 * in the format specified in RFC 2046 for "media types". Its contents 196 * depend on the value of the 'accept' attribute for HTML input 197 * elements. This function should normally be called before presenting 198 * the file chooser dialog to the user, to decide whether to allow the 199 * user to select multiple files at once or only one. 200 * 201 * Returns: (array zero-terminated=1) (transfer none): a 202 * %NULL-terminated array of strings if a list of accepted MIME types 203 * is defined or %NULL otherwise, meaning that any MIME type should be 204 * accepted. This array and its contents are owned by WebKitGTK+ and 205 * should not be modified or freed. 206 * 207 * Since: 1.10 208 */ 209const gchar* const* webkit_file_chooser_request_get_mime_types(WebKitFileChooserRequest* request) 210{ 211 g_return_val_if_fail(WEBKIT_IS_FILE_CHOOSER_REQUEST(request), 0); 212 if (request->priv->mimeTypes) 213 return reinterpret_cast<gchar**>(request->priv->mimeTypes->pdata); 214 215 FileChooserSettings settings = request->priv->chooser->settings(); 216 size_t numOfMimeTypes = settings.acceptMIMETypes.size(); 217 if (!numOfMimeTypes) 218 return 0; 219 220 request->priv->mimeTypes = adoptGRef(g_ptr_array_new_with_free_func(g_free)); 221 for (size_t i = 0; i < numOfMimeTypes; ++i) { 222 String mimeTypeString = settings.acceptMIMETypes[i]; 223 if (mimeTypeString.isEmpty()) 224 continue; 225 g_ptr_array_add(request->priv->mimeTypes.get(), g_strdup(mimeTypeString.utf8().data())); 226 } 227 g_ptr_array_add(request->priv->mimeTypes.get(), 0); 228 229 return reinterpret_cast<gchar**>(request->priv->mimeTypes->pdata); 230} 231 232/** 233 * webkit_file_chooser_request_get_mime_types_filter: 234 * @request: a #WebKitFileChooserRequest 235 * 236 * Get the filter currently associated with the request, ready to be 237 * used by #GtkFileChooser. This function should normally be called 238 * before presenting the file chooser dialog to the user, to decide 239 * whether to apply a filter so the user would not be allowed to 240 * select files with other MIME types. 241 * 242 * See webkit_file_chooser_request_get_mime_types() if you are 243 * interested in getting the list of accepted MIME types. 244 * 245 * Returns: (transfer none): a #GtkFileFilter if a list of accepted 246 * MIME types is defined or %NULL otherwise. The returned object is 247 * owned by WebKitGTK+ should not be modified or freed. 248 * 249 * Since: 1.10 250 */ 251GtkFileFilter* webkit_file_chooser_request_get_mime_types_filter(WebKitFileChooserRequest* request) 252{ 253 g_return_val_if_fail(WEBKIT_IS_FILE_CHOOSER_REQUEST(request), 0); 254 if (request->priv->filter) 255 return request->priv->filter.get(); 256 257 FileChooserSettings settings = request->priv->chooser->settings(); 258 size_t numOfMimeTypes = settings.acceptMIMETypes.size(); 259 if (!numOfMimeTypes) 260 return 0; 261 262 // Do not use adoptGRef here, since we want to sink the floating 263 // reference for the new instance of GtkFileFilter, so we make 264 // sure we keep the ownership during the lifetime of the request. 265 request->priv->filter = gtk_file_filter_new(); 266 for (size_t i = 0; i < numOfMimeTypes; ++i) { 267 String mimeTypeString = settings.acceptMIMETypes[i]; 268 if (mimeTypeString.isEmpty()) 269 continue; 270 gtk_file_filter_add_mime_type(request->priv->filter.get(), mimeTypeString.utf8().data()); 271 } 272 273 return request->priv->filter.get(); 274} 275 276/** 277 * webkit_file_chooser_request_get_select_multiple: 278 * @request: a #WebKitFileChooserRequest 279 * 280 * Determine whether the file chooser associated to this 281 * #WebKitFileChooserRequest should allow selecting multiple files, 282 * which depends on the HTML input element having a 'multiple' 283 * attribute defined. 284 * 285 * Returns: %TRUE if the file chooser should allow selecting multiple files or %FALSE otherwise. 286 * 287 * Since: 1.10 288 */ 289gboolean webkit_file_chooser_request_get_select_multiple(WebKitFileChooserRequest* request) 290{ 291 g_return_val_if_fail(WEBKIT_IS_FILE_CHOOSER_REQUEST(request), FALSE); 292 return request->priv->chooser->settings().allowsMultipleFiles; 293} 294 295/** 296 * webkit_file_chooser_request_select_files: 297 * @request: a #WebKitFileChooserRequest 298 * @files: (array zero-terminated=1) (transfer none): a 299 * %NULL-terminated array of strings, containing paths to local files. 300 * 301 * Ask WebKit to select local files for upload and complete the 302 * request. 303 * 304 * Since: 1.10 305 */ 306void webkit_file_chooser_request_select_files(WebKitFileChooserRequest* request, const gchar* const* files) 307{ 308 g_return_if_fail(WEBKIT_IS_FILE_CHOOSER_REQUEST(request)); 309 g_return_if_fail(files); 310 311 Vector<String> names; 312 GRefPtr<GPtrArray> selectedFiles = adoptGRef(g_ptr_array_new_with_free_func(g_free)); 313 314 for (int i = 0; files[i]; i++) { 315 names.append(filenameToString(files[i])); 316 g_ptr_array_add(selectedFiles.get(), g_strdup(files[i])); 317 } 318 319 g_ptr_array_add(selectedFiles.get(), 0); 320 request->priv->chooser->chooseFiles(names); 321 request->priv->selectedFiles = selectedFiles; 322} 323 324/** 325 * webkit_file_chooser_request_get_selected_files: 326 * @request: a #WebKitFileChooserRequest 327 * 328 * Get the list of selected files currently associated to the 329 * request. Initially, the return value of this method contains any 330 * files selected in previous file chooser requests for this HTML 331 * input element. Once webkit_file_chooser_request_select_files, the 332 * value will reflect whatever files are given. 333 * 334 * This function should normally be called only before presenting the 335 * file chooser dialog to the user, to decide whether to perform some 336 * extra action, like pre-selecting the files from a previous request. 337 * 338 * Returns: (array zero-terminated=1) (transfer none): a 339 * %NULL-terminated array of strings if there are selected files 340 * associated with the request or %NULL otherwise. This array and its 341 * contents are owned by WebKitGTK+ and should not be modified or 342 * freed. 343 * 344 * Since: 1.10 345 */ 346const gchar* const* webkit_file_chooser_request_get_selected_files(WebKitFileChooserRequest* request) 347{ 348 g_return_val_if_fail(WEBKIT_IS_FILE_CHOOSER_REQUEST(request), 0); 349 if (request->priv->selectedFiles) 350 return reinterpret_cast<gchar**>(request->priv->selectedFiles->pdata); 351 352 FileChooserSettings settings = request->priv->chooser->settings(); 353 size_t numOfFiles = settings.selectedFiles.size(); 354 if (!numOfFiles) 355 return 0; 356 357 request->priv->selectedFiles = adoptGRef(g_ptr_array_new_with_free_func(g_free)); 358 for (size_t i = 0; i < numOfFiles; ++i) { 359 if (settings.selectedFiles[i].isEmpty()) 360 continue; 361 CString filename = fileSystemRepresentation(settings.selectedFiles[i]); 362 g_ptr_array_add(request->priv->selectedFiles.get(), g_strdup(filename.data())); 363 } 364 g_ptr_array_add(request->priv->selectedFiles.get(), 0); 365 366 return reinterpret_cast<gchar**>(request->priv->selectedFiles->pdata); 367} 368