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

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

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

Powered by Google App Engine
This is Rietveld 408576698