Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(264)

Side by Side Diff: content/browser/web_contents/web_drag_source_gtk.cc

Issue 231733005: Delete the GTK+ port of Chrome. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Remerge to ToT Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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 "content/browser/web_contents/web_drag_source_gtk.h"
6
7 #include <string>
8
9 #include "base/files/file.h"
10 #include "base/nix/mime_util_xdg.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/threading/thread_restrictions.h"
13 #include "content/browser/download/drag_download_file.h"
14 #include "content/browser/download/drag_download_util.h"
15 #include "content/browser/renderer_host/render_view_host_delegate.h"
16 #include "content/browser/renderer_host/render_view_host_impl.h"
17 #include "content/browser/web_contents/drag_utils_gtk.h"
18 #include "content/browser/web_contents/web_contents_impl.h"
19 #include "content/public/browser/content_browser_client.h"
20 #include "content/public/browser/web_contents_view.h"
21 #include "content/public/common/content_client.h"
22 #include "content/public/common/drop_data.h"
23 #include "net/base/filename_util.h"
24 #include "third_party/skia/include/core/SkBitmap.h"
25 #include "ui/base/clipboard/custom_data_helper.h"
26 #include "ui/base/dragdrop/gtk_dnd_util.h"
27 #include "ui/base/gtk/gtk_screen_util.h"
28 #include "ui/gfx/gtk_compat.h"
29 #include "ui/gfx/gtk_util.h"
30
31 using blink::WebDragOperation;
32 using blink::WebDragOperationsMask;
33 using blink::WebDragOperationNone;
34
35 namespace content {
36
37 WebDragSourceGtk::WebDragSourceGtk(WebContents* web_contents)
38 : web_contents_(static_cast<WebContentsImpl*>(web_contents)),
39 drag_pixbuf_(NULL),
40 drag_failed_(false),
41 drag_widget_(gtk_invisible_new()),
42 drag_context_(NULL),
43 drag_icon_(gtk_window_new(GTK_WINDOW_POPUP)) {
44 signals_.Connect(drag_widget_, "drag-failed",
45 G_CALLBACK(OnDragFailedThunk), this);
46 signals_.Connect(drag_widget_, "drag-begin",
47 G_CALLBACK(OnDragBeginThunk),
48 this);
49 signals_.Connect(drag_widget_, "drag-end",
50 G_CALLBACK(OnDragEndThunk), this);
51 signals_.Connect(drag_widget_, "drag-data-get",
52 G_CALLBACK(OnDragDataGetThunk), this);
53
54 signals_.Connect(drag_icon_, "expose-event",
55 G_CALLBACK(OnDragIconExposeThunk), this);
56 }
57
58 WebDragSourceGtk::~WebDragSourceGtk() {
59 // Break the current drag, if any.
60 if (drop_data_) {
61 gtk_grab_add(drag_widget_);
62 gtk_grab_remove(drag_widget_);
63 drop_data_.reset();
64 }
65
66 gtk_widget_destroy(drag_widget_);
67 gtk_widget_destroy(drag_icon_);
68 }
69
70 bool WebDragSourceGtk::StartDragging(const DropData& drop_data,
71 WebDragOperationsMask allowed_ops,
72 GdkEventButton* last_mouse_down,
73 const SkBitmap& image,
74 const gfx::Vector2d& image_offset) {
75 // Guard against re-starting before previous drag completed.
76 if (drag_context_) {
77 NOTREACHED();
78 return false;
79 }
80
81 int targets_mask = ui::RENDERER_TAINT;
82
83 if (!drop_data.text.string().empty())
84 targets_mask |= ui::TEXT_PLAIN;
85 if (drop_data.url.is_valid()) {
86 targets_mask |= ui::TEXT_URI_LIST;
87 targets_mask |= ui::CHROME_NAMED_URL;
88 targets_mask |= ui::NETSCAPE_URL;
89 }
90 if (!drop_data.html.string().empty())
91 targets_mask |= ui::TEXT_HTML;
92 if (!drop_data.file_contents.empty())
93 targets_mask |= ui::CHROME_WEBDROP_FILE_CONTENTS;
94 if (!drop_data.download_metadata.empty() &&
95 ParseDownloadMetadata(drop_data.download_metadata,
96 &wide_download_mime_type_,
97 &download_file_name_,
98 &download_url_)) {
99 targets_mask |= ui::DIRECT_SAVE_FILE;
100 }
101 if (!drop_data.custom_data.empty())
102 targets_mask |= ui::CUSTOM_DATA;
103
104 // NOTE: Begin a drag even if no targets present. Otherwise, things like
105 // draggable list elements will not work.
106
107 drop_data_.reset(new DropData(drop_data));
108
109 // The image we get from WebKit makes heavy use of alpha-shading. This looks
110 // bad on non-compositing WMs. Fall back to the default drag icon.
111 if (!image.isNull() && ui::IsScreenComposited())
112 drag_pixbuf_ = gfx::GdkPixbufFromSkBitmap(image);
113 image_offset_ = image_offset;
114
115 GtkTargetList* list = ui::GetTargetListFromCodeMask(targets_mask);
116 if (targets_mask & ui::CHROME_WEBDROP_FILE_CONTENTS) {
117 // Looking up the mime type can hit the disk. http://crbug.com/84896
118 base::ThreadRestrictions::ScopedAllowIO allow_io;
119 drag_file_mime_type_ = gdk_atom_intern(
120 base::nix::GetDataMimeType(drop_data.file_contents).c_str(), FALSE);
121 gtk_target_list_add(list, drag_file_mime_type_,
122 0, ui::CHROME_WEBDROP_FILE_CONTENTS);
123 }
124
125 drag_failed_ = false;
126 // If we don't pass an event, GDK won't know what event time to start grabbing
127 // mouse events. Technically it's the mouse motion event and not the mouse
128 // down event that causes the drag, but there's no reliable way to know
129 // *which* motion event initiated the drag, so this will have to do.
130 // TODO(estade): This can sometimes be very far off, e.g. if the user clicks
131 // and holds and doesn't start dragging for a long time. I doubt it matters
132 // much, but we should probably look into the possibility of getting the
133 // initiating event from webkit.
134 drag_context_ = gtk_drag_begin(drag_widget_, list,
135 WebDragOpToGdkDragAction(allowed_ops),
136 1, // Drags are always initiated by the left button.
137 reinterpret_cast<GdkEvent*>(last_mouse_down));
138 // The drag adds a ref; let it own the list.
139 gtk_target_list_unref(list);
140
141 // Sometimes the drag fails to start; |context| will be NULL and we won't
142 // get a drag-end signal.
143 if (!drag_context_) {
144 drag_failed_ = true;
145 drop_data_.reset();
146 return false;
147 }
148
149 return true;
150 }
151
152 void WebDragSourceGtk::OnDragDataGet(GtkWidget* sender,
153 GdkDragContext* context,
154 GtkSelectionData* selection_data,
155 guint target_type,
156 guint time) {
157 const int kBitsPerByte = 8;
158
159 switch (target_type) {
160 case ui::TEXT_PLAIN: {
161 std::string utf8_text = base::UTF16ToUTF8(drop_data_->text.string());
162 gtk_selection_data_set_text(selection_data, utf8_text.c_str(),
163 utf8_text.length());
164 break;
165 }
166
167 case ui::TEXT_HTML: {
168 // TODO(estade): change relative links to be absolute using
169 // |html_base_url|.
170 std::string utf8_text = base::UTF16ToUTF8(drop_data_->html.string());
171 gtk_selection_data_set(selection_data,
172 ui::GetAtomForTarget(ui::TEXT_HTML),
173 kBitsPerByte,
174 reinterpret_cast<const guchar*>(utf8_text.c_str()),
175 utf8_text.length());
176 break;
177 }
178
179 case ui::TEXT_URI_LIST:
180 case ui::CHROME_NAMED_URL:
181 case ui::NETSCAPE_URL: {
182 ui::WriteURLWithName(selection_data, drop_data_->url,
183 drop_data_->url_title, target_type);
184 break;
185 }
186
187 case ui::CHROME_WEBDROP_FILE_CONTENTS: {
188 gtk_selection_data_set(
189 selection_data,
190 drag_file_mime_type_, kBitsPerByte,
191 reinterpret_cast<const guchar*>(drop_data_->file_contents.data()),
192 drop_data_->file_contents.length());
193 break;
194 }
195
196 case ui::DIRECT_SAVE_FILE: {
197 char status_code = 'E';
198
199 // Retrieves the full file path (in file URL format) provided by the
200 // drop target by reading from the source window's XdndDirectSave0
201 // property.
202 gint file_url_len = 0;
203 guchar* file_url_value = NULL;
204 if (gdk_property_get(context->source_window,
205 ui::GetAtomForTarget(ui::DIRECT_SAVE_FILE),
206 ui::GetAtomForTarget(ui::TEXT_PLAIN_NO_CHARSET),
207 0,
208 1024,
209 FALSE,
210 NULL,
211 NULL,
212 &file_url_len,
213 &file_url_value) &&
214 file_url_value) {
215 // Convert from the file url to the file path.
216 GURL file_url(std::string(reinterpret_cast<char*>(file_url_value),
217 file_url_len));
218 g_free(file_url_value);
219 base::FilePath file_path;
220 if (net::FileURLToFilePath(file_url, &file_path)) {
221 // Open the file.
222 base::File file(CreateFileForDrop(&file_path));
223 if (file.IsValid()) {
224 // Start downloading the file to the stream.
225 scoped_refptr<DragDownloadFile> drag_file_downloader =
226 new DragDownloadFile(
227 file_path,
228 file.Pass(),
229 download_url_,
230 Referrer(web_contents_->GetURL(),
231 drop_data_->referrer_policy),
232 web_contents_->GetEncoding(),
233 web_contents_);
234 drag_file_downloader->Start(
235 new PromiseFileFinalizer(drag_file_downloader.get()));
236
237 // Set the status code to success.
238 status_code = 'S';
239 }
240 }
241
242 // Return the status code to the file manager.
243 gtk_selection_data_set(selection_data,
244 gtk_selection_data_get_target(selection_data),
245 kBitsPerByte,
246 reinterpret_cast<guchar*>(&status_code),
247 1);
248 }
249 break;
250 }
251
252 case ui::CUSTOM_DATA: {
253 Pickle custom_data;
254 ui::WriteCustomDataToPickle(drop_data_->custom_data, &custom_data);
255 gtk_selection_data_set(
256 selection_data,
257 ui::GetAtomForTarget(ui::CUSTOM_DATA),
258 kBitsPerByte,
259 reinterpret_cast<const guchar*>(custom_data.data()),
260 custom_data.size());
261 break;
262 }
263
264 case ui::RENDERER_TAINT: {
265 static const char kPlaceholder[] = "x";
266 gtk_selection_data_set(
267 selection_data,
268 ui::GetAtomForTarget(ui::RENDERER_TAINT),
269 kBitsPerByte,
270 reinterpret_cast<const guchar*>(kPlaceholder),
271 strlen(kPlaceholder));
272 break;
273 }
274
275 default:
276 NOTREACHED();
277 }
278 }
279
280 gboolean WebDragSourceGtk::OnDragFailed(GtkWidget* sender,
281 GdkDragContext* context,
282 GtkDragResult result) {
283 drag_failed_ = true;
284
285 gfx::Point root = ui::ScreenPoint(GetContentNativeView());
286 gfx::Point client = ui::ClientPoint(GetContentNativeView());
287
288 if (web_contents_) {
289 web_contents_->DragSourceEndedAt(
290 client.x(), client.y(), root.x(), root.y(),
291 WebDragOperationNone);
292 }
293
294 // Let the native failure animation run.
295 return FALSE;
296 }
297
298 void WebDragSourceGtk::OnDragBegin(GtkWidget* sender,
299 GdkDragContext* drag_context) {
300 if (!download_url_.is_empty()) {
301 // Generate the file name based on both mime type and proposed file name.
302 std::string default_name =
303 GetContentClient()->browser()->GetDefaultDownloadName();
304 base::FilePath generated_download_file_name =
305 net::GenerateFileName(download_url_,
306 std::string(),
307 std::string(),
308 download_file_name_.value(),
309 base::UTF16ToUTF8(wide_download_mime_type_),
310 default_name);
311
312 // Pass the file name to the drop target by setting the source window's
313 // XdndDirectSave0 property.
314 gdk_property_change(drag_context->source_window,
315 ui::GetAtomForTarget(ui::DIRECT_SAVE_FILE),
316 ui::GetAtomForTarget(ui::TEXT_PLAIN_NO_CHARSET),
317 8,
318 GDK_PROP_MODE_REPLACE,
319 reinterpret_cast<const guchar*>(
320 generated_download_file_name.value().c_str()),
321 generated_download_file_name.value().length());
322 }
323
324 if (drag_pixbuf_) {
325 gtk_widget_set_size_request(drag_icon_,
326 gdk_pixbuf_get_width(drag_pixbuf_),
327 gdk_pixbuf_get_height(drag_pixbuf_));
328
329 // We only need to do this once.
330 if (!gtk_widget_get_realized(drag_icon_)) {
331 GdkScreen* screen = gtk_widget_get_screen(drag_icon_);
332 GdkColormap* rgba = gdk_screen_get_rgba_colormap(screen);
333 if (rgba)
334 gtk_widget_set_colormap(drag_icon_, rgba);
335 }
336
337 gtk_drag_set_icon_widget(drag_context, drag_icon_,
338 image_offset_.x(), image_offset_.y());
339 }
340 }
341
342 void WebDragSourceGtk::OnDragEnd(GtkWidget* sender,
343 GdkDragContext* drag_context) {
344 if (drag_pixbuf_) {
345 g_object_unref(drag_pixbuf_);
346 drag_pixbuf_ = NULL;
347 }
348
349 if (!download_url_.is_empty()) {
350 gdk_property_delete(drag_context->source_window,
351 ui::GetAtomForTarget(ui::DIRECT_SAVE_FILE));
352 }
353
354 if (!drag_failed_) {
355 gfx::Point root = ui::ScreenPoint(GetContentNativeView());
356 gfx::Point client = ui::ClientPoint(GetContentNativeView());
357
358 if (web_contents_) {
359 web_contents_->DragSourceEndedAt(
360 client.x(), client.y(), root.x(), root.y(),
361 GdkDragActionToWebDragOp(drag_context->action));
362 }
363 }
364
365 web_contents_->SystemDragEnded();
366
367 drop_data_.reset();
368 drag_context_ = NULL;
369 }
370
371 gfx::NativeView WebDragSourceGtk::GetContentNativeView() const {
372 return web_contents_->GetView()->GetContentNativeView();
373 }
374
375 gboolean WebDragSourceGtk::OnDragIconExpose(GtkWidget* sender,
376 GdkEventExpose* event) {
377 cairo_t* cr = gdk_cairo_create(event->window);
378 gdk_cairo_rectangle(cr, &event->area);
379 cairo_clip(cr);
380 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
381 gdk_cairo_set_source_pixbuf(cr, drag_pixbuf_, 0, 0);
382 cairo_paint(cr);
383 cairo_destroy(cr);
384
385 return TRUE;
386 }
387
388 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698