summaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-12 19:00:48 +0000
committerben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-12 19:00:48 +0000
commit41d2e4af3daa1d1ca8f8cbfa6788604a4c9913b9 (patch)
treefa82bb18c231744e72ffd334217be1e256c9e849 /ui
parent356a50ef463bb52fdd9d5700ca11b4b83d4809bc (diff)
downloadchromium_src-41d2e4af3daa1d1ca8f8cbfa6788604a4c9913b9.zip
chromium_src-41d2e4af3daa1d1ca8f8cbfa6788604a4c9913b9.tar.gz
chromium_src-41d2e4af3daa1d1ca8f8cbfa6788604a4c9913b9.tar.bz2
Move OSExchangeData from src/app to src/ui/base/dragdrop
BUG=none TEST=none TBR=brettw Review URL: http://codereview.chromium.org/6200005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@71205 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui')
-rw-r--r--ui/DEPS6
-rw-r--r--ui/base/dragdrop/os_exchange_data.cc148
-rw-r--r--ui/base/dragdrop/os_exchange_data.h199
-rw-r--r--ui/base/dragdrop/os_exchange_data_provider_gtk.cc248
-rw-r--r--ui/base/dragdrop/os_exchange_data_provider_gtk.h120
-rw-r--r--ui/base/dragdrop/os_exchange_data_provider_win.cc925
-rw-r--r--ui/base/dragdrop/os_exchange_data_provider_win.h180
-rw-r--r--ui/base/dragdrop/os_exchange_data_win_unittest.cc377
8 files changed, 2203 insertions, 0 deletions
diff --git a/ui/DEPS b/ui/DEPS
index f1fae9a..3d8a962 100644
--- a/ui/DEPS
+++ b/ui/DEPS
@@ -1,4 +1,10 @@
include_rules = [
"+gfx",
+ "+net",
"+third_party/mozilla",
+
+ # Temporary until all of src/app is consumed into src/ui
+ "+app",
+ "+grit/app_locale_settings.h",
+ "+grit/app_strings.h",
]
diff --git a/ui/base/dragdrop/os_exchange_data.cc b/ui/base/dragdrop/os_exchange_data.cc
new file mode 100644
index 0000000..e5701c3
--- /dev/null
+++ b/ui/base/dragdrop/os_exchange_data.cc
@@ -0,0 +1,148 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/dragdrop/os_exchange_data.h"
+
+#include "base/pickle.h"
+#include "googleurl/src/gurl.h"
+
+namespace ui {
+
+OSExchangeData::DownloadFileInfo::DownloadFileInfo(
+ const FilePath& filename,
+ DownloadFileProvider* downloader)
+ : filename(filename),
+ downloader(downloader) {
+}
+
+OSExchangeData::DownloadFileInfo::~DownloadFileInfo() {}
+
+OSExchangeData::OSExchangeData() : provider_(CreateProvider()) {
+}
+
+OSExchangeData::OSExchangeData(Provider* provider) : provider_(provider) {
+}
+
+OSExchangeData::~OSExchangeData() {
+}
+
+void OSExchangeData::SetString(const std::wstring& data) {
+ provider_->SetString(data);
+}
+
+void OSExchangeData::SetURL(const GURL& url, const std::wstring& title) {
+ provider_->SetURL(url, title);
+}
+
+void OSExchangeData::SetFilename(const std::wstring& full_path) {
+ provider_->SetFilename(full_path);
+}
+
+void OSExchangeData::SetPickledData(CustomFormat format, const Pickle& data) {
+ provider_->SetPickledData(format, data);
+}
+
+bool OSExchangeData::GetString(std::wstring* data) const {
+ return provider_->GetString(data);
+}
+
+bool OSExchangeData::GetURLAndTitle(GURL* url, std::wstring* title) const {
+ return provider_->GetURLAndTitle(url, title);
+}
+
+bool OSExchangeData::GetFilename(std::wstring* full_path) const {
+ return provider_->GetFilename(full_path);
+}
+
+bool OSExchangeData::GetPickledData(CustomFormat format, Pickle* data) const {
+ return provider_->GetPickledData(format, data);
+}
+
+bool OSExchangeData::HasString() const {
+ return provider_->HasString();
+}
+
+bool OSExchangeData::HasURL() const {
+ return provider_->HasURL();
+}
+
+bool OSExchangeData::HasFile() const {
+ return provider_->HasFile();
+}
+
+bool OSExchangeData::HasCustomFormat(CustomFormat format) const {
+ return provider_->HasCustomFormat(format);
+}
+
+bool OSExchangeData::HasAllFormats(
+ int formats,
+ const std::set<CustomFormat>& custom_formats) const {
+ if ((formats & STRING) != 0 && !HasString())
+ return false;
+ if ((formats & URL) != 0 && !HasURL())
+ return false;
+#if defined(OS_WIN)
+ if ((formats & FILE_CONTENTS) != 0 && !provider_->HasFileContents())
+ return false;
+ if ((formats & HTML) != 0 && !provider_->HasHtml())
+ return false;
+#endif
+ if ((formats & FILE_NAME) != 0 && !provider_->HasFile())
+ return false;
+ for (std::set<CustomFormat>::const_iterator i = custom_formats.begin();
+ i != custom_formats.end(); ++i) {
+ if (!HasCustomFormat(*i))
+ return false;
+ }
+ return true;
+}
+
+bool OSExchangeData::HasAnyFormat(
+ int formats,
+ const std::set<CustomFormat>& custom_formats) const {
+ if ((formats & STRING) != 0 && HasString())
+ return true;
+ if ((formats & URL) != 0 && HasURL())
+ return true;
+#if defined(OS_WIN)
+ if ((formats & FILE_CONTENTS) != 0 && provider_->HasFileContents())
+ return true;
+ if ((formats & HTML) != 0 && provider_->HasHtml())
+ return true;
+#endif
+ if ((formats & FILE_NAME) != 0 && provider_->HasFile())
+ return true;
+ for (std::set<CustomFormat>::const_iterator i = custom_formats.begin();
+ i != custom_formats.end(); ++i) {
+ if (HasCustomFormat(*i))
+ return true;
+ }
+ return false;
+}
+
+#if defined(OS_WIN)
+void OSExchangeData::SetFileContents(const std::wstring& filename,
+ const std::string& file_contents) {
+ provider_->SetFileContents(filename, file_contents);
+}
+
+void OSExchangeData::SetHtml(const std::wstring& html, const GURL& base_url) {
+ provider_->SetHtml(html, base_url);
+}
+
+bool OSExchangeData::GetFileContents(std::wstring* filename,
+ std::string* file_contents) const {
+ return provider_->GetFileContents(filename, file_contents);
+}
+
+bool OSExchangeData::GetHtml(std::wstring* html, GURL* base_url) const {
+ return provider_->GetHtml(html, base_url);
+}
+
+void OSExchangeData::SetDownloadFileInfo(const DownloadFileInfo& download) {
+ return provider_->SetDownloadFileInfo(download);
+}
+#endif
+
+} // namespace ui
diff --git a/ui/base/dragdrop/os_exchange_data.h b/ui/base/dragdrop/os_exchange_data.h
new file mode 100644
index 0000000..c04737d
--- /dev/null
+++ b/ui/base/dragdrop/os_exchange_data.h
@@ -0,0 +1,199 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_DRAGDROP_OS_EXCHANGE_DATA_H_
+#define UI_BASE_DRAGDROP_OS_EXCHANGE_DATA_H_
+#pragma once
+
+#include "build/build_config.h"
+
+#include <set>
+#include <string>
+
+#if defined(OS_WIN)
+#include <objidl.h>
+#elif !defined(OS_MACOSX)
+#include <gtk/gtk.h>
+#endif
+
+#include "app/download_file_interface.h"
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/scoped_ptr.h"
+
+class GURL;
+class Pickle;
+
+namespace ui {
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// OSExchangeData
+// An object that holds interchange data to be sent out to OS services like
+// clipboard, drag and drop, etc. This object exposes an API that clients can
+// use to specify raw data and its high level type. This object takes care of
+// translating that into something the OS can understand.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+// NOTE: Support for html and file contents is required by TabContentViewWin.
+// TabContentsViewGtk uses a different class to handle drag support that does
+// not use OSExchangeData. As such, file contents and html support is only
+// compiled on windows.
+class OSExchangeData {
+ public:
+ // CustomFormats are used for non-standard data types. For example, bookmark
+ // nodes are written using a CustomFormat.
+#if defined(OS_WIN)
+ typedef CLIPFORMAT CustomFormat;
+#elif !defined(OS_MACOSX)
+ typedef GdkAtom CustomFormat;
+#endif
+
+ // Enumeration of the known formats.
+ enum Format {
+ STRING = 1 << 0,
+ URL = 1 << 1,
+ FILE_NAME = 1 << 2,
+ PICKLED_DATA = 1 << 3,
+#if defined(OS_WIN)
+ FILE_CONTENTS = 1 << 4,
+ HTML = 1 << 5,
+#endif
+ };
+
+ // Encapsulates the info about a file to be downloaded.
+ struct DownloadFileInfo {
+ DownloadFileInfo(const FilePath& filename,
+ DownloadFileProvider* downloader);
+ ~DownloadFileInfo();
+
+ FilePath filename;
+ scoped_refptr<DownloadFileProvider> downloader;
+ };
+
+ // Provider defines the platform specific part of OSExchangeData that
+ // interacts with the native system.
+ class Provider {
+ public:
+ Provider() {}
+ virtual ~Provider() {}
+
+ virtual void SetString(const std::wstring& data) = 0;
+ virtual void SetURL(const GURL& url, const std::wstring& title) = 0;
+ virtual void SetFilename(const std::wstring& full_path) = 0;
+ virtual void SetPickledData(CustomFormat format, const Pickle& data) = 0;
+
+ virtual bool GetString(std::wstring* data) const = 0;
+ virtual bool GetURLAndTitle(GURL* url, std::wstring* title) const = 0;
+ virtual bool GetFilename(std::wstring* full_path) const = 0;
+ virtual bool GetPickledData(CustomFormat format, Pickle* data) const = 0;
+
+ virtual bool HasString() const = 0;
+ virtual bool HasURL() const = 0;
+ virtual bool HasFile() const = 0;
+ virtual bool HasCustomFormat(
+ OSExchangeData::CustomFormat format) const = 0;
+
+#if defined(OS_WIN)
+ virtual void SetFileContents(const std::wstring& filename,
+ const std::string& file_contents) = 0;
+ virtual void SetHtml(const std::wstring& html, const GURL& base_url) = 0;
+ virtual bool GetFileContents(std::wstring* filename,
+ std::string* file_contents) const = 0;
+ virtual bool GetHtml(std::wstring* html, GURL* base_url) const = 0;
+ virtual bool HasFileContents() const = 0;
+ virtual bool HasHtml() const = 0;
+ virtual void SetDownloadFileInfo(const DownloadFileInfo& download) = 0;
+#endif
+ };
+
+ OSExchangeData();
+ // Creates an OSExchangeData with the specified provider. OSExchangeData
+ // takes ownership of the supplied provider.
+ explicit OSExchangeData(Provider* provider);
+
+ ~OSExchangeData();
+
+ // Registers the specific string as a possible format for data.
+ static CustomFormat RegisterCustomFormat(const std::string& type);
+
+ // Returns the Provider, which actually stores and manages the data.
+ const Provider& provider() const { return *provider_; }
+ Provider& provider() { return *provider_; }
+
+ // These functions add data to the OSExchangeData object of various Chrome
+ // types. The OSExchangeData object takes care of translating the data into
+ // a format suitable for exchange with the OS.
+ // NOTE WELL: Typically, a data object like this will contain only one of the
+ // following types of data. In cases where more data is held, the
+ // order in which these functions are called is _important_!
+ // ---> The order types are added to an OSExchangeData object controls
+ // the order of enumeration in our IEnumFORMATETC implementation!
+ // This comes into play when selecting the best (most preferable)
+ // data type for insertion into a DropTarget.
+ void SetString(const std::wstring& data);
+ // A URL can have an optional title in some exchange formats.
+ void SetURL(const GURL& url, const std::wstring& title);
+ // A full path to a file.
+ // TODO: convert to Filepath.
+ void SetFilename(const std::wstring& full_path);
+ // Adds pickled data of the specified format.
+ void SetPickledData(CustomFormat format, const Pickle& data);
+
+ // These functions retrieve data of the specified type. If data exists, the
+ // functions return and the result is in the out parameter. If the data does
+ // not exist, the out parameter is not touched. The out parameter cannot be
+ // NULL.
+ bool GetString(std::wstring* data) const;
+ bool GetURLAndTitle(GURL* url, std::wstring* title) const;
+ // Return the path of a file, if available.
+ bool GetFilename(std::wstring* full_path) const;
+ bool GetPickledData(CustomFormat format, Pickle* data) const;
+
+ // Test whether or not data of certain types is present, without actually
+ // returning anything.
+ bool HasString() const;
+ bool HasURL() const;
+ bool HasFile() const;
+ bool HasCustomFormat(CustomFormat format) const;
+
+ // Returns true if this OSExchangeData has data for ALL the formats in
+ // |formats| and ALL the custom formats in |custom_formats|.
+ bool HasAllFormats(int formats,
+ const std::set<CustomFormat>& custom_formats) const;
+
+ // Returns true if this OSExchangeData has data in any of the formats in
+ // |formats| or any custom format in |custom_formats|.
+ bool HasAnyFormat(int formats,
+ const std::set<CustomFormat>& custom_formats) const;
+
+#if defined(OS_WIN)
+ // Adds the bytes of a file (CFSTR_FILECONTENTS and CFSTR_FILEDESCRIPTOR).
+ void SetFileContents(const std::wstring& filename,
+ const std::string& file_contents);
+ // Adds a snippet of HTML. |html| is just raw html but this sets both
+ // text/html and CF_HTML.
+ void SetHtml(const std::wstring& html, const GURL& base_url);
+ bool GetFileContents(std::wstring* filename,
+ std::string* file_contents) const;
+ bool GetHtml(std::wstring* html, GURL* base_url) const;
+
+ // Adds a download file with full path (CF_HDROP).
+ void SetDownloadFileInfo(const DownloadFileInfo& download);
+#endif
+
+ private:
+ // Creates the platform specific Provider.
+ static Provider* CreateProvider();
+
+ // Provides the actual data.
+ scoped_ptr<Provider> provider_;
+
+ DISALLOW_COPY_AND_ASSIGN(OSExchangeData);
+};
+
+} // namespace ui
+
+#endif // UI_BASE_DRAGDROP_OS_EXCHANGE_DATA_H_
diff --git a/ui/base/dragdrop/os_exchange_data_provider_gtk.cc b/ui/base/dragdrop/os_exchange_data_provider_gtk.cc
new file mode 100644
index 0000000..83bd600
--- /dev/null
+++ b/ui/base/dragdrop/os_exchange_data_provider_gtk.cc
@@ -0,0 +1,248 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/dragdrop/os_exchange_data_provider_gtk.h"
+
+#include <algorithm>
+
+#include "app/gtk_dnd_util.h"
+#include "base/file_path.h"
+#include "base/utf_string_conversions.h"
+#include "net/base/net_util.h"
+
+namespace ui {
+
+OSExchangeDataProviderGtk::OSExchangeDataProviderGtk(
+ int known_formats,
+ const std::set<GdkAtom>& known_custom_formats)
+ : known_formats_(known_formats),
+ known_custom_formats_(known_custom_formats),
+ formats_(0),
+ drag_image_(NULL) {
+}
+
+OSExchangeDataProviderGtk::OSExchangeDataProviderGtk()
+ : known_formats_(0),
+ formats_(0),
+ drag_image_(NULL) {
+}
+
+OSExchangeDataProviderGtk::~OSExchangeDataProviderGtk() {
+ if (drag_image_)
+ g_object_unref(drag_image_);
+}
+
+bool OSExchangeDataProviderGtk::HasDataForAllFormats(
+ int formats,
+ const std::set<GdkAtom>& custom_formats) const {
+ if ((formats_ & formats) != formats)
+ return false;
+ for (std::set<GdkAtom>::iterator i = custom_formats.begin();
+ i != custom_formats.end(); ++i) {
+ if (pickle_data_.find(*i) == pickle_data_.end())
+ return false;
+ }
+ return true;
+}
+
+GtkTargetList* OSExchangeDataProviderGtk::GetTargetList() const {
+ GtkTargetList* targets = gtk_target_list_new(NULL, 0);
+
+ if ((formats_ & OSExchangeData::STRING) != 0)
+ gtk_target_list_add_text_targets(targets, OSExchangeData::STRING);
+
+ if ((formats_ & OSExchangeData::URL) != 0) {
+ gtk_target_list_add_uri_targets(targets, OSExchangeData::URL);
+ gtk_target_list_add(
+ targets,
+ gtk_dnd_util::GetAtomForTarget(gtk_dnd_util::CHROME_NAMED_URL),
+ 0,
+ OSExchangeData::URL);
+ }
+
+ if ((formats_ & OSExchangeData::FILE_NAME) != 0)
+ gtk_target_list_add_uri_targets(targets, OSExchangeData::FILE_NAME);
+
+ for (PickleData::const_iterator i = pickle_data_.begin();
+ i != pickle_data_.end(); ++i) {
+ gtk_target_list_add(targets, i->first, 0, OSExchangeData::PICKLED_DATA);
+ }
+
+ return targets;
+}
+
+void OSExchangeDataProviderGtk::WriteFormatToSelection(
+ int format,
+ GtkSelectionData* selection) const {
+ if ((format & OSExchangeData::STRING) != 0) {
+ gtk_selection_data_set_text(
+ selection,
+ reinterpret_cast<const gchar*>(string_.c_str()),
+ -1);
+ }
+
+ if ((format & OSExchangeData::URL) != 0) {
+ // TODO: this should be pulled out of TabContentsDragSource into a common
+ // place.
+ Pickle pickle;
+ pickle.WriteString(UTF16ToUTF8(title_));
+ pickle.WriteString(url_.spec());
+ gtk_selection_data_set(
+ selection,
+ gtk_dnd_util::GetAtomForTarget(gtk_dnd_util::CHROME_NAMED_URL),
+ 8,
+ reinterpret_cast<const guchar*>(pickle.data()),
+ pickle.size());
+
+ gchar* uri_array[2];
+ uri_array[0] = strdup(url_.spec().c_str());
+ uri_array[1] = NULL;
+ gtk_selection_data_set_uris(selection, uri_array);
+ free(uri_array[0]);
+ }
+
+ if ((format & OSExchangeData::FILE_NAME) != 0) {
+ gchar* uri_array[2];
+ uri_array[0] =
+ strdup(net::FilePathToFileURL(FilePath(filename_)).spec().c_str());
+ uri_array[1] = NULL;
+ gtk_selection_data_set_uris(selection, uri_array);
+ free(uri_array[0]);
+ }
+
+ if ((format & OSExchangeData::PICKLED_DATA) != 0) {
+ for (PickleData::const_iterator i = pickle_data_.begin();
+ i != pickle_data_.end(); ++i) {
+ const Pickle& data = i->second;
+ gtk_selection_data_set(
+ selection,
+ i->first,
+ 8,
+ reinterpret_cast<const guchar*>(data.data()),
+ data.size());
+ }
+ }
+}
+
+void OSExchangeDataProviderGtk::SetString(const std::wstring& data) {
+ string_ = WideToUTF16Hack(data);
+ formats_ |= OSExchangeData::STRING;
+}
+
+void OSExchangeDataProviderGtk::SetURL(const GURL& url,
+ const std::wstring& title) {
+ url_ = url;
+ title_ = WideToUTF16Hack(title);
+ formats_ |= OSExchangeData::URL;
+}
+
+void OSExchangeDataProviderGtk::SetFilename(const std::wstring& full_path) {
+ filename_ = WideToUTF8(full_path);
+ formats_ |= OSExchangeData::FILE_NAME;
+}
+
+void OSExchangeDataProviderGtk::SetPickledData(GdkAtom format,
+ const Pickle& data) {
+ pickle_data_[format] = data;
+ formats_ |= OSExchangeData::PICKLED_DATA;
+}
+
+bool OSExchangeDataProviderGtk::GetString(std::wstring* data) const {
+ if ((formats_ & OSExchangeData::STRING) == 0)
+ return false;
+ *data = UTF16ToWideHack(string_);
+ return true;
+}
+
+bool OSExchangeDataProviderGtk::GetURLAndTitle(GURL* url,
+ std::wstring* title) const {
+ if ((formats_ & OSExchangeData::URL) == 0) {
+ title->clear();
+ return GetPlainTextURL(url);
+ }
+
+ if (!url_.is_valid())
+ return false;
+
+ *url = url_;
+ *title = UTF16ToWideHack(title_);
+ return true;
+}
+
+bool OSExchangeDataProviderGtk::GetFilename(std::wstring* full_path) const {
+ if ((formats_ & OSExchangeData::FILE_NAME) == 0)
+ return false;
+ *full_path = UTF8ToWide(filename_);
+ return true;
+}
+
+bool OSExchangeDataProviderGtk::GetPickledData(GdkAtom format,
+ Pickle* data) const {
+ PickleData::const_iterator i = pickle_data_.find(format);
+ if (i == pickle_data_.end())
+ return false;
+
+ *data = i->second;
+ return true;
+}
+
+bool OSExchangeDataProviderGtk::HasString() const {
+ return (known_formats_ & OSExchangeData::STRING) != 0 ||
+ (formats_ & OSExchangeData::STRING) != 0;
+}
+
+bool OSExchangeDataProviderGtk::HasURL() const {
+ if ((known_formats_ & OSExchangeData::URL) != 0 ||
+ (formats_ & OSExchangeData::URL) != 0) {
+ return true;
+ }
+ // No URL, see if we have plain text that can be parsed as a URL.
+ return GetPlainTextURL(NULL);
+}
+
+bool OSExchangeDataProviderGtk::HasFile() const {
+ return (known_formats_ & OSExchangeData::FILE_NAME) != 0 ||
+ (formats_ & OSExchangeData::FILE_NAME) != 0;
+ }
+
+bool OSExchangeDataProviderGtk::HasCustomFormat(GdkAtom format) const {
+ return known_custom_formats_.find(format) != known_custom_formats_.end() ||
+ pickle_data_.find(format) != pickle_data_.end();
+}
+
+bool OSExchangeDataProviderGtk::GetPlainTextURL(GURL* url) const {
+ if ((formats_ & OSExchangeData::STRING) == 0)
+ return false;
+
+ GURL test_url(string_);
+ if (!test_url.is_valid())
+ return false;
+
+ if (url)
+ *url = test_url;
+ return true;
+}
+
+void OSExchangeDataProviderGtk::SetDragImage(GdkPixbuf* drag_image,
+ const gfx::Point& cursor_offset) {
+ if (drag_image_)
+ g_object_unref(drag_image_);
+ g_object_ref(drag_image);
+ drag_image_ = drag_image;
+ cursor_offset_ = cursor_offset;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// OSExchangeData, public:
+
+// static
+OSExchangeData::Provider* OSExchangeData::CreateProvider() {
+ return new OSExchangeDataProviderGtk();
+}
+
+GdkAtom OSExchangeData::RegisterCustomFormat(const std::string& type) {
+ return gdk_atom_intern(type.c_str(), false);
+}
+
+} // namespace ui
diff --git a/ui/base/dragdrop/os_exchange_data_provider_gtk.h b/ui/base/dragdrop/os_exchange_data_provider_gtk.h
new file mode 100644
index 0000000..2025b98
--- /dev/null
+++ b/ui/base/dragdrop/os_exchange_data_provider_gtk.h
@@ -0,0 +1,120 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_DRAGDROP_OS_EXCHANGE_DATA_PROVIDER_GTK_H_
+#define UI_BASE_DRAGDROP_OS_EXCHANGE_DATA_PROVIDER_GTK_H_
+#pragma once
+
+#include <gtk/gtk.h>
+#include <map>
+#include <set>
+#include <string>
+
+#include "base/pickle.h"
+#include "base/string16.h"
+#include "gfx/point.h"
+#include "googleurl/src/gurl.h"
+#include "ui/base/dragdrop/os_exchange_data.h"
+
+namespace ui {
+
+// OSExchangeData::Provider implementation for Gtk. OSExchangeDataProviderGtk
+// is created with a set of known data types. In addition specific data
+// types can be set on OSExchangeDataProviderGtk by way of the various setters.
+// The various has methods return true if the format was supplied to the
+// constructor, or explicitly set.
+class OSExchangeDataProviderGtk : public OSExchangeData::Provider {
+ public:
+ OSExchangeDataProviderGtk(int known_formats,
+ const std::set<GdkAtom>& known_custom_formats_);
+ OSExchangeDataProviderGtk();
+
+ virtual ~OSExchangeDataProviderGtk();
+
+ int known_formats() const { return known_formats_; }
+ const std::set<GdkAtom>& known_custom_formats() const {
+ return known_custom_formats_;
+ }
+
+ // Returns true if all the formats and custom formats identified by |formats|
+ // and |custom_formats| have been set in this provider.
+ //
+ // NOTE: this is NOT the same as whether a format may be provided (as is
+ // returned by the various HasXXX methods), but rather if the data for the
+ // formats has been set on this provider by way of the various Setter
+ // methods.
+ bool HasDataForAllFormats(int formats,
+ const std::set<GdkAtom>& custom_formats) const;
+
+ // Returns the set of formats available as a GtkTargetList. It is up to the
+ // caller to free (gtk_target_list_unref) the returned list.
+ GtkTargetList* GetTargetList() const;
+
+ // Writes the data to |selection|. |format| is any combination of
+ // OSExchangeData::Formats.
+ void WriteFormatToSelection(int format,
+ GtkSelectionData* selection) const;
+
+ // Provider methods.
+ virtual void SetString(const std::wstring& data);
+ virtual void SetURL(const GURL& url, const std::wstring& title);
+ virtual void SetFilename(const std::wstring& full_path);
+ virtual void SetPickledData(OSExchangeData::CustomFormat format,
+ const Pickle& data);
+ virtual bool GetString(std::wstring* data) const;
+ virtual bool GetURLAndTitle(GURL* url, std::wstring* title) const;
+ virtual bool GetFilename(std::wstring* full_path) const;
+ virtual bool GetPickledData(OSExchangeData::CustomFormat format,
+ Pickle* data) const;
+ virtual bool HasString() const;
+ virtual bool HasURL() const;
+ virtual bool HasFile() const;
+ virtual bool HasCustomFormat(OSExchangeData::CustomFormat format) const;
+
+ // Set the image and cursor offset data for this drag. Will
+ // increment the ref count of pixbuf.
+ void SetDragImage(GdkPixbuf* pixbuf, const gfx::Point& cursor_offset);
+ GdkPixbuf* drag_image() const { return drag_image_; }
+ gfx::Point cursor_offset() const { return cursor_offset_; }
+
+ private:
+ typedef std::map<OSExchangeData::CustomFormat, Pickle> PickleData;
+
+ // Returns true if |formats_| contains a string format and the string can be
+ // parsed as a URL.
+ bool GetPlainTextURL(GURL* url) const;
+
+ // These are the possible formats the OSExchangeData may contain. Don't
+ // confuse this with the actual formats that have been set, which are
+ // |formats_| and |custom_formats_|.
+ const int known_formats_;
+ const std::set<GdkAtom> known_custom_formats_;
+
+ // Actual formats that have been set. See comment above |known_formats_|
+ // for details.
+ int formats_;
+
+ // String contents.
+ string16 string_;
+
+ // URL contents.
+ GURL url_;
+ string16 title_;
+
+ // File name.
+ std::string filename_;
+
+ // PICKLED_DATA contents.
+ PickleData pickle_data_;
+
+ // Drag image and offset data.
+ GdkPixbuf* drag_image_;
+ gfx::Point cursor_offset_;
+
+ DISALLOW_COPY_AND_ASSIGN(OSExchangeDataProviderGtk);
+};
+
+} // namespace ui
+
+#endif // UI_BASE_DRAGDROP_OS_EXCHANGE_DATA_PROVIDER_GTK_H_
diff --git a/ui/base/dragdrop/os_exchange_data_provider_win.cc b/ui/base/dragdrop/os_exchange_data_provider_win.cc
new file mode 100644
index 0000000..f8488a3
--- /dev/null
+++ b/ui/base/dragdrop/os_exchange_data_provider_win.cc
@@ -0,0 +1,925 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/dragdrop/os_exchange_data_provider_win.h"
+
+#include "app/l10n_util.h"
+#include "base/file_path.h"
+#include "base/i18n/file_util_icu.h"
+#include "base/logging.h"
+#include "base/pickle.h"
+#include "base/scoped_handle.h"
+#include "base/stl_util-inl.h"
+#include "base/utf_string_conversions.h"
+#include "base/win/scoped_hglobal.h"
+#include "googleurl/src/gurl.h"
+#include "grit/app_strings.h"
+#include "net/base/net_util.h"
+#include "ui/base/clipboard/clipboard_util_win.h"
+
+namespace ui {
+
+// Creates a new STGMEDIUM object to hold the specified text. The caller
+// owns the resulting object. The "Bytes" version does not NULL terminate, the
+// string version does.
+static STGMEDIUM* GetStorageForBytes(const char* data, size_t bytes);
+static STGMEDIUM* GetStorageForWString(const std::wstring& data);
+static STGMEDIUM* GetStorageForString(const std::string& data);
+// Creates the contents of an Internet Shortcut file for the given URL.
+static void GetInternetShortcutFileContents(const GURL& url, std::string* data);
+// Creates a valid file name given a suggested title and URL.
+static void CreateValidFileNameFromTitle(const GURL& url,
+ const std::wstring& title,
+ std::wstring* validated);
+// Creates a new STGMEDIUM object to hold a file.
+static STGMEDIUM* GetStorageForFileName(const std::wstring& full_path);
+// Creates a File Descriptor for the creation of a file to the given URL and
+// returns a handle to it.
+static STGMEDIUM* GetStorageForFileDescriptor(
+ const std::wstring& valid_file_name);
+
+///////////////////////////////////////////////////////////////////////////////
+// FormatEtcEnumerator
+
+//
+// This object implements an enumeration interface. The existence of an
+// implementation of this interface is exposed to clients through
+// OSExchangeData's EnumFormatEtc method. Our implementation is nobody's
+// business but our own, so it lives in this file.
+//
+// This Windows API is truly a gem. It wants to be an enumerator but assumes
+// some sort of sequential data (why not just use an array?). See comments
+// throughout.
+//
+class FormatEtcEnumerator : public IEnumFORMATETC {
+ public:
+ FormatEtcEnumerator(DataObjectImpl::StoredData::const_iterator begin,
+ DataObjectImpl::StoredData::const_iterator end);
+ ~FormatEtcEnumerator();
+
+ // IEnumFORMATETC implementation:
+ HRESULT __stdcall Next(
+ ULONG count, FORMATETC* elements_array, ULONG* elements_fetched);
+ HRESULT __stdcall Skip(ULONG skip_count);
+ HRESULT __stdcall Reset();
+ HRESULT __stdcall Clone(IEnumFORMATETC** clone);
+
+ // IUnknown implementation:
+ HRESULT __stdcall QueryInterface(const IID& iid, void** object);
+ ULONG __stdcall AddRef();
+ ULONG __stdcall Release();
+
+ private:
+ // This can only be called from |CloneFromOther|, since it initializes the
+ // contents_ from the other enumerator's contents.
+ FormatEtcEnumerator() : ref_count_(0) {
+ }
+
+ // Clone a new FormatEtc from another instance of this enumeration.
+ static FormatEtcEnumerator* CloneFromOther(const FormatEtcEnumerator* other);
+
+ private:
+ // We are _forced_ to use a vector as our internal data model as Windows'
+ // retarded IEnumFORMATETC API assumes a deterministic ordering of elements
+ // through methods like Next and Skip. This exposes the underlying data
+ // structure to the user. Bah.
+ std::vector<FORMATETC*> contents_;
+
+ // The cursor of the active enumeration - an index into |contents_|.
+ size_t cursor_;
+
+ LONG ref_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(FormatEtcEnumerator);
+};
+
+// Safely makes a copy of all of the relevant bits of a FORMATETC object.
+static void CloneFormatEtc(FORMATETC* source, FORMATETC* clone) {
+ *clone = *source;
+ if (source->ptd) {
+ source->ptd =
+ static_cast<DVTARGETDEVICE*>(CoTaskMemAlloc(sizeof(DVTARGETDEVICE)));
+ *(clone->ptd) = *(source->ptd);
+ }
+}
+
+FormatEtcEnumerator::FormatEtcEnumerator(
+ DataObjectImpl::StoredData::const_iterator start,
+ DataObjectImpl::StoredData::const_iterator end)
+ : ref_count_(0), cursor_(0) {
+ // Copy FORMATETC data from our source into ourselves.
+ while (start != end) {
+ FORMATETC* format_etc = new FORMATETC;
+ CloneFormatEtc(&(*start)->format_etc, format_etc);
+ contents_.push_back(format_etc);
+ ++start;
+ }
+}
+
+FormatEtcEnumerator::~FormatEtcEnumerator() {
+ STLDeleteContainerPointers(contents_.begin(), contents_.end());
+}
+
+STDMETHODIMP FormatEtcEnumerator::Next(
+ ULONG count, FORMATETC* elements_array, ULONG* elements_fetched) {
+ // MSDN says |elements_fetched| is allowed to be NULL if count is 1.
+ if (!elements_fetched)
+ DCHECK(count == 1);
+
+ // This method copies count elements into |elements_array|.
+ ULONG index = 0;
+ while (cursor_ < contents_.size() && index < count) {
+ CloneFormatEtc(contents_[cursor_], &elements_array[index]);
+ ++cursor_;
+ ++index;
+ }
+ // The out param is for how many we actually copied.
+ if (elements_fetched)
+ *elements_fetched = index;
+
+ // If the two don't agree, then we fail.
+ return index == count ? S_OK : S_FALSE;
+}
+
+STDMETHODIMP FormatEtcEnumerator::Skip(ULONG skip_count) {
+ cursor_ += skip_count;
+ // MSDN implies it's OK to leave the enumerator trashed.
+ // "Whatever you say, boss"
+ return cursor_ <= contents_.size() ? S_OK : S_FALSE;
+}
+
+STDMETHODIMP FormatEtcEnumerator::Reset() {
+ cursor_ = 0;
+ return S_OK;
+}
+
+STDMETHODIMP FormatEtcEnumerator::Clone(IEnumFORMATETC** clone) {
+ // Clone the current enumerator in its exact state, including cursor.
+ FormatEtcEnumerator* e = CloneFromOther(this);
+ e->AddRef();
+ *clone = e;
+ return S_OK;
+}
+
+STDMETHODIMP FormatEtcEnumerator::QueryInterface(const IID& iid,
+ void** object) {
+ *object = NULL;
+ if (IsEqualIID(iid, IID_IUnknown) || IsEqualIID(iid, IID_IEnumFORMATETC)) {
+ *object = this;
+ } else {
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+}
+
+ULONG FormatEtcEnumerator::AddRef() {
+ return InterlockedIncrement(&ref_count_);
+}
+
+ULONG FormatEtcEnumerator::Release() {
+ if (InterlockedDecrement(&ref_count_) == 0) {
+ ULONG copied_refcnt = ref_count_;
+ delete this;
+ return copied_refcnt;
+ }
+ return ref_count_;
+}
+
+// static
+FormatEtcEnumerator* FormatEtcEnumerator::CloneFromOther(
+ const FormatEtcEnumerator* other) {
+ FormatEtcEnumerator* e = new FormatEtcEnumerator;
+ // Copy FORMATETC data from our source into ourselves.
+ std::vector<FORMATETC*>::const_iterator start = other->contents_.begin();
+ while (start != other->contents_.end()) {
+ FORMATETC* format_etc = new FORMATETC;
+ CloneFormatEtc(*start, format_etc);
+ e->contents_.push_back(format_etc);
+ ++start;
+ }
+ // Carry over
+ e->cursor_ = other->cursor_;
+ return e;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// OSExchangeDataProviderWin, public:
+
+// static
+bool OSExchangeDataProviderWin::HasPlainTextURL(IDataObject* source) {
+ std::wstring plain_text;
+ return (ClipboardUtil::GetPlainText(source, &plain_text) &&
+ !plain_text.empty() && GURL(plain_text).is_valid());
+}
+
+// static
+bool OSExchangeDataProviderWin::GetPlainTextURL(IDataObject* source,
+ GURL* url) {
+ std::wstring plain_text;
+ if (ClipboardUtil::GetPlainText(source, &plain_text) &&
+ !plain_text.empty()) {
+ GURL gurl(plain_text);
+ if (gurl.is_valid()) {
+ *url = gurl;
+ return true;
+ }
+ }
+ return false;
+}
+
+// static
+DataObjectImpl* OSExchangeDataProviderWin::GetDataObjectImpl(
+ const OSExchangeData& data) {
+ return static_cast<const OSExchangeDataProviderWin*>(&data.provider())->
+ data_.get();
+}
+
+// static
+IDataObject* OSExchangeDataProviderWin::GetIDataObject(
+ const OSExchangeData& data) {
+ return static_cast<const OSExchangeDataProviderWin*>(&data.provider())->
+ data_object();
+}
+
+// static
+IAsyncOperation* OSExchangeDataProviderWin::GetIAsyncOperation(
+ const OSExchangeData& data) {
+ return static_cast<const OSExchangeDataProviderWin*>(&data.provider())->
+ async_operation();
+}
+
+OSExchangeDataProviderWin::OSExchangeDataProviderWin(IDataObject* source)
+ : data_(new DataObjectImpl()),
+ source_object_(source) {
+}
+
+OSExchangeDataProviderWin::OSExchangeDataProviderWin()
+ : data_(new DataObjectImpl()),
+ source_object_(data_.get()) {
+}
+
+OSExchangeDataProviderWin::~OSExchangeDataProviderWin() {
+}
+
+void OSExchangeDataProviderWin::SetString(const std::wstring& data) {
+ STGMEDIUM* storage = GetStorageForWString(data);
+ data_->contents_.push_back(
+ new DataObjectImpl::StoredDataInfo(CF_UNICODETEXT, storage));
+
+ // Also add plain text.
+ storage = GetStorageForString(WideToUTF8(data));
+ data_->contents_.push_back(
+ new DataObjectImpl::StoredDataInfo(CF_TEXT, storage));
+}
+
+void OSExchangeDataProviderWin::SetURL(const GURL& url,
+ const std::wstring& title) {
+ // NOTE WELL:
+ // Every time you change the order of the first two CLIPFORMATS that get
+ // added here, you need to update the EnumerationViaCOM test case in
+ // the _unittest.cc file to reflect the new arrangement otherwise that test
+ // will fail! It assumes an insertion order.
+
+ // Add text/x-moz-url for drags from Firefox
+ std::wstring x_moz_url_str = UTF8ToWide(url.spec());
+ x_moz_url_str += '\n';
+ x_moz_url_str += title;
+ STGMEDIUM* storage = GetStorageForWString(x_moz_url_str);
+ data_->contents_.push_back(new DataObjectImpl::StoredDataInfo(
+ ClipboardUtil::GetMozUrlFormat()->cfFormat, storage));
+
+ // Add a .URL shortcut file for dragging to Explorer.
+ std::wstring valid_file_name;
+ CreateValidFileNameFromTitle(url, title, &valid_file_name);
+ std::string shortcut_url_file_contents;
+ GetInternetShortcutFileContents(url, &shortcut_url_file_contents);
+ SetFileContents(valid_file_name, shortcut_url_file_contents);
+
+ // Add a UniformResourceLocator link for apps like IE and Word.
+ storage = GetStorageForWString(UTF8ToWide(url.spec()));
+ data_->contents_.push_back(new DataObjectImpl::StoredDataInfo(
+ ClipboardUtil::GetUrlWFormat()->cfFormat, storage));
+ storage = GetStorageForString(url.spec());
+ data_->contents_.push_back(new DataObjectImpl::StoredDataInfo(
+ ClipboardUtil::GetUrlFormat()->cfFormat, storage));
+
+ // TODO(beng): add CF_HTML.
+ // http://code.google.com/p/chromium/issues/detail?id=6767
+
+ // Also add text representations (these should be last since they're the
+ // least preferable).
+ storage = GetStorageForWString(UTF8ToWide(url.spec()));
+ data_->contents_.push_back(
+ new DataObjectImpl::StoredDataInfo(CF_UNICODETEXT, storage));
+ storage = GetStorageForString(url.spec());
+ data_->contents_.push_back(
+ new DataObjectImpl::StoredDataInfo(CF_TEXT, storage));
+}
+
+void OSExchangeDataProviderWin::SetFilename(const std::wstring& full_path) {
+ STGMEDIUM* storage = GetStorageForFileName(full_path);
+ DataObjectImpl::StoredDataInfo* info =
+ new DataObjectImpl::StoredDataInfo(CF_HDROP, storage);
+ data_->contents_.push_back(info);
+}
+
+void OSExchangeDataProviderWin::SetPickledData(CLIPFORMAT format,
+ const Pickle& data) {
+ STGMEDIUM* storage = GetStorageForString(
+ std::string(static_cast<const char *>(data.data()),
+ static_cast<size_t>(data.size())));
+ data_->contents_.push_back(
+ new DataObjectImpl::StoredDataInfo(format, storage));
+}
+
+void OSExchangeDataProviderWin::SetFileContents(
+ const std::wstring& filename,
+ const std::string& file_contents) {
+ // Add CFSTR_FILEDESCRIPTOR
+ STGMEDIUM* storage = GetStorageForFileDescriptor(filename);
+ data_->contents_.push_back(new DataObjectImpl::StoredDataInfo(
+ ClipboardUtil::GetFileDescriptorFormat()->cfFormat, storage));
+
+ // Add CFSTR_FILECONTENTS
+ storage = GetStorageForBytes(file_contents.data(), file_contents.length());
+ data_->contents_.push_back(new DataObjectImpl::StoredDataInfo(
+ ClipboardUtil::GetFileContentFormatZero(), storage));
+}
+
+void OSExchangeDataProviderWin::SetHtml(const std::wstring& html,
+ const GURL& base_url) {
+ // Add both MS CF_HTML and text/html format. CF_HTML should be in utf-8.
+ std::string utf8_html = WideToUTF8(html);
+ std::string url = base_url.is_valid() ? base_url.spec() : std::string();
+
+ std::string cf_html = ClipboardUtil::HtmlToCFHtml(utf8_html, url);
+ STGMEDIUM* storage = GetStorageForBytes(cf_html.c_str(), cf_html.size());
+ data_->contents_.push_back(new DataObjectImpl::StoredDataInfo(
+ ClipboardUtil::GetHtmlFormat()->cfFormat, storage));
+
+ STGMEDIUM* storage_plain = GetStorageForBytes(utf8_html.c_str(),
+ utf8_html.size());
+ data_->contents_.push_back(new DataObjectImpl::StoredDataInfo(
+ ClipboardUtil::GetTextHtmlFormat()->cfFormat, storage_plain));
+}
+
+bool OSExchangeDataProviderWin::GetString(std::wstring* data) const {
+ return ClipboardUtil::GetPlainText(source_object_, data);
+}
+
+bool OSExchangeDataProviderWin::GetURLAndTitle(GURL* url,
+ std::wstring* title) const {
+ std::wstring url_str;
+ bool success = ClipboardUtil::GetUrl(source_object_, &url_str, title, true);
+ if (success) {
+ GURL test_url(url_str);
+ if (test_url.is_valid()) {
+ *url = test_url;
+ return true;
+ }
+ } else if (GetPlainTextURL(source_object_, url)) {
+ title->clear();
+ return true;
+ }
+ return false;
+}
+
+bool OSExchangeDataProviderWin::GetFilename(std::wstring* full_path) const {
+ std::vector<std::wstring> filenames;
+ bool success = ClipboardUtil::GetFilenames(source_object_, &filenames);
+ if (success)
+ full_path->assign(filenames[0]);
+ return success;
+}
+
+bool OSExchangeDataProviderWin::GetPickledData(CLIPFORMAT format,
+ Pickle* data) const {
+ DCHECK(data);
+ FORMATETC format_etc =
+ { format, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ bool success = false;
+ STGMEDIUM medium;
+ if (SUCCEEDED(source_object_->GetData(&format_etc, &medium))) {
+ if (medium.tymed & TYMED_HGLOBAL) {
+ base::win::ScopedHGlobal<char> c_data(medium.hGlobal);
+ DCHECK(c_data.Size() > 0);
+ // Need to subtract 1 as SetPickledData adds an extra byte to the end.
+ *data = Pickle(c_data.get(), static_cast<int>(c_data.Size() - 1));
+ success = true;
+ }
+ ReleaseStgMedium(&medium);
+ }
+ return success;
+}
+
+bool OSExchangeDataProviderWin::GetFileContents(
+ std::wstring* filename,
+ std::string* file_contents) const {
+ return ClipboardUtil::GetFileContents(source_object_, filename,
+ file_contents);
+}
+
+bool OSExchangeDataProviderWin::GetHtml(std::wstring* html,
+ GURL* base_url) const {
+ std::string url;
+ bool success = ClipboardUtil::GetHtml(source_object_, html, &url);
+ if (success)
+ *base_url = GURL(url);
+ return success;
+}
+
+bool OSExchangeDataProviderWin::HasString() const {
+ return ClipboardUtil::HasPlainText(source_object_);
+}
+
+bool OSExchangeDataProviderWin::HasURL() const {
+ return (ClipboardUtil::HasUrl(source_object_) ||
+ HasPlainTextURL(source_object_));
+}
+
+bool OSExchangeDataProviderWin::HasFile() const {
+ return ClipboardUtil::HasFilenames(source_object_);
+}
+
+bool OSExchangeDataProviderWin::HasFileContents() const {
+ return ClipboardUtil::HasFileContents(source_object_);
+}
+
+bool OSExchangeDataProviderWin::HasHtml() const {
+ return ClipboardUtil::HasHtml(source_object_);
+}
+
+bool OSExchangeDataProviderWin::HasCustomFormat(CLIPFORMAT format) const {
+ FORMATETC format_etc =
+ { format, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ return (source_object_->QueryGetData(&format_etc) == S_OK);
+}
+
+void OSExchangeDataProviderWin::SetDownloadFileInfo(
+ const OSExchangeData::DownloadFileInfo& download) {
+ // If the filename is not provided, set stoarge to NULL to indicate that
+ // the delay rendering will be used.
+ STGMEDIUM* storage = NULL;
+ if (!download.filename.empty())
+ storage = GetStorageForFileName(download.filename.value());
+
+ // Add CF_HDROP.
+ DataObjectImpl::StoredDataInfo* info = new DataObjectImpl::StoredDataInfo(
+ ClipboardUtil::GetCFHDropFormat()->cfFormat, storage);
+ info->downloader = download.downloader;
+ data_->contents_.push_back(info);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// DataObjectImpl, IDataObject implementation:
+
+// The following function, DuplicateMedium, is derived from WCDataObject.cpp
+// in the WebKit source code. This is the license information for the file:
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+static void DuplicateMedium(CLIPFORMAT source_clipformat,
+ STGMEDIUM* source,
+ STGMEDIUM* destination) {
+ switch (source->tymed) {
+ case TYMED_HGLOBAL:
+ destination->hGlobal =
+ static_cast<HGLOBAL>(OleDuplicateData(
+ source->hGlobal, source_clipformat, 0));
+ break;
+ case TYMED_MFPICT:
+ destination->hMetaFilePict =
+ static_cast<HMETAFILEPICT>(OleDuplicateData(
+ source->hMetaFilePict, source_clipformat, 0));
+ break;
+ case TYMED_GDI:
+ destination->hBitmap =
+ static_cast<HBITMAP>(OleDuplicateData(
+ source->hBitmap, source_clipformat, 0));
+ break;
+ case TYMED_ENHMF:
+ destination->hEnhMetaFile =
+ static_cast<HENHMETAFILE>(OleDuplicateData(
+ source->hEnhMetaFile, source_clipformat, 0));
+ break;
+ case TYMED_FILE:
+ destination->lpszFileName =
+ static_cast<LPOLESTR>(OleDuplicateData(
+ source->lpszFileName, source_clipformat, 0));
+ break;
+ case TYMED_ISTREAM:
+ destination->pstm = source->pstm;
+ destination->pstm->AddRef();
+ break;
+ case TYMED_ISTORAGE:
+ destination->pstg = source->pstg;
+ destination->pstg->AddRef();
+ break;
+ }
+
+ destination->tymed = source->tymed;
+ destination->pUnkForRelease = source->pUnkForRelease;
+ if (destination->pUnkForRelease)
+ destination->pUnkForRelease->AddRef();
+}
+
+DataObjectImpl::DataObjectImpl()
+ : is_aborting_(false),
+ in_async_mode_(false),
+ async_operation_started_(false),
+ observer_(NULL) {
+}
+
+DataObjectImpl::~DataObjectImpl() {
+ StopDownloads();
+ STLDeleteContainerPointers(contents_.begin(), contents_.end());
+ if (observer_)
+ observer_->OnDataObjectDisposed();
+}
+
+void DataObjectImpl::StopDownloads() {
+ for (StoredData::iterator iter = contents_.begin();
+ iter != contents_.end(); ++iter) {
+ if ((*iter)->downloader.get()) {
+ (*iter)->downloader->Stop();
+ (*iter)->downloader = 0;
+ }
+ }
+}
+
+void DataObjectImpl::OnDownloadCompleted(const FilePath& file_path) {
+ CLIPFORMAT hdrop_format = ClipboardUtil::GetCFHDropFormat()->cfFormat;
+ DataObjectImpl::StoredData::iterator iter = contents_.begin();
+ for (; iter != contents_.end(); ++iter) {
+ if ((*iter)->format_etc.cfFormat == hdrop_format) {
+ // Release the old storage.
+ if ((*iter)->owns_medium) {
+ ReleaseStgMedium((*iter)->medium);
+ delete (*iter)->medium;
+ }
+
+ // Update the storage.
+ (*iter)->owns_medium = true;
+ (*iter)->medium = GetStorageForFileName(file_path.value());
+
+ break;
+ }
+ }
+ DCHECK(iter != contents_.end());
+}
+
+void DataObjectImpl::OnDownloadAborted() {
+}
+
+HRESULT DataObjectImpl::GetData(FORMATETC* format_etc, STGMEDIUM* medium) {
+ if (is_aborting_)
+ return DV_E_FORMATETC;
+
+ StoredData::iterator iter = contents_.begin();
+ while (iter != contents_.end()) {
+ if ((*iter)->format_etc.cfFormat == format_etc->cfFormat &&
+ (*iter)->format_etc.lindex == format_etc->lindex &&
+ ((*iter)->format_etc.tymed & format_etc->tymed)) {
+ // If medium is NULL, delay-rendering will be used.
+ if ((*iter)->medium) {
+ DuplicateMedium((*iter)->format_etc.cfFormat, (*iter)->medium, medium);
+ } else {
+ // Check if the left button is down.
+ bool is_left_button_down = (GetKeyState(VK_LBUTTON) & 0x8000) != 0;
+
+ bool wait_for_data = false;
+ if ((*iter)->in_delay_rendering) {
+ // Make sure the left button is up. Sometimes the drop target, like
+ // Shell, might be too aggresive in calling GetData when the left
+ // button is not released.
+ if (is_left_button_down)
+ return DV_E_FORMATETC;
+
+ // In async mode, we do not want to start waiting for the data before
+ // the async operation is started. This is because we want to postpone
+ // until Shell kicks off a background thread to do the work so that
+ // we do not block the UI thread.
+ if (!in_async_mode_ || async_operation_started_)
+ wait_for_data = true;
+ } else {
+ // If the left button is up and the target has not requested the data
+ // yet, it probably means that the target does not support delay-
+ // rendering. So instead, we wait for the data.
+ if (is_left_button_down) {
+ (*iter)->in_delay_rendering = true;
+ memset(medium, 0, sizeof(STGMEDIUM));
+ } else {
+ wait_for_data = true;
+ }
+ }
+
+ if (!wait_for_data)
+ return DV_E_FORMATETC;
+
+ // Notify the observer we start waiting for the data. This gives
+ // an observer a chance to end the drag and drop.
+ if (observer_)
+ observer_->OnWaitForData();
+
+ // Now we can start the download.
+ if ((*iter)->downloader.get()) {
+ if (!(*iter)->downloader->Start(this)) {
+ is_aborting_ = true;
+ return DV_E_FORMATETC;
+ }
+ }
+
+ // The stored data should have been updated with the final version.
+ // So we just need to call this function again to retrieve it.
+ return GetData(format_etc, medium);
+ }
+ return S_OK;
+ }
+ ++iter;
+ }
+
+ return DV_E_FORMATETC;
+}
+
+HRESULT DataObjectImpl::GetDataHere(FORMATETC* format_etc,
+ STGMEDIUM* medium) {
+ return DATA_E_FORMATETC;
+}
+
+HRESULT DataObjectImpl::QueryGetData(FORMATETC* format_etc) {
+ StoredData::const_iterator iter = contents_.begin();
+ while (iter != contents_.end()) {
+ if ((*iter)->format_etc.cfFormat == format_etc->cfFormat)
+ return S_OK;
+ ++iter;
+ }
+ return DV_E_FORMATETC;
+}
+
+HRESULT DataObjectImpl::GetCanonicalFormatEtc(
+ FORMATETC* format_etc, FORMATETC* result) {
+ format_etc->ptd = NULL;
+ return E_NOTIMPL;
+}
+
+HRESULT DataObjectImpl::SetData(
+ FORMATETC* format_etc, STGMEDIUM* medium, BOOL should_release) {
+ STGMEDIUM* local_medium = new STGMEDIUM;
+ if (should_release) {
+ *local_medium = *medium;
+ } else {
+ DuplicateMedium(format_etc->cfFormat, medium, local_medium);
+ }
+
+ DataObjectImpl::StoredDataInfo* info =
+ new DataObjectImpl::StoredDataInfo(format_etc->cfFormat, local_medium);
+ info->medium->tymed = format_etc->tymed;
+ info->owns_medium = !!should_release;
+ contents_.push_back(info);
+
+ return S_OK;
+}
+
+HRESULT DataObjectImpl::EnumFormatEtc(
+ DWORD direction, IEnumFORMATETC** enumerator) {
+ if (direction == DATADIR_GET) {
+ FormatEtcEnumerator* e =
+ new FormatEtcEnumerator(contents_.begin(), contents_.end());
+ e->AddRef();
+ *enumerator = e;
+ return S_OK;
+ }
+ return E_NOTIMPL;
+}
+
+HRESULT DataObjectImpl::DAdvise(
+ FORMATETC* format_etc, DWORD advf, IAdviseSink* sink, DWORD* connection) {
+ return OLE_E_ADVISENOTSUPPORTED;
+}
+
+HRESULT DataObjectImpl::DUnadvise(DWORD connection) {
+ return OLE_E_ADVISENOTSUPPORTED;
+}
+
+HRESULT DataObjectImpl::EnumDAdvise(IEnumSTATDATA** enumerator) {
+ return OLE_E_ADVISENOTSUPPORTED;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// DataObjectImpl, IAsyncOperation implementation:
+
+HRESULT DataObjectImpl::EndOperation(
+ HRESULT result, IBindCtx* reserved, DWORD effects) {
+ async_operation_started_ = false;
+ return S_OK;
+}
+
+HRESULT DataObjectImpl::GetAsyncMode(BOOL* is_op_async) {
+ *is_op_async = in_async_mode_ ? TRUE : FALSE;
+ return S_OK;
+}
+
+HRESULT DataObjectImpl::InOperation(BOOL* in_async_op) {
+ *in_async_op = async_operation_started_ ? TRUE : FALSE;
+ return S_OK;
+}
+
+HRESULT DataObjectImpl::SetAsyncMode(BOOL do_op_async) {
+ in_async_mode_ = (do_op_async == TRUE);
+ return S_OK;
+}
+
+HRESULT DataObjectImpl::StartOperation(IBindCtx* reserved) {
+ async_operation_started_ = true;
+ return S_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// DataObjectImpl, IUnknown implementation:
+
+HRESULT DataObjectImpl::QueryInterface(const IID& iid, void** object) {
+ if (!object)
+ return E_POINTER;
+ if (IsEqualIID(iid, IID_IDataObject) || IsEqualIID(iid, IID_IUnknown)) {
+ *object = static_cast<IDataObject*>(this);
+ } else if (in_async_mode_ && IsEqualIID(iid, IID_IAsyncOperation)) {
+ *object = static_cast<IAsyncOperation*>(this);
+ } else {
+ *object = NULL;
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+}
+
+ULONG DataObjectImpl::AddRef() {
+ base::RefCountedThreadSafe<DownloadFileObserver>::AddRef();
+ return 0;
+}
+
+ULONG DataObjectImpl::Release() {
+ base::RefCountedThreadSafe<DownloadFileObserver>::Release();
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// DataObjectImpl, private:
+
+static STGMEDIUM* GetStorageForBytes(const char* data, size_t bytes) {
+ HANDLE handle = GlobalAlloc(GPTR, static_cast<int>(bytes));
+ base::win::ScopedHGlobal<char> scoped(handle);
+ size_t allocated = static_cast<size_t>(GlobalSize(handle));
+ memcpy(scoped.get(), data, allocated);
+
+ STGMEDIUM* storage = new STGMEDIUM;
+ storage->hGlobal = handle;
+ storage->tymed = TYMED_HGLOBAL;
+ storage->pUnkForRelease = NULL;
+ return storage;
+}
+
+template<class T>
+static HGLOBAL CopyStringToGlobalHandle(const T& payload) {
+ int bytes = static_cast<int>(payload.size() + 1) * sizeof(T::value_type);
+ HANDLE handle = GlobalAlloc(GPTR, bytes);
+ void* data = GlobalLock(handle);
+ size_t allocated = static_cast<size_t>(GlobalSize(handle));
+ memcpy(data, payload.c_str(), allocated);
+ static_cast<T::value_type*>(data)[payload.size()] = '\0';
+ GlobalUnlock(handle);
+ return handle;
+}
+
+static STGMEDIUM* GetStorageForWString(const std::wstring& data) {
+ STGMEDIUM* storage = new STGMEDIUM;
+ storage->hGlobal = CopyStringToGlobalHandle<std::wstring>(data);
+ storage->tymed = TYMED_HGLOBAL;
+ storage->pUnkForRelease = NULL;
+ return storage;
+}
+
+static STGMEDIUM* GetStorageForString(const std::string& data) {
+ STGMEDIUM* storage = new STGMEDIUM;
+ storage->hGlobal = CopyStringToGlobalHandle<std::string>(data);
+ storage->tymed = TYMED_HGLOBAL;
+ storage->pUnkForRelease = NULL;
+ return storage;
+}
+
+static void GetInternetShortcutFileContents(const GURL& url,
+ std::string* data) {
+ DCHECK(data);
+ static const std::string kInternetShortcutFileStart =
+ "[InternetShortcut]\r\nURL=";
+ static const std::string kInternetShortcutFileEnd =
+ "\r\n";
+ *data = kInternetShortcutFileStart + url.spec() + kInternetShortcutFileEnd;
+}
+
+static void CreateValidFileNameFromTitle(const GURL& url,
+ const std::wstring& title,
+ std::wstring* validated) {
+ if (title.empty()) {
+ if (url.is_valid()) {
+ *validated = net::GetSuggestedFilename(
+ url, std::string(), std::string(), FilePath()).ToWStringHack();
+ } else {
+ // Nothing else can be done, just use a default.
+ *validated =
+ l10n_util::GetStringUTF16(IDS_APP_UNTITLED_SHORTCUT_FILE_NAME);
+ }
+ } else {
+ *validated = title;
+ file_util::ReplaceIllegalCharactersInPath(validated, '-');
+ }
+ static const wchar_t extension[] = L".url";
+ static const size_t max_length = MAX_PATH - arraysize(extension);
+ if (validated->size() > max_length)
+ validated->erase(max_length);
+ *validated += extension;
+}
+
+static STGMEDIUM* GetStorageForFileName(const std::wstring& full_path) {
+ const size_t kDropSize = sizeof(DROPFILES);
+ const size_t kTotalBytes =
+ kDropSize + (full_path.length() + 2) * sizeof(wchar_t);
+ HANDLE hdata = GlobalAlloc(GMEM_MOVEABLE, kTotalBytes);
+
+ base::win::ScopedHGlobal<DROPFILES> locked_mem(hdata);
+ DROPFILES* drop_files = locked_mem.get();
+ drop_files->pFiles = sizeof(DROPFILES);
+ drop_files->fWide = TRUE;
+ wchar_t* data = reinterpret_cast<wchar_t*>(
+ reinterpret_cast<BYTE*>(drop_files) + kDropSize);
+ const size_t copy_size = (full_path.length() + 1) * sizeof(wchar_t);
+ memcpy(data, full_path.c_str(), copy_size);
+ data[full_path.length() + 1] = L'\0'; // Double NULL
+
+ STGMEDIUM* storage = new STGMEDIUM;
+ storage->tymed = TYMED_HGLOBAL;
+ storage->hGlobal = drop_files;
+ storage->pUnkForRelease = NULL;
+ return storage;
+}
+
+static STGMEDIUM* GetStorageForFileDescriptor(
+ const std::wstring& valid_file_name) {
+ DCHECK(!valid_file_name.empty() && valid_file_name.size() + 1 <= MAX_PATH);
+ HANDLE handle = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR));
+ FILEGROUPDESCRIPTOR* descriptor =
+ reinterpret_cast<FILEGROUPDESCRIPTOR*>(GlobalLock(handle));
+
+ descriptor->cItems = 1;
+ wcscpy_s(descriptor->fgd[0].cFileName,
+ valid_file_name.size() + 1,
+ valid_file_name.c_str());
+ descriptor->fgd[0].dwFlags = FD_LINKUI;
+
+ GlobalUnlock(handle);
+
+ STGMEDIUM* storage = new STGMEDIUM;
+ storage->hGlobal = handle;
+ storage->tymed = TYMED_HGLOBAL;
+ storage->pUnkForRelease = NULL;
+ return storage;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// OSExchangeData, public:
+
+// static
+OSExchangeData::Provider* OSExchangeData::CreateProvider() {
+ return new OSExchangeDataProviderWin();
+}
+
+// static
+OSExchangeData::CustomFormat OSExchangeData::RegisterCustomFormat(
+ const std::string& type) {
+ return RegisterClipboardFormat(ASCIIToWide(type).c_str());
+}
+
+} // namespace ui
diff --git a/ui/base/dragdrop/os_exchange_data_provider_win.h b/ui/base/dragdrop/os_exchange_data_provider_win.h
new file mode 100644
index 0000000..dc888bd
--- /dev/null
+++ b/ui/base/dragdrop/os_exchange_data_provider_win.h
@@ -0,0 +1,180 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_DRAGDROP_OS_EXCHANGE_DATA_PROVIDER_WIN_H_
+#define UI_BASE_DRAGDROP_OS_EXCHANGE_DATA_PROVIDER_WIN_H_
+#pragma once
+
+#include <objidl.h>
+#include <shlobj.h>
+#include <string>
+
+#include "base/scoped_comptr_win.h"
+#include "ui/base/dragdrop/os_exchange_data.h"
+
+namespace ui {
+
+class DataObjectImpl : public DownloadFileObserver,
+ public IDataObject,
+ public IAsyncOperation {
+ public:
+ class Observer {
+ public:
+ virtual void OnWaitForData() = 0;
+ virtual void OnDataObjectDisposed() = 0;
+ protected:
+ virtual ~Observer() { }
+ };
+
+ DataObjectImpl();
+
+ // Accessors.
+ void set_observer(Observer* observer) { observer_ = observer; }
+
+ // DownloadFileObserver implementation:
+ virtual void OnDownloadCompleted(const FilePath& file_path);
+ virtual void OnDownloadAborted();
+
+ // IDataObject implementation:
+ HRESULT __stdcall GetData(FORMATETC* format_etc, STGMEDIUM* medium);
+ HRESULT __stdcall GetDataHere(FORMATETC* format_etc, STGMEDIUM* medium);
+ HRESULT __stdcall QueryGetData(FORMATETC* format_etc);
+ HRESULT __stdcall GetCanonicalFormatEtc(
+ FORMATETC* format_etc, FORMATETC* result);
+ HRESULT __stdcall SetData(
+ FORMATETC* format_etc, STGMEDIUM* medium, BOOL should_release);
+ HRESULT __stdcall EnumFormatEtc(
+ DWORD direction, IEnumFORMATETC** enumerator);
+ HRESULT __stdcall DAdvise(FORMATETC* format_etc, DWORD advf,
+ IAdviseSink* sink, DWORD* connection);
+ HRESULT __stdcall DUnadvise(DWORD connection);
+ HRESULT __stdcall EnumDAdvise(IEnumSTATDATA** enumerator);
+
+ // IAsyncOperation implementation:
+ HRESULT __stdcall EndOperation(
+ HRESULT result, IBindCtx* reserved, DWORD effects);
+ HRESULT __stdcall GetAsyncMode(BOOL* is_op_async);
+ HRESULT __stdcall InOperation(BOOL* in_async_op);
+ HRESULT __stdcall SetAsyncMode(BOOL do_op_async);
+ HRESULT __stdcall StartOperation(IBindCtx* reserved);
+
+ // IUnknown implementation:
+ HRESULT __stdcall QueryInterface(const IID& iid, void** object);
+ ULONG __stdcall AddRef();
+ ULONG __stdcall Release();
+
+ private:
+ // FormatEtcEnumerator only likes us for our StoredDataMap typedef.
+ friend class FormatEtcEnumerator;
+ friend class OSExchangeDataProviderWin;
+
+ virtual ~DataObjectImpl();
+
+ void StopDownloads();
+
+ // Our internal representation of stored data & type info.
+ struct StoredDataInfo {
+ FORMATETC format_etc;
+ STGMEDIUM* medium;
+ bool owns_medium;
+ bool in_delay_rendering;
+ scoped_refptr<DownloadFileProvider> downloader;
+
+ StoredDataInfo(CLIPFORMAT cf, STGMEDIUM* medium)
+ : medium(medium),
+ owns_medium(true),
+ in_delay_rendering(false) {
+ format_etc.cfFormat = cf;
+ format_etc.dwAspect = DVASPECT_CONTENT;
+ format_etc.lindex = -1;
+ format_etc.ptd = NULL;
+ format_etc.tymed = medium ? medium->tymed : TYMED_HGLOBAL;
+ }
+
+ StoredDataInfo(FORMATETC* format_etc, STGMEDIUM* medium)
+ : format_etc(*format_etc),
+ medium(medium),
+ owns_medium(true),
+ in_delay_rendering(false) {
+ }
+
+ ~StoredDataInfo() {
+ if (owns_medium) {
+ ReleaseStgMedium(medium);
+ delete medium;
+ }
+ if (downloader.get())
+ downloader->Stop();
+ }
+ };
+
+ typedef std::vector<StoredDataInfo*> StoredData;
+ StoredData contents_;
+
+ ScopedComPtr<IDataObject> source_object_;
+
+ bool is_aborting_;
+ bool in_async_mode_;
+ bool async_operation_started_;
+ Observer* observer_;
+};
+
+class OSExchangeDataProviderWin : public OSExchangeData::Provider {
+ public:
+ // Returns true if source has plain text that is a valid url.
+ static bool HasPlainTextURL(IDataObject* source);
+
+ // Returns true if source has plain text that is a valid URL and sets url to
+ // that url.
+ static bool GetPlainTextURL(IDataObject* source, GURL* url);
+
+ static DataObjectImpl* GetDataObjectImpl(const OSExchangeData& data);
+ static IDataObject* GetIDataObject(const OSExchangeData& data);
+ static IAsyncOperation* GetIAsyncOperation(const OSExchangeData& data);
+
+ explicit OSExchangeDataProviderWin(IDataObject* source);
+ OSExchangeDataProviderWin();
+
+ virtual ~OSExchangeDataProviderWin();
+
+ IDataObject* data_object() const { return data_.get(); }
+ IAsyncOperation* async_operation() const { return data_.get(); }
+
+ // OSExchangeData::Provider methods.
+ virtual void SetString(const std::wstring& data);
+ virtual void SetURL(const GURL& url, const std::wstring& title);
+ virtual void SetFilename(const std::wstring& full_path);
+ virtual void SetPickledData(OSExchangeData::CustomFormat format,
+ const Pickle& data);
+ virtual void SetFileContents(const std::wstring& filename,
+ const std::string& file_contents);
+ virtual void SetHtml(const std::wstring& html, const GURL& base_url);
+
+ virtual bool GetString(std::wstring* data) const;
+ virtual bool GetURLAndTitle(GURL* url, std::wstring* title) const;
+ virtual bool GetFilename(std::wstring* full_path) const;
+ virtual bool GetPickledData(OSExchangeData::CustomFormat format,
+ Pickle* data) const;
+ virtual bool GetFileContents(std::wstring* filename,
+ std::string* file_contents) const;
+ virtual bool GetHtml(std::wstring* html, GURL* base_url) const;
+ virtual bool HasString() const;
+ virtual bool HasURL() const;
+ virtual bool HasFile() const;
+ virtual bool HasFileContents() const;
+ virtual bool HasHtml() const;
+ virtual bool HasCustomFormat(OSExchangeData::CustomFormat format) const;
+ virtual void SetDownloadFileInfo(
+ const OSExchangeData::DownloadFileInfo& download_info);
+
+ private:
+ scoped_refptr<DataObjectImpl> data_;
+ ScopedComPtr<IDataObject> source_object_;
+
+ DISALLOW_COPY_AND_ASSIGN(OSExchangeDataProviderWin);
+};
+
+} // namespace ui
+
+#endif // UI_BASE_DRAGDROP_OS_EXCHANGE_DATA_PROVIDER_WIN_H_
diff --git a/ui/base/dragdrop/os_exchange_data_win_unittest.cc b/ui/base/dragdrop/os_exchange_data_win_unittest.cc
new file mode 100644
index 0000000..d9509b9
--- /dev/null
+++ b/ui/base/dragdrop/os_exchange_data_win_unittest.cc
@@ -0,0 +1,377 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/dragdrop/os_exchange_data.h"
+#include "ui/base/dragdrop/os_exchange_data_provider_win.h"
+#include "base/pickle.h"
+#include "base/ref_counted.h"
+#include "base/scoped_handle.h"
+#include "base/scoped_ptr.h"
+#include "base/utf_string_conversions.h"
+#include "base/win/scoped_hglobal.h"
+#include "googleurl/src/gurl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/clipboard/clipboard_util_win.h"
+
+namespace ui {
+
+typedef testing::Test OSExchangeDataTest;
+
+namespace {
+
+OSExchangeData::Provider* CloneProvider(const OSExchangeData& data) {
+ return new OSExchangeDataProviderWin(
+ OSExchangeDataProviderWin::GetIDataObject(data));
+}
+
+} // namespace
+
+// Test setting/getting using the OSExchangeData API
+TEST(OSExchangeDataTest, StringDataGetAndSet) {
+ OSExchangeData data;
+ std::wstring input = L"I can has cheezburger?";
+ data.SetString(input);
+
+ OSExchangeData data2(CloneProvider(data));
+ std::wstring output;
+ EXPECT_TRUE(data2.GetString(&output));
+ EXPECT_EQ(input, output);
+ std::string url_spec = "http://www.goats.com/";
+ GURL url(url_spec);
+ std::wstring title;
+ EXPECT_FALSE(data2.GetURLAndTitle(&url, &title));
+ // No URLs in |data|, so url should be untouched.
+ EXPECT_EQ(url_spec, url.spec());
+}
+
+// Test getting using the IDataObject COM API
+TEST(OSExchangeDataTest, StringDataAccessViaCOM) {
+ OSExchangeData data;
+ std::wstring input = L"O hai googlz.";
+ data.SetString(input);
+ ScopedComPtr<IDataObject> com_data(
+ OSExchangeDataProviderWin::GetIDataObject(data));
+
+ FORMATETC format_etc =
+ { CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ EXPECT_EQ(S_OK, com_data->QueryGetData(&format_etc));
+
+ STGMEDIUM medium;
+ EXPECT_EQ(S_OK, com_data->GetData(&format_etc, &medium));
+ std::wstring output =
+ base::win::ScopedHGlobal<wchar_t>(medium.hGlobal).get();
+ EXPECT_EQ(input, output);
+ ReleaseStgMedium(&medium);
+}
+
+// Test setting using the IDataObject COM API
+TEST(OSExchangeDataTest, StringDataWritingViaCOM) {
+ OSExchangeData data;
+ std::wstring input = L"http://www.google.com/";
+
+ ScopedComPtr<IDataObject> com_data(
+ OSExchangeDataProviderWin::GetIDataObject(data));
+
+ // Store data in the object using the COM SetData API.
+ CLIPFORMAT cfstr_ineturl = RegisterClipboardFormat(CFSTR_INETURL);
+ FORMATETC format_etc =
+ { cfstr_ineturl, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ STGMEDIUM medium;
+ medium.tymed = TYMED_HGLOBAL;
+ HGLOBAL glob = GlobalAlloc(GPTR, sizeof(wchar_t) * (input.size() + 1));
+ size_t stringsz = input.size();
+ SIZE_T sz = GlobalSize(glob);
+ base::win::ScopedHGlobal<wchar_t> global_lock(glob);
+ wchar_t* buffer_handle = global_lock.get();
+ wcscpy_s(buffer_handle, input.size() + 1, input.c_str());
+ medium.hGlobal = glob;
+ medium.pUnkForRelease = NULL;
+ EXPECT_EQ(S_OK, com_data->SetData(&format_etc, &medium, TRUE));
+
+ // Construct a new object with the old object so that we can use our access
+ // APIs.
+ OSExchangeData data2(CloneProvider(data));
+ EXPECT_TRUE(data2.HasURL());
+ GURL url_from_data;
+ std::wstring title;
+ EXPECT_TRUE(data2.GetURLAndTitle(&url_from_data, &title));
+ GURL reference_url(input);
+ EXPECT_EQ(reference_url.spec(), url_from_data.spec());
+}
+
+TEST(OSExchangeDataTest, URLDataAccessViaCOM) {
+ OSExchangeData data;
+ GURL url("http://www.google.com/");
+ data.SetURL(url, L"");
+ ScopedComPtr<IDataObject> com_data(
+ OSExchangeDataProviderWin::GetIDataObject(data));
+
+ CLIPFORMAT cfstr_ineturl = RegisterClipboardFormat(CFSTR_INETURL);
+ FORMATETC format_etc =
+ { cfstr_ineturl, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ EXPECT_EQ(S_OK, com_data->QueryGetData(&format_etc));
+
+ STGMEDIUM medium;
+ EXPECT_EQ(S_OK, com_data->GetData(&format_etc, &medium));
+ std::wstring output =
+ base::win::ScopedHGlobal<wchar_t>(medium.hGlobal).get();
+ EXPECT_EQ(url.spec(), WideToUTF8(output));
+ ReleaseStgMedium(&medium);
+}
+
+TEST(OSExchangeDataTest, MultipleFormatsViaCOM) {
+ OSExchangeData data;
+ std::string url_spec = "http://www.google.com/";
+ GURL url(url_spec);
+ std::wstring text = L"O hai googlz.";
+ data.SetURL(url, L"Google");
+ data.SetString(text);
+
+ ScopedComPtr<IDataObject> com_data(
+ OSExchangeDataProviderWin::GetIDataObject(data));
+
+ CLIPFORMAT cfstr_ineturl = RegisterClipboardFormat(CFSTR_INETURL);
+ FORMATETC url_format_etc =
+ { cfstr_ineturl, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ EXPECT_EQ(S_OK, com_data->QueryGetData(&url_format_etc));
+ FORMATETC text_format_etc =
+ { CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ EXPECT_EQ(S_OK, com_data->QueryGetData(&text_format_etc));
+
+ STGMEDIUM medium;
+ EXPECT_EQ(S_OK, com_data->GetData(&url_format_etc, &medium));
+ std::wstring output_url =
+ base::win::ScopedHGlobal<wchar_t>(medium.hGlobal).get();
+ EXPECT_EQ(url.spec(), WideToUTF8(output_url));
+ ReleaseStgMedium(&medium);
+
+ // The text is supposed to be the raw text of the URL, _NOT_ the value of
+ // |text|! This is because the URL is added first and thus takes precedence!
+ EXPECT_EQ(S_OK, com_data->GetData(&text_format_etc, &medium));
+ std::wstring output_text =
+ base::win::ScopedHGlobal<wchar_t>(medium.hGlobal).get();
+ EXPECT_EQ(url_spec, WideToUTF8(output_text));
+ ReleaseStgMedium(&medium);
+}
+
+TEST(OSExchangeDataTest, EnumerationViaCOM) {
+ OSExchangeData data;
+ data.SetURL(GURL("http://www.google.com/"), L"");
+ data.SetString(L"O hai googlz.");
+
+ CLIPFORMAT cfstr_file_group_descriptor =
+ RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR);
+ CLIPFORMAT text_x_moz_url = RegisterClipboardFormat(L"text/x-moz-url");
+
+ ScopedComPtr<IDataObject> com_data(
+ OSExchangeDataProviderWin::GetIDataObject(data));
+ ScopedComPtr<IEnumFORMATETC> enumerator;
+ EXPECT_EQ(S_OK, com_data.get()->EnumFormatEtc(DATADIR_GET,
+ enumerator.Receive()));
+
+ // Test that we can get one item.
+ {
+ // Explictly don't reset the first time, to verify the creation state is
+ // OK.
+ ULONG retrieved = 0;
+ FORMATETC elements_array[1];
+ EXPECT_EQ(S_OK, enumerator->Next(1,
+ reinterpret_cast<FORMATETC*>(&elements_array), &retrieved));
+ EXPECT_EQ(1, retrieved);
+ EXPECT_EQ(text_x_moz_url, elements_array[0].cfFormat);
+ }
+
+ // Test that we can get one item with a NULL retrieved value.
+ {
+ EXPECT_EQ(S_OK, enumerator->Reset());
+ FORMATETC elements_array[1];
+ EXPECT_EQ(S_OK, enumerator->Next(1,
+ reinterpret_cast<FORMATETC*>(&elements_array), NULL));
+ EXPECT_EQ(text_x_moz_url, elements_array[0].cfFormat);
+ }
+
+ // Test that we can get two items.
+ {
+ EXPECT_EQ(S_OK, enumerator->Reset());
+ ULONG retrieved = 0;
+ FORMATETC elements_array[2];
+ EXPECT_EQ(S_OK, enumerator->Next(2,
+ reinterpret_cast<FORMATETC*>(&elements_array), &retrieved));
+ EXPECT_EQ(2, retrieved);
+ EXPECT_EQ(text_x_moz_url, elements_array[0].cfFormat);
+ EXPECT_EQ(cfstr_file_group_descriptor, elements_array[1].cfFormat);
+ }
+
+ // Test that we can skip the first item.
+ {
+ EXPECT_EQ(S_OK, enumerator->Reset());
+ EXPECT_EQ(S_OK, enumerator->Skip(1));
+ ULONG retrieved = 0;
+ FORMATETC elements_array[1];
+ EXPECT_EQ(S_OK, enumerator->Next(1,
+ reinterpret_cast<FORMATETC*>(&elements_array), &retrieved));
+ EXPECT_EQ(1, retrieved);
+ EXPECT_EQ(cfstr_file_group_descriptor, elements_array[0].cfFormat);
+ }
+
+ // Test that we can skip the first item, and create a clone that matches in
+ // this state, and modify the original without affecting the clone.
+ {
+ EXPECT_EQ(S_OK, enumerator->Reset());
+ EXPECT_EQ(S_OK, enumerator->Skip(1));
+ ScopedComPtr<IEnumFORMATETC> cloned_enumerator;
+ EXPECT_EQ(S_OK, enumerator.get()->Clone(cloned_enumerator.Receive()));
+ EXPECT_EQ(S_OK, enumerator.get()->Reset());
+
+ {
+ ULONG retrieved = 0;
+ FORMATETC elements_array[1];
+ EXPECT_EQ(S_OK, cloned_enumerator->Next(1,
+ reinterpret_cast<FORMATETC*>(&elements_array), &retrieved));
+ EXPECT_EQ(1, retrieved);
+ EXPECT_EQ(cfstr_file_group_descriptor, elements_array[0].cfFormat);
+ }
+
+ {
+ ULONG retrieved = 0;
+ FORMATETC elements_array[1];
+ EXPECT_EQ(S_OK, enumerator->Next(1,
+ reinterpret_cast<FORMATETC*>(&elements_array), &retrieved));
+ EXPECT_EQ(1, retrieved);
+ EXPECT_EQ(text_x_moz_url, elements_array[0].cfFormat);
+ }
+ }
+}
+
+TEST(OSExchangeDataTest, TestURLExchangeFormats) {
+ OSExchangeData data;
+ std::string url_spec = "http://www.google.com/";
+ GURL url(url_spec);
+ std::wstring url_title = L"Google";
+ data.SetURL(url, url_title);
+ std::wstring output;
+
+ OSExchangeData data2(CloneProvider(data));
+
+ // URL spec and title should match
+ GURL output_url;
+ std::wstring output_title;
+ EXPECT_TRUE(data2.GetURLAndTitle(&output_url, &output_title));
+ EXPECT_EQ(url_spec, output_url.spec());
+ EXPECT_EQ(url_title, output_title);
+ std::wstring output_string;
+
+ // URL should be the raw text response
+ EXPECT_TRUE(data2.GetString(&output_string));
+ EXPECT_EQ(url_spec, WideToUTF8(output_string));
+
+ // File contents access via COM
+ ScopedComPtr<IDataObject> com_data(
+ OSExchangeDataProviderWin::GetIDataObject(data));
+ {
+ CLIPFORMAT cfstr_file_contents =
+ RegisterClipboardFormat(CFSTR_FILECONTENTS);
+ FORMATETC format_etc =
+ { cfstr_file_contents, NULL, DVASPECT_CONTENT, 0, TYMED_HGLOBAL };
+ EXPECT_EQ(S_OK, com_data->QueryGetData(&format_etc));
+
+ STGMEDIUM medium;
+ EXPECT_EQ(S_OK, com_data->GetData(&format_etc, &medium));
+ base::win::ScopedHGlobal<char> glob(medium.hGlobal);
+ std::string output(glob.get(), glob.Size());
+ std::string file_contents = "[InternetShortcut]\r\nURL=";
+ file_contents += url_spec;
+ file_contents += "\r\n";
+ EXPECT_EQ(file_contents, output);
+ ReleaseStgMedium(&medium);
+ }
+}
+
+TEST(OSExchangeDataTest, TestPickledData) {
+ CLIPFORMAT test_cf = RegisterClipboardFormat(L"chrome/test");
+
+ Pickle saved_pickle;
+ saved_pickle.WriteInt(1);
+ saved_pickle.WriteInt(2);
+ OSExchangeData data;
+ data.SetPickledData(test_cf, saved_pickle);
+
+ OSExchangeData copy(CloneProvider(data));
+ EXPECT_TRUE(copy.HasCustomFormat(test_cf));
+
+ Pickle restored_pickle;
+ EXPECT_TRUE(copy.GetPickledData(test_cf, &restored_pickle));
+ void* p_iterator = NULL;
+ int value;
+ EXPECT_TRUE(restored_pickle.ReadInt(&p_iterator, &value));
+ EXPECT_EQ(1, value);
+ EXPECT_TRUE(restored_pickle.ReadInt(&p_iterator, &value));
+ EXPECT_EQ(2, value);
+}
+
+TEST(OSExchangeDataTest, FileContents) {
+ OSExchangeData data;
+ std::string file_contents("data\0with\0nulls", 15);
+ data.SetFileContents(L"filename.txt", file_contents);
+
+ OSExchangeData copy(CloneProvider(data));
+ std::wstring filename;
+ std::string read_contents;
+ EXPECT_TRUE(copy.GetFileContents(&filename, &read_contents));
+ EXPECT_EQ(L"filename.txt", filename);
+ EXPECT_EQ(file_contents, read_contents);
+}
+
+TEST(OSExchangeDataTest, Html) {
+ OSExchangeData data;
+ GURL url("http://www.google.com/");
+ std::wstring html(
+ L"<HTML>\n<BODY>\n"
+ L"<b>bold.</b> <i><b>This is bold italic.</b></i>\n"
+ L"</BODY>\n</HTML>");
+ data.SetHtml(html, url);
+
+ OSExchangeData copy(CloneProvider(data));
+ std::wstring read_html;
+ EXPECT_TRUE(copy.GetHtml(&read_html, &url));
+ EXPECT_EQ(html, read_html);
+
+ // Check the CF_HTML too.
+ std::string expected_cf_html(
+ "Version:0.9\r\nStartHTML:0000000139\r\nEndHTML:0000000292\r\n"
+ "StartFragment:0000000177\r\nEndFragment:0000000254\r\n"
+ "SourceURL:http://www.google.com/\r\n<html>\r\n<body>\r\n"
+ "<!--StartFragment-->\r\n");
+ expected_cf_html += WideToUTF8(html);
+ expected_cf_html.append("\r\n<!--EndFragment-->\r\n</body>\r\n</html>");
+
+ STGMEDIUM medium;
+ IDataObject* data_object = OSExchangeDataProviderWin::GetIDataObject(data);
+ EXPECT_EQ(S_OK,
+ data_object->GetData(ui::ClipboardUtil::GetHtmlFormat(), &medium));
+ base::win::ScopedHGlobal<char> glob(medium.hGlobal);
+ std::string output(glob.get(), glob.Size());
+ EXPECT_EQ(expected_cf_html, output);
+ ReleaseStgMedium(&medium);
+}
+
+TEST(OSExchangeDataTest, SetURLWithMaxPath) {
+ OSExchangeData data;
+ std::wstring long_title(L'a', MAX_PATH + 1);
+ data.SetURL(GURL("http://google.com"), long_title);
+}
+
+TEST(OSExchangeDataTest, ProvideURLForPlainTextURL) {
+ OSExchangeData data;
+ data.SetString(L"http://google.com");
+
+ OSExchangeData data2(CloneProvider(data));
+ ASSERT_TRUE(data2.HasURL());
+ GURL read_url;
+ std::wstring title;
+ EXPECT_TRUE(data2.GetURLAndTitle(&read_url, &title));
+ EXPECT_EQ(GURL("http://google.com"), read_url);
+}
+
+} // namespace ui