172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/gtk/tab_contents_drag_source.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <string>
8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/file_util.h"
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/mime_util.h"
11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/utf_string_conversions.h"
123345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "chrome/browser/download/download_util.h"
13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/download/drag_download_file.h"
14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/download/drag_download_util.h"
1572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/gtk/gtk_util.h"
16dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/renderer_host/render_view_host.h"
17dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/renderer_host/render_view_host_delegate.h"
18dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/tab_contents/tab_contents.h"
19dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/tab_contents/tab_contents_view.h"
20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "net/base/file_stream.h"
21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "net/base/net_util.h"
2272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/dragdrop/gtk_dnd_util.h"
2372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/gtk_util.h"
24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "webkit/glue/webdropdata.h"
25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing WebKit::WebDragOperation;
27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing WebKit::WebDragOperationsMask;
28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing WebKit::WebDragOperationNone;
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTabContentsDragSource::TabContentsDragSource(
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TabContentsView* tab_contents_view)
32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : tab_contents_view_(tab_contents_view),
33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      drag_pixbuf_(NULL),
34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      drag_failed_(false),
35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      drag_widget_(gtk_invisible_new()),
3672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      drag_context_(NULL),
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      drag_icon_(gtk_window_new(GTK_WINDOW_POPUP)) {
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  signals_.Connect(drag_widget_, "drag-failed",
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   G_CALLBACK(OnDragFailedThunk), this);
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  signals_.Connect(drag_widget_, "drag-begin",
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   G_CALLBACK(OnDragBeginThunk),
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   this);
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  signals_.Connect(drag_widget_, "drag-end",
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   G_CALLBACK(OnDragEndThunk), this);
45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  signals_.Connect(drag_widget_, "drag-data-get",
46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   G_CALLBACK(OnDragDataGetThunk), this);
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  signals_.Connect(drag_icon_, "expose-event",
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   G_CALLBACK(OnDragIconExposeThunk), this);
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTabContentsDragSource::~TabContentsDragSource() {
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Break the current drag, if any.
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (drop_data_.get()) {
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gtk_grab_add(drag_widget_);
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gtk_grab_remove(drag_widget_);
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    MessageLoopForUI::current()->RemoveObserver(this);
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    drop_data_.reset();
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
61513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  gtk_widget_destroy(drag_widget_);
62513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  gtk_widget_destroy(drag_icon_);
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTabContents* TabContentsDragSource::tab_contents() const {
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return tab_contents_view_->tab_contents();
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabContentsDragSource::StartDragging(const WebDropData& drop_data,
70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                          WebDragOperationsMask allowed_ops,
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                          GdkEventButton* last_mouse_down,
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                          const SkBitmap& image,
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                          const gfx::Point& image_offset) {
7472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // Guard against re-starting before previous drag completed.
7572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (drag_context_) {
7672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    NOTREACHED();
7772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    tab_contents()->SystemDragEnded();
7872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return;
7972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
8072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int targets_mask = 0;
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!drop_data.plain_text.empty())
8472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    targets_mask |= ui::TEXT_PLAIN;
85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (drop_data.url.is_valid()) {
8672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    targets_mask |= ui::TEXT_URI_LIST;
8772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    targets_mask |= ui::CHROME_NAMED_URL;
8872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    targets_mask |= ui::NETSCAPE_URL;
89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!drop_data.text_html.empty())
9172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    targets_mask |= ui::TEXT_HTML;
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!drop_data.file_contents.empty())
9372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    targets_mask |= ui::CHROME_WEBDROP_FILE_CONTENTS;
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!drop_data.download_metadata.empty() &&
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      drag_download_util::ParseDownloadMetadata(drop_data.download_metadata,
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                                &wide_download_mime_type_,
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                                &download_file_name_,
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                                &download_url_)) {
9972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    targets_mask |= ui::DIRECT_SAVE_FILE;
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
10272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // NOTE: Begin a drag even if no targets present. Otherwise, things like
10372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // draggable list elements will not work.
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  drop_data_.reset(new WebDropData(drop_data));
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1073345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // The image we get from WebKit makes heavy use of alpha-shading. This looks
1083345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // bad on non-compositing WMs. Fall back to the default drag icon.
1093345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (!image.isNull() && gtk_util::IsScreenComposited())
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    drag_pixbuf_ = gfx::GdkPixbufFromSkBitmap(&image);
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  image_offset_ = image_offset;
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
11372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  GtkTargetList* list = ui::GetTargetListFromCodeMask(targets_mask);
11472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (targets_mask & ui::CHROME_WEBDROP_FILE_CONTENTS) {
115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    drag_file_mime_type_ = gdk_atom_intern(
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        mime_util::GetDataMimeType(drop_data.file_contents).c_str(), FALSE);
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gtk_target_list_add(list, drag_file_mime_type_,
11872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                        0, ui::CHROME_WEBDROP_FILE_CONTENTS);
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  drag_failed_ = false;
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If we don't pass an event, GDK won't know what event time to start grabbing
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // mouse events. Technically it's the mouse motion event and not the mouse
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // down event that causes the drag, but there's no reliable way to know
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // *which* motion event initiated the drag, so this will have to do.
126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // TODO(estade): This can sometimes be very far off, e.g. if the user clicks
127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // and holds and doesn't start dragging for a long time. I doubt it matters
128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // much, but we should probably look into the possibility of getting the
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // initiating event from webkit.
13072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  drag_context_ = gtk_drag_begin(drag_widget_, list,
131731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      gtk_util::WebDragOpToGdkDragAction(allowed_ops),
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      1,  // Drags are always initiated by the left button.
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      reinterpret_cast<GdkEvent*>(last_mouse_down));
134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The drag adds a ref; let it own the list.
135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_target_list_unref(list);
136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Sometimes the drag fails to start; |context| will be NULL and we won't
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // get a drag-end signal.
13972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (!drag_context_) {
14072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    drag_failed_ = true;
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    drop_data_.reset();
14272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    tab_contents()->SystemDragEnded();
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  MessageLoopForUI::current()->AddObserver(this);
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabContentsDragSource::WillProcessEvent(GdkEvent* event) {
150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // No-op.
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabContentsDragSource::DidProcessEvent(GdkEvent* event) {
154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (event->type != GDK_MOTION_NOTIFY)
155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GdkEventMotion* event_motion = reinterpret_cast<GdkEventMotion*>(event);
158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Point client = gtk_util::ClientPoint(GetContentNativeView());
159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (tab_contents()->render_view_host()) {
161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tab_contents()->render_view_host()->DragSourceMovedTo(
162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        client.x(), client.y(),
163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        static_cast<int>(event_motion->x_root),
164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        static_cast<int>(event_motion->y_root));
165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabContentsDragSource::OnDragDataGet(GtkWidget* sender,
169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    GdkDragContext* context, GtkSelectionData* selection_data,
170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    guint target_type, guint time) {
171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const int kBitsPerByte = 8;
172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  switch (target_type) {
17472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    case ui::TEXT_PLAIN: {
175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      std::string utf8_text = UTF16ToUTF8(drop_data_->plain_text);
176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      gtk_selection_data_set_text(selection_data, utf8_text.c_str(),
177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                  utf8_text.length());
178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
18172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    case ui::TEXT_HTML: {
182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // TODO(estade): change relative links to be absolute using
183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // |html_base_url|.
184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      std::string utf8_text = UTF16ToUTF8(drop_data_->text_html);
185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      gtk_selection_data_set(selection_data,
18672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                             ui::GetAtomForTarget(ui::TEXT_HTML),
187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                             kBitsPerByte,
188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                             reinterpret_cast<const guchar*>(utf8_text.c_str()),
189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                             utf8_text.length());
190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
19372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    case ui::TEXT_URI_LIST:
19472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    case ui::CHROME_NAMED_URL:
19572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    case ui::NETSCAPE_URL: {
19672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      ui::WriteURLWithName(selection_data, drop_data_->url,
19772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                           drop_data_->url_title, target_type);
198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
20172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    case ui::CHROME_WEBDROP_FILE_CONTENTS: {
202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      gtk_selection_data_set(
203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          selection_data,
204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          drag_file_mime_type_, kBitsPerByte,
205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          reinterpret_cast<const guchar*>(drop_data_->file_contents.data()),
206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          drop_data_->file_contents.length());
207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
21072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    case ui::DIRECT_SAVE_FILE: {
211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      char status_code = 'E';
212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Retrieves the full file path (in file URL format) provided by the
214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // drop target by reading from the source window's XdndDirectSave0
215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // property.
216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      gint file_url_len = 0;
217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      guchar* file_url_value = NULL;
218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (gdk_property_get(context->source_window,
21972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                           ui::GetAtomForTarget(ui::DIRECT_SAVE_FILE),
22072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                           ui::GetAtomForTarget(ui::TEXT_PLAIN_NO_CHARSET),
221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                           0,
222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                           1024,
223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                           FALSE,
224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                           NULL,
225c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                           NULL,
226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                           &file_url_len,
227c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                           &file_url_value) &&
228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          file_url_value) {
229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        // Convert from the file url to the file path.
230c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        GURL file_url(std::string(reinterpret_cast<char*>(file_url_value),
231c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                  file_url_len));
232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        g_free(file_url_value);
233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        FilePath file_path;
234c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        if (net::FileURLToFilePath(file_url, &file_path)) {
235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          // Open the file as a stream.
236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          net::FileStream* file_stream =
237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch              drag_download_util::CreateFileStreamForDrop(&file_path);
238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          if (file_stream) {
239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch              // Start downloading the file to the stream.
240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch              TabContents* tab_contents = tab_contents_view_->tab_contents();
241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch              scoped_refptr<DragDownloadFile> drag_file_downloader =
242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                  new DragDownloadFile(file_path,
243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                       linked_ptr<net::FileStream>(file_stream),
244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                       download_url_,
245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                       tab_contents->GetURL(),
246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                       tab_contents->encoding(),
247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                       tab_contents);
248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch              drag_file_downloader->Start(
249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                  new drag_download_util::PromiseFileFinalizer(
250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                      drag_file_downloader));
251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch              // Set the status code to success.
253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch              status_code = 'S';
254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          }
255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        }
256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        // Return the status code to the file manager.
258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        gtk_selection_data_set(selection_data,
259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                               selection_data->target,
260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                               8,
261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                               reinterpret_cast<guchar*>(&status_code),
262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                               1);
263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    default:
268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NOTREACHED();
269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgboolean TabContentsDragSource::OnDragFailed(GtkWidget* sender,
273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                             GdkDragContext* context,
274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                             GtkDragResult result) {
275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  drag_failed_ = true;
276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Point root = gtk_util::ScreenPoint(GetContentNativeView());
278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Point client = gtk_util::ClientPoint(GetContentNativeView());
279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (tab_contents()->render_view_host()) {
281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tab_contents()->render_view_host()->DragSourceEndedAt(
282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        client.x(), client.y(), root.x(), root.y(),
283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        WebDragOperationNone);
284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Let the native failure animation run.
287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return FALSE;
288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabContentsDragSource::OnDragBegin(GtkWidget* sender,
291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                        GdkDragContext* drag_context) {
292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!download_url_.is_empty()) {
293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Generate the file name based on both mime type and proposed file name.
294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    std::string download_mime_type = UTF16ToUTF8(wide_download_mime_type_);
295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    std::string content_disposition("attachment; filename=");
296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    content_disposition += download_file_name_.value();
297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    FilePath generated_download_file_name;
2983345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    download_util::GenerateFileName(download_url_,
2993345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                                    content_disposition,
3003345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                                    std::string(),
3013345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                                    download_mime_type,
3023345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                                    &generated_download_file_name);
303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Pass the file name to the drop target by setting the source window's
305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // XdndDirectSave0 property.
306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gdk_property_change(drag_context->source_window,
30772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                        ui::GetAtomForTarget(ui::DIRECT_SAVE_FILE),
30872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                        ui::GetAtomForTarget(ui::TEXT_PLAIN_NO_CHARSET),
309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                        8,
310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                        GDK_PROP_MODE_REPLACE,
311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                        reinterpret_cast<const guchar*>(
312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                            generated_download_file_name.value().c_str()),
313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                        generated_download_file_name.value().length());
314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (drag_pixbuf_) {
317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gtk_widget_set_size_request(drag_icon_,
318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                gdk_pixbuf_get_width(drag_pixbuf_),
319c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                gdk_pixbuf_get_height(drag_pixbuf_));
320c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
3213345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    // We only need to do this once.
3223345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    if (!GTK_WIDGET_REALIZED(drag_icon_)) {
3233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      GdkScreen* screen = gtk_widget_get_screen(drag_icon_);
3243345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      GdkColormap* rgba = gdk_screen_get_rgba_colormap(screen);
3253345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      if (rgba)
3263345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        gtk_widget_set_colormap(drag_icon_, rgba);
3273345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    }
328c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
329c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gtk_drag_set_icon_widget(drag_context, drag_icon_,
330c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                             image_offset_.x(), image_offset_.y());
331c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
332c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
333c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabContentsDragSource::OnDragEnd(GtkWidget* sender,
335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                      GdkDragContext* drag_context) {
336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (drag_pixbuf_) {
337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    g_object_unref(drag_pixbuf_);
338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    drag_pixbuf_ = NULL;
339c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  MessageLoopForUI::current()->RemoveObserver(this);
342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!download_url_.is_empty()) {
344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gdk_property_delete(drag_context->source_window,
34572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                        ui::GetAtomForTarget(ui::DIRECT_SAVE_FILE));
346c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
347c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
348c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!drag_failed_) {
349c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gfx::Point root = gtk_util::ScreenPoint(GetContentNativeView());
350c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gfx::Point client = gtk_util::ClientPoint(GetContentNativeView());
351c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
352c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (tab_contents()->render_view_host()) {
353c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      tab_contents()->render_view_host()->DragSourceEndedAt(
354c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          client.x(), client.y(), root.x(), root.y(),
355731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick          gtk_util::GdkDragActionToWebDragOp(drag_context->action));
356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
35972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  tab_contents()->SystemDragEnded();
360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  drop_data_.reset();
36272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  drag_context_ = NULL;
363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgfx::NativeView TabContentsDragSource::GetContentNativeView() const {
366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return tab_contents_view_->GetContentNativeView();
367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgboolean TabContentsDragSource::OnDragIconExpose(GtkWidget* sender,
370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                                 GdkEventExpose* event) {
371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_t* cr = gdk_cairo_create(event->window);
372c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gdk_cairo_rectangle(cr, &event->area);
373c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_clip(cr);
374c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
375c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gdk_cairo_set_source_pixbuf(cr, drag_pixbuf_, 0, 0);
376c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_paint(cr);
377c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_destroy(cr);
378c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
379c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return TRUE;
380c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
381