15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/browser/web_contents/web_drag_source_gtk.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/nix/mime_util_xdg.h"
10868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/threading/thread_restrictions.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/browser/download/drag_download_file.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/browser/download/drag_download_util.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/browser/renderer_host/render_view_host_delegate.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/browser/renderer_host/render_view_host_impl.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/browser/web_contents/drag_utils_gtk.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/browser/web_contents/web_contents_impl.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/content_browser_client.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/web_contents_view.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/common/content_client.h"
21eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "content/public/common/drop_data.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/file_stream.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/net_util.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkBitmap.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/clipboard/custom_data_helper.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/dragdrop/gtk_dnd_util.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/gtk/gtk_screen_util.h"
2858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "ui/gfx/gtk_compat.h"
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/gtk_util.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
31f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)using blink::WebDragOperation;
32f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)using blink::WebDragOperationsMask;
33f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)using blink::WebDragOperationNone;
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace content {
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)WebDragSourceGtk::WebDragSourceGtk(WebContents* web_contents)
38c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    : web_contents_(static_cast<WebContentsImpl*>(web_contents)),
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      drag_pixbuf_(NULL),
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      drag_failed_(false),
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      drag_widget_(gtk_invisible_new()),
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      drag_context_(NULL),
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      drag_icon_(gtk_window_new(GTK_WINDOW_POPUP)) {
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  signals_.Connect(drag_widget_, "drag-failed",
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   G_CALLBACK(OnDragFailedThunk), this);
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  signals_.Connect(drag_widget_, "drag-begin",
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   G_CALLBACK(OnDragBeginThunk),
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   this);
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  signals_.Connect(drag_widget_, "drag-end",
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   G_CALLBACK(OnDragEndThunk), this);
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  signals_.Connect(drag_widget_, "drag-data-get",
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   G_CALLBACK(OnDragDataGetThunk), this);
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  signals_.Connect(drag_icon_, "expose-event",
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   G_CALLBACK(OnDragIconExposeThunk), this);
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)WebDragSourceGtk::~WebDragSourceGtk() {
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Break the current drag, if any.
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (drop_data_) {
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gtk_grab_add(drag_widget_);
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gtk_grab_remove(drag_widget_);
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    base::MessageLoopForUI::current()->RemoveObserver(this);
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    drop_data_.reset();
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_widget_destroy(drag_widget_);
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_widget_destroy(drag_icon_);
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
71eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochbool WebDragSourceGtk::StartDragging(const DropData& drop_data,
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     WebDragOperationsMask allowed_ops,
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     GdkEventButton* last_mouse_down,
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     const SkBitmap& image,
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     const gfx::Vector2d& image_offset) {
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Guard against re-starting before previous drag completed.
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (drag_context_) {
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED();
79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return false;
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int targets_mask = 0;
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!drop_data.text.string().empty())
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    targets_mask |= ui::TEXT_PLAIN;
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (drop_data.url.is_valid()) {
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    targets_mask |= ui::TEXT_URI_LIST;
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    targets_mask |= ui::CHROME_NAMED_URL;
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    targets_mask |= ui::NETSCAPE_URL;
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!drop_data.html.string().empty())
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    targets_mask |= ui::TEXT_HTML;
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!drop_data.file_contents.empty())
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    targets_mask |= ui::CHROME_WEBDROP_FILE_CONTENTS;
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!drop_data.download_metadata.empty() &&
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ParseDownloadMetadata(drop_data.download_metadata,
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            &wide_download_mime_type_,
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            &download_file_name_,
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            &download_url_)) {
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    targets_mask |= ui::DIRECT_SAVE_FILE;
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!drop_data.custom_data.empty())
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    targets_mask |= ui::CUSTOM_DATA;
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // NOTE: Begin a drag even if no targets present. Otherwise, things like
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // draggable list elements will not work.
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
108eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  drop_data_.reset(new DropData(drop_data));
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The image we get from WebKit makes heavy use of alpha-shading. This looks
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // bad on non-compositing WMs. Fall back to the default drag icon.
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!image.isNull() && ui::IsScreenComposited())
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    drag_pixbuf_ = gfx::GdkPixbufFromSkBitmap(image);
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  image_offset_ = image_offset;
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GtkTargetList* list = ui::GetTargetListFromCodeMask(targets_mask);
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (targets_mask & ui::CHROME_WEBDROP_FILE_CONTENTS) {
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Looking up the mime type can hit the disk.  http://crbug.com/84896
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::ThreadRestrictions::ScopedAllowIO allow_io;
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    drag_file_mime_type_ = gdk_atom_intern(
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::nix::GetDataMimeType(drop_data.file_contents).c_str(), FALSE);
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gtk_target_list_add(list, drag_file_mime_type_,
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        0, ui::CHROME_WEBDROP_FILE_CONTENTS);
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  drag_failed_ = false;
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we don't pass an event, GDK won't know what event time to start grabbing
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // mouse events. Technically it's the mouse motion event and not the mouse
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // down event that causes the drag, but there's no reliable way to know
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // *which* motion event initiated the drag, so this will have to do.
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(estade): This can sometimes be very far off, e.g. if the user clicks
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // and holds and doesn't start dragging for a long time. I doubt it matters
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // much, but we should probably look into the possibility of getting the
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // initiating event from webkit.
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  drag_context_ = gtk_drag_begin(drag_widget_, list,
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      WebDragOpToGdkDragAction(allowed_ops),
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      1,  // Drags are always initiated by the left button.
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      reinterpret_cast<GdkEvent*>(last_mouse_down));
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The drag adds a ref; let it own the list.
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_target_list_unref(list);
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Sometimes the drag fails to start; |context| will be NULL and we won't
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // get a drag-end signal.
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!drag_context_) {
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    drag_failed_ = true;
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    drop_data_.reset();
147c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return false;
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
150c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::MessageLoopForUI::current()->AddObserver(this);
151c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return true;
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WebDragSourceGtk::WillProcessEvent(GdkEvent* event) {
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // No-op.
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WebDragSourceGtk::DidProcessEvent(GdkEvent* event) {
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (event->type != GDK_MOTION_NOTIFY)
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GdkEventMotion* event_motion = reinterpret_cast<GdkEventMotion*>(event);
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Point client = ui::ClientPoint(GetContentNativeView());
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
165c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (web_contents_) {
166c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    web_contents_->DragSourceMovedTo(
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        client.x(), client.y(),
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        static_cast<int>(event_motion->x_root),
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        static_cast<int>(event_motion->y_root));
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WebDragSourceGtk::OnDragDataGet(GtkWidget* sender,
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     GdkDragContext* context,
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     GtkSelectionData* selection_data,
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     guint target_type,
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     guint time) {
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int kBitsPerByte = 8;
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (target_type) {
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::TEXT_PLAIN: {
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::string utf8_text = UTF16ToUTF8(drop_data_->text.string());
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gtk_selection_data_set_text(selection_data, utf8_text.c_str(),
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  utf8_text.length());
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::TEXT_HTML: {
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // TODO(estade): change relative links to be absolute using
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // |html_base_url|.
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::string utf8_text = UTF16ToUTF8(drop_data_->html.string());
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gtk_selection_data_set(selection_data,
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             ui::GetAtomForTarget(ui::TEXT_HTML),
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             kBitsPerByte,
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             reinterpret_cast<const guchar*>(utf8_text.c_str()),
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             utf8_text.length());
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::TEXT_URI_LIST:
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::CHROME_NAMED_URL:
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::NETSCAPE_URL: {
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ui::WriteURLWithName(selection_data, drop_data_->url,
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           drop_data_->url_title, target_type);
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::CHROME_WEBDROP_FILE_CONTENTS: {
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gtk_selection_data_set(
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          selection_data,
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          drag_file_mime_type_, kBitsPerByte,
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          reinterpret_cast<const guchar*>(drop_data_->file_contents.data()),
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          drop_data_->file_contents.length());
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::DIRECT_SAVE_FILE: {
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      char status_code = 'E';
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Retrieves the full file path (in file URL format) provided by the
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // drop target by reading from the source window's XdndDirectSave0
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // property.
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gint file_url_len = 0;
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      guchar* file_url_value = NULL;
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (gdk_property_get(context->source_window,
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           ui::GetAtomForTarget(ui::DIRECT_SAVE_FILE),
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           ui::GetAtomForTarget(ui::TEXT_PLAIN_NO_CHARSET),
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           0,
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           1024,
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           FALSE,
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           NULL,
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           NULL,
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           &file_url_len,
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           &file_url_value) &&
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          file_url_value) {
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Convert from the file url to the file path.
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        GURL file_url(std::string(reinterpret_cast<char*>(file_url_value),
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  file_url_len));
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        g_free(file_url_value);
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        base::FilePath file_path;
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (net::FileURLToFilePath(file_url, &file_path)) {
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // Open the file as a stream.
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          scoped_ptr<net::FileStream> file_stream(
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              CreateFileStreamForDrop(
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  &file_path,
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  GetContentClient()->browser()->GetNetLog()));
247c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          if (file_stream) {
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            // Start downloading the file to the stream.
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            scoped_refptr<DragDownloadFile> drag_file_downloader =
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                new DragDownloadFile(
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    file_path,
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    file_stream.Pass(),
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    download_url_,
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    Referrer(web_contents_->GetURL(),
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      drop_data_->referrer_policy),
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    web_contents_->GetEncoding(),
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    web_contents_);
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            drag_file_downloader->Start(
259868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                new PromiseFileFinalizer(drag_file_downloader.get()));
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            // Set the status code to success.
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            status_code = 'S';
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Return the status code to the file manager.
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        gtk_selection_data_set(selection_data,
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               gtk_selection_data_get_target(selection_data),
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               kBitsPerByte,
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               reinterpret_cast<guchar*>(&status_code),
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               1);
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::CUSTOM_DATA: {
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Pickle custom_data;
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ui::WriteCustomDataToPickle(drop_data_->custom_data, &custom_data);
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gtk_selection_data_set(
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          selection_data,
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          ui::GetAtomForTarget(ui::CUSTOM_DATA),
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          kBitsPerByte,
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          reinterpret_cast<const guchar*>(custom_data.data()),
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          custom_data.size());
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gboolean WebDragSourceGtk::OnDragFailed(GtkWidget* sender,
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                        GdkDragContext* context,
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                        GtkDragResult result) {
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  drag_failed_ = true;
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Point root = ui::ScreenPoint(GetContentNativeView());
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Point client = ui::ClientPoint(GetContentNativeView());
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
301c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (web_contents_) {
302c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    web_contents_->DragSourceEndedAt(
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        client.x(), client.y(), root.x(), root.y(),
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        WebDragOperationNone);
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Let the native failure animation run.
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return FALSE;
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WebDragSourceGtk::OnDragBegin(GtkWidget* sender,
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   GdkDragContext* drag_context) {
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!download_url_.is_empty()) {
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Generate the file name based on both mime type and proposed file name.
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string default_name =
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        GetContentClient()->browser()->GetDefaultDownloadName();
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::FilePath generated_download_file_name =
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        net::GenerateFileName(download_url_,
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              std::string(),
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              std::string(),
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              download_file_name_.value(),
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              UTF16ToUTF8(wide_download_mime_type_),
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              default_name);
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Pass the file name to the drop target by setting the source window's
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // XdndDirectSave0 property.
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gdk_property_change(drag_context->source_window,
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        ui::GetAtomForTarget(ui::DIRECT_SAVE_FILE),
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        ui::GetAtomForTarget(ui::TEXT_PLAIN_NO_CHARSET),
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        8,
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        GDK_PROP_MODE_REPLACE,
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        reinterpret_cast<const guchar*>(
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            generated_download_file_name.value().c_str()),
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        generated_download_file_name.value().length());
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (drag_pixbuf_) {
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gtk_widget_set_size_request(drag_icon_,
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                gdk_pixbuf_get_width(drag_pixbuf_),
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                gdk_pixbuf_get_height(drag_pixbuf_));
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We only need to do this once.
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!gtk_widget_get_realized(drag_icon_)) {
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GdkScreen* screen = gtk_widget_get_screen(drag_icon_);
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GdkColormap* rgba = gdk_screen_get_rgba_colormap(screen);
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (rgba)
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        gtk_widget_set_colormap(drag_icon_, rgba);
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gtk_drag_set_icon_widget(drag_context, drag_icon_,
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             image_offset_.x(), image_offset_.y());
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WebDragSourceGtk::OnDragEnd(GtkWidget* sender,
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 GdkDragContext* drag_context) {
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (drag_pixbuf_) {
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    g_object_unref(drag_pixbuf_);
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    drag_pixbuf_ = NULL;
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
362c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::MessageLoopForUI::current()->RemoveObserver(this);
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!download_url_.is_empty()) {
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gdk_property_delete(drag_context->source_window,
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        ui::GetAtomForTarget(ui::DIRECT_SAVE_FILE));
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!drag_failed_) {
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Point root = ui::ScreenPoint(GetContentNativeView());
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Point client = ui::ClientPoint(GetContentNativeView());
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
373c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (web_contents_) {
374c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      web_contents_->DragSourceEndedAt(
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          client.x(), client.y(), root.x(), root.y(),
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          GdkDragActionToWebDragOp(drag_context->action));
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  web_contents_->SystemDragEnded();
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  drop_data_.reset();
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  drag_context_ = NULL;
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::NativeView WebDragSourceGtk::GetContentNativeView() const {
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return web_contents_->GetView()->GetContentNativeView();
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gboolean WebDragSourceGtk::OnDragIconExpose(GtkWidget* sender,
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                            GdkEventExpose* event) {
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cairo_t* cr = gdk_cairo_create(event->window);
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gdk_cairo_rectangle(cr, &event->area);
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cairo_clip(cr);
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gdk_cairo_set_source_pixbuf(cr, drag_pixbuf_, 0, 0);
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cairo_paint(cr);
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cairo_destroy(cr);
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return TRUE;
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace content
404