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 "ui/base/dragdrop/gtk_dnd_util.h" 6 7 #include <string> 8 9 #include "base/logging.h" 10 #include "base/pickle.h" 11 #include "base/strings/utf_string_conversions.h" 12 #include "ui/base/clipboard/custom_data_helper.h" 13 #include "url/gurl.h" 14 15 namespace ui { 16 17 namespace { 18 19 const int kBitsPerByte = 8; 20 21 void AddTargetToList(GtkTargetList* targets, int target_code) { 22 switch (target_code) { 23 case ui::TEXT_PLAIN: 24 gtk_target_list_add_text_targets(targets, ui::TEXT_PLAIN); 25 break; 26 27 case ui::TEXT_URI_LIST: 28 gtk_target_list_add_uri_targets(targets, ui::TEXT_URI_LIST); 29 break; 30 31 case ui::TEXT_HTML: 32 gtk_target_list_add( 33 targets, ui::GetAtomForTarget(ui::TEXT_HTML), 0, ui::TEXT_HTML); 34 break; 35 36 case ui::NETSCAPE_URL: 37 gtk_target_list_add(targets, 38 ui::GetAtomForTarget(ui::NETSCAPE_URL), 0, ui::NETSCAPE_URL); 39 break; 40 41 case ui::CHROME_TAB: 42 case ui::CHROME_BOOKMARK_ITEM: 43 case ui::CHROME_NAMED_URL: 44 gtk_target_list_add(targets, ui::GetAtomForTarget(target_code), 45 GTK_TARGET_SAME_APP, target_code); 46 break; 47 48 case ui::DIRECT_SAVE_FILE: 49 gtk_target_list_add(targets, 50 ui::GetAtomForTarget(ui::DIRECT_SAVE_FILE), 0, ui::DIRECT_SAVE_FILE); 51 break; 52 53 case ui::CUSTOM_DATA: 54 gtk_target_list_add(targets, 55 ui::GetAtomForTarget(ui::CUSTOM_DATA), 0, ui::CUSTOM_DATA); 56 break; 57 58 default: 59 NOTREACHED() << " Unexpected target code: " << target_code; 60 } 61 } 62 63 } // namespace 64 65 GdkAtom GetAtomForTarget(int target) { 66 switch (target) { 67 case CHROME_TAB: 68 static const GdkAtom kTabAtom = gdk_atom_intern( 69 "application/x-chrome-tab", false); 70 return kTabAtom; 71 72 case TEXT_HTML: 73 static const GdkAtom kHtmlAtom = gdk_atom_intern( 74 "text/html", false); 75 return kHtmlAtom; 76 77 case CHROME_BOOKMARK_ITEM: 78 static const GdkAtom kBookmarkAtom = gdk_atom_intern( 79 "application/x-chrome-bookmark-item", false); 80 return kBookmarkAtom; 81 82 case TEXT_PLAIN: 83 static const GdkAtom kTextAtom= gdk_atom_intern( 84 "text/plain;charset=utf-8", false); 85 return kTextAtom; 86 87 case TEXT_URI_LIST: 88 static const GdkAtom kUrisAtom = gdk_atom_intern( 89 "text/uri-list", false); 90 return kUrisAtom; 91 92 case CHROME_NAMED_URL: 93 static const GdkAtom kNamedUrl = gdk_atom_intern( 94 "application/x-chrome-named-url", false); 95 return kNamedUrl; 96 97 case NETSCAPE_URL: 98 static const GdkAtom kNetscapeUrl = gdk_atom_intern( 99 "_NETSCAPE_URL", false); 100 return kNetscapeUrl; 101 102 case TEXT_PLAIN_NO_CHARSET: 103 static const GdkAtom kTextNoCharsetAtom = gdk_atom_intern( 104 "text/plain", false); 105 return kTextNoCharsetAtom; 106 107 case DIRECT_SAVE_FILE: 108 static const GdkAtom kXdsAtom = gdk_atom_intern( 109 "XdndDirectSave0", false); 110 return kXdsAtom; 111 112 case CUSTOM_DATA: 113 static const GdkAtom kCustomData = gdk_atom_intern( 114 kMimeTypeWebCustomData, false); 115 return kCustomData; 116 117 default: 118 NOTREACHED(); 119 } 120 121 return NULL; 122 } 123 124 GtkTargetList* GetTargetListFromCodeMask(int code_mask) { 125 GtkTargetList* targets = gtk_target_list_new(NULL, 0); 126 127 for (size_t i = 1; i < INVALID_TARGET; i = i << 1) { 128 if (i == CHROME_WEBDROP_FILE_CONTENTS) 129 continue; 130 131 if (i & code_mask) 132 AddTargetToList(targets, i); 133 } 134 135 return targets; 136 } 137 138 void SetSourceTargetListFromCodeMask(GtkWidget* source, int code_mask) { 139 GtkTargetList* targets = GetTargetListFromCodeMask(code_mask); 140 gtk_drag_source_set_target_list(source, targets); 141 gtk_target_list_unref(targets); 142 } 143 144 void SetDestTargetList(GtkWidget* dest, const int* target_codes) { 145 GtkTargetList* targets = gtk_target_list_new(NULL, 0); 146 147 for (size_t i = 0; target_codes[i] != -1; ++i) { 148 AddTargetToList(targets, target_codes[i]); 149 } 150 151 gtk_drag_dest_set_target_list(dest, targets); 152 gtk_target_list_unref(targets); 153 } 154 155 void WriteURLWithName(GtkSelectionData* selection_data, 156 const GURL& url, 157 base::string16 title, 158 int type) { 159 if (title.empty()) { 160 // We prefer to not have empty titles. Set it to the filename extracted 161 // from the URL. 162 title = UTF8ToUTF16(url.ExtractFileName()); 163 } 164 165 switch (type) { 166 case TEXT_PLAIN: { 167 gtk_selection_data_set_text(selection_data, url.spec().c_str(), 168 url.spec().length()); 169 break; 170 } 171 case TEXT_URI_LIST: { 172 gchar* uri_array[2]; 173 uri_array[0] = strdup(url.spec().c_str()); 174 uri_array[1] = NULL; 175 gtk_selection_data_set_uris(selection_data, uri_array); 176 free(uri_array[0]); 177 break; 178 } 179 case CHROME_NAMED_URL: { 180 Pickle pickle; 181 pickle.WriteString(UTF16ToUTF8(title)); 182 pickle.WriteString(url.spec()); 183 gtk_selection_data_set( 184 selection_data, 185 GetAtomForTarget(ui::CHROME_NAMED_URL), 186 kBitsPerByte, 187 reinterpret_cast<const guchar*>(pickle.data()), 188 pickle.size()); 189 break; 190 } 191 case NETSCAPE_URL: { 192 // _NETSCAPE_URL format is URL + \n + title. 193 std::string utf8_text = url.spec() + "\n" + UTF16ToUTF8(title); 194 gtk_selection_data_set(selection_data, 195 gtk_selection_data_get_target(selection_data), 196 kBitsPerByte, 197 reinterpret_cast<const guchar*>(utf8_text.c_str()), 198 utf8_text.length()); 199 break; 200 } 201 202 default: { 203 NOTREACHED(); 204 break; 205 } 206 } 207 } 208 209 bool ExtractNamedURL(GtkSelectionData* selection_data, 210 GURL* url, 211 base::string16* title) { 212 if (!selection_data || gtk_selection_data_get_length(selection_data) <= 0) 213 return false; 214 215 Pickle data( 216 reinterpret_cast<const char*>( 217 gtk_selection_data_get_data(selection_data)), 218 gtk_selection_data_get_length(selection_data)); 219 PickleIterator iter(data); 220 std::string title_utf8, url_utf8; 221 if (!data.ReadString(&iter, &title_utf8) || 222 !data.ReadString(&iter, &url_utf8)) { 223 return false; 224 } 225 226 GURL gurl(url_utf8); 227 if (!gurl.is_valid()) 228 return false; 229 230 *url = gurl; 231 *title = UTF8ToUTF16(title_utf8); 232 return true; 233 } 234 235 bool ExtractURIList(GtkSelectionData* selection_data, std::vector<GURL>* urls) { 236 gchar** uris = gtk_selection_data_get_uris(selection_data); 237 if (!uris) 238 return false; 239 240 for (size_t i = 0; uris[i] != NULL; ++i) { 241 GURL url(uris[i]); 242 if (url.is_valid()) 243 urls->push_back(url); 244 } 245 246 g_strfreev(uris); 247 return true; 248 } 249 250 bool ExtractNetscapeURL(GtkSelectionData* selection_data, 251 GURL* url, 252 base::string16* title) { 253 if (!selection_data || gtk_selection_data_get_length(selection_data) <= 0) 254 return false; 255 256 // Find the first '\n' in the data. It is the separator between the url and 257 // the title. 258 std::string data( 259 reinterpret_cast<const char*>( 260 gtk_selection_data_get_data(selection_data)), 261 gtk_selection_data_get_length(selection_data)); 262 std::string::size_type newline = data.find('\n'); 263 if (newline == std::string::npos) 264 return false; 265 266 GURL gurl(data.substr(0, newline)); 267 if (!gurl.is_valid()) 268 return false; 269 270 *url = gurl; 271 *title = UTF8ToUTF16(data.substr(newline + 1)); 272 return true; 273 } 274 275 } // namespace ui 276