summaryrefslogtreecommitdiffstats
path: root/app/clipboard
diff options
context:
space:
mode:
authorbrettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-03 04:25:37 +0000
committerbrettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-03 04:25:37 +0000
commit90f399075f9082bf7031512d6579ec1196dc060a (patch)
tree8d47273615d98732a60d0e77a0787d1623204a4b /app/clipboard
parentf0f400cb2e0a3c545d723524e8bbaf136caacf81 (diff)
downloadchromium_src-90f399075f9082bf7031512d6579ec1196dc060a.zip
chromium_src-90f399075f9082bf7031512d6579ec1196dc060a.tar.gz
chromium_src-90f399075f9082bf7031512d6579ec1196dc060a.tar.bz2
Move the clipboard stuff out of base and into app/clipboard. I renamed
clipboard_util to clipboard_util_win since it's Windows-only. This patch makes test_shell depend on app as well. There should be no logic change. TEST=none BUG=none Review URL: http://codereview.chromium.org/260003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@27937 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'app/clipboard')
-rw-r--r--app/clipboard/clipboard.cc65
-rw-r--r--app/clipboard/clipboard.h259
-rw-r--r--app/clipboard/clipboard_linux.cc410
-rw-r--r--app/clipboard/clipboard_mac.mm284
-rw-r--r--app/clipboard/clipboard_unittest.cc337
-rw-r--r--app/clipboard/clipboard_util_win.cc502
-rw-r--r--app/clipboard/clipboard_util_win.h65
-rw-r--r--app/clipboard/clipboard_win.cc686
-rw-r--r--app/clipboard/scoped_clipboard_writer.cc175
-rw-r--r--app/clipboard/scoped_clipboard_writer.h85
10 files changed, 2868 insertions, 0 deletions
diff --git a/app/clipboard/clipboard.cc b/app/clipboard/clipboard.cc
new file mode 100644
index 0000000..bc91408
--- /dev/null
+++ b/app/clipboard/clipboard.cc
@@ -0,0 +1,65 @@
+// Copyright (c) 2009 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 "app/clipboard/clipboard.h"
+
+#include "base/gfx/size.h"
+#include "base/logging.h"
+
+namespace {
+
+// A compromised renderer could send us bad data, so validate it.
+bool IsBitmapSafe(const Clipboard::ObjectMapParams& params) {
+ const gfx::Size* size =
+ reinterpret_cast<const gfx::Size*>(&(params[1].front()));
+ return params[0].size() ==
+ static_cast<size_t>(size->width() * size->height() * 4);
+}
+
+} // namespace
+
+void Clipboard::DispatchObject(ObjectType type, const ObjectMapParams& params) {
+ switch (type) {
+ case CBF_TEXT:
+ WriteText(&(params[0].front()), params[0].size());
+ break;
+
+ case CBF_HTML:
+ if (params.size() == 2)
+ WriteHTML(&(params[0].front()), params[0].size(),
+ &(params[1].front()), params[1].size());
+ else
+ WriteHTML(&(params[0].front()), params[0].size(), NULL, 0);
+ break;
+
+ case CBF_BOOKMARK:
+ WriteBookmark(&(params[0].front()), params[0].size(),
+ &(params[1].front()), params[1].size());
+ break;
+
+ case CBF_FILES:
+ WriteFiles(&(params[0].front()), params[0].size());
+ break;
+
+ case CBF_WEBKIT:
+ WriteWebSmartPaste();
+ break;
+
+#if defined(OS_WIN) || defined(OS_LINUX)
+ case CBF_BITMAP:
+ if (!IsBitmapSafe(params))
+ return;
+ WriteBitmap(&(params[0].front()), &(params[1].front()));
+ break;
+
+ case CBF_DATA:
+ WriteData(&(params[0].front()), params[0].size(),
+ &(params[1].front()), params[1].size());
+ break;
+#endif // defined(OS_WIN) || defined(OS_LINUX)
+
+ default:
+ NOTREACHED();
+ }
+}
diff --git a/app/clipboard/clipboard.h b/app/clipboard/clipboard.h
new file mode 100644
index 0000000..6dd3927
--- /dev/null
+++ b/app/clipboard/clipboard.h
@@ -0,0 +1,259 @@
+// Copyright (c) 2009 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 APP_CLIPBOARD_CLIPBOARD_H_
+#define APP_CLIPBOARD_CLIPBOARD_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/process.h"
+#include "base/string16.h"
+
+namespace gfx {
+class Size;
+}
+
+class FilePath;
+
+class Clipboard {
+ public:
+ typedef std::string FormatType;
+#if defined(USE_X11)
+ typedef struct _GtkClipboard GtkClipboard;
+ typedef std::map<FormatType, std::pair<char*, size_t> > TargetMap;
+#endif
+
+ // ObjectType designates the type of data to be stored in the clipboard. This
+ // designation is shared across all OSes. The system-specific designation
+ // is defined by FormatType. A single ObjectType might be represented by
+ // several system-specific FormatTypes. For example, on Linux the CBF_TEXT
+ // ObjectType maps to "text/plain", "STRING", and several other formats. On
+ // windows it maps to CF_UNICODETEXT.
+ enum ObjectType {
+ CBF_TEXT,
+ CBF_HTML,
+ CBF_BOOKMARK,
+ CBF_FILES,
+ CBF_WEBKIT,
+ CBF_BITMAP,
+ CBF_SMBITMAP, // Bitmap from shared memory.
+ CBF_DATA, // Arbitrary block of bytes.
+ };
+
+ // ObjectMap is a map from ObjectType to associated data.
+ // The data is organized differently for each ObjectType. The following
+ // table summarizes what kind of data is stored for each key.
+ // * indicates an optional argument.
+ //
+ // Key Arguments Type
+ // -------------------------------------
+ // CBF_TEXT text char array
+ // CBF_HTML html char array
+ // url* char array
+ // CBF_BOOKMARK html char array
+ // url char array
+ // CBF_LINK html char array
+ // url char array
+ // CBF_FILES files char array representing multiple files.
+ // Filenames are separated by null characters and
+ // the final filename is double null terminated.
+ // The filenames are encoded in platform-specific
+ // encoding.
+ // CBF_WEBKIT none empty vector
+ // CBF_BITMAP pixels byte array
+ // size gfx::Size struct
+ // CBF_SMBITMAP shared_mem shared memory handle
+ // size gfx::Size struct
+ // CBF_DATA format char array
+ // data byte array
+ typedef std::vector<char> ObjectMapParam;
+ typedef std::vector<ObjectMapParam> ObjectMapParams;
+ typedef std::map<int /* ObjectType */, ObjectMapParams> ObjectMap;
+
+ // Buffer designates which clipboard the action should be applied to.
+ // Only platforms that use the X Window System support the selection
+ // buffer. Furthermore we currently only use a buffer other than the
+ // standard buffer when reading from the clipboard so only those
+ // functions accept a buffer parameter.
+ enum Buffer {
+ BUFFER_STANDARD,
+#if defined(OS_LINUX)
+ BUFFER_SELECTION,
+#endif
+ };
+
+ static bool IsValidBuffer(int32 buffer) {
+ switch (buffer) {
+ case BUFFER_STANDARD:
+ return true;
+#if defined(OS_LINUX)
+ case BUFFER_SELECTION:
+ return true;
+#endif
+ }
+ return false;
+ }
+
+ static Buffer FromInt(int32 buffer) {
+ return static_cast<Buffer>(buffer);
+ }
+
+ Clipboard();
+ ~Clipboard();
+
+ // Write a bunch of objects to the system clipboard. Copies are made of the
+ // contents of |objects|. On Windows they are copied to the system clipboard.
+ // On linux they are copied into a structure owned by the Clipboard object and
+ // kept until the system clipboard is set again.
+ void WriteObjects(const ObjectMap& objects);
+
+ // Behaves as above. If there is some shared memory handle passed as one of
+ // the objects, it came from the process designated by |process|. This will
+ // assist in turning it into a shared memory region that the current process
+ // can use.
+ void WriteObjects(const ObjectMap& objects, base::ProcessHandle process);
+
+ // On Linux, we need to know when the clipboard is set to a URL. Most
+ // platforms don't care.
+#if !defined(OS_LINUX)
+ void DidWriteURL(const std::string& utf8_text) {}
+#else // !defined(OS_LINUX)
+ void DidWriteURL(const std::string& utf8_text);
+#endif
+
+ // Tests whether the clipboard contains a certain format
+ bool IsFormatAvailable(const FormatType& format, Buffer buffer) const;
+
+ // As above, but instead of interpreting |format| by some platform-specific
+ // definition, interpret it as a literal MIME type.
+ bool IsFormatAvailableByString(const std::string& format,
+ Buffer buffer) const;
+
+ // Reads UNICODE text from the clipboard, if available.
+ void ReadText(Buffer buffer, string16* result) const;
+
+ // Reads ASCII text from the clipboard, if available.
+ void ReadAsciiText(Buffer buffer, std::string* result) const;
+
+ // Reads HTML from the clipboard, if available.
+ void ReadHTML(Buffer buffer, string16* markup, std::string* src_url) const;
+
+ // Reads a bookmark from the clipboard, if available.
+ void ReadBookmark(string16* title, std::string* url) const;
+
+ // Reads a file or group of files from the clipboard, if available, into the
+ // out parameter.
+ void ReadFile(FilePath* file) const;
+ void ReadFiles(std::vector<FilePath>* files) const;
+
+ // Reads raw data from the clipboard with the given format type. Stores result
+ // as a byte vector.
+ void ReadData(const std::string& format, std::string* result);
+
+ // Get format Identifiers for various types.
+ static FormatType GetUrlFormatType();
+ static FormatType GetUrlWFormatType();
+ static FormatType GetMozUrlFormatType();
+ static FormatType GetPlainTextFormatType();
+ static FormatType GetPlainTextWFormatType();
+ static FormatType GetFilenameFormatType();
+ static FormatType GetFilenameWFormatType();
+ static FormatType GetWebKitSmartPasteFormatType();
+ // Win: MS HTML Format, Other: Generic HTML format
+ static FormatType GetHtmlFormatType();
+#if defined(OS_WIN)
+ static FormatType GetBitmapFormatType();
+ // Firefox text/html
+ static FormatType GetTextHtmlFormatType();
+ static FormatType GetCFHDropFormatType();
+ static FormatType GetFileDescriptorFormatType();
+ static FormatType GetFileContentFormatZeroType();
+
+ // Duplicates any remote shared memory handle embedded inside |objects| that
+ // was created by |process| so that it can be used by this process.
+ static void DuplicateRemoteHandles(base::ProcessHandle process,
+ ObjectMap* objects);
+#endif
+
+ private:
+ void DispatchObject(ObjectType type, const ObjectMapParams& params);
+
+ void WriteText(const char* text_data, size_t text_len);
+
+ void WriteHTML(const char* markup_data,
+ size_t markup_len,
+ const char* url_data,
+ size_t url_len);
+
+ void WriteBookmark(const char* title_data,
+ size_t title_len,
+ const char* url_data,
+ size_t url_len);
+
+ void WriteWebSmartPaste();
+
+ void WriteFiles(const char* file_data, size_t file_len);
+
+ void WriteBitmap(const char* pixel_data, const char* size_data);
+
+#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_FREEBSD)
+ // |format_name| is an ASCII string and should be NULL-terminated.
+ // TODO(estade): port to mac.
+ void WriteData(const char* format_name, size_t format_len,
+ const char* data_data, size_t data_len);
+#endif
+#if defined(OS_WIN)
+ void WriteBitmapFromSharedMemory(const char* bitmap_data,
+ const char* size_data,
+ base::ProcessHandle handle);
+
+ void WriteBitmapFromHandle(HBITMAP source_hbitmap,
+ const gfx::Size& size);
+
+ // Safely write to system clipboard. Free |handle| on failure.
+ void WriteToClipboard(unsigned int format, HANDLE handle);
+
+ static void ParseBookmarkClipboardFormat(const string16& bookmark,
+ string16* title,
+ std::string* url);
+
+ // Free a handle depending on its type (as intuited from format)
+ static void FreeData(unsigned int format, HANDLE data);
+
+ // Return the window that should be the clipboard owner, creating it
+ // if neccessary. Marked const for lazily initialization by const methods.
+ HWND GetClipboardWindow() const;
+
+ // Mark this as mutable so const methods can still do lazy initialization.
+ mutable HWND clipboard_owner_;
+
+ // True if we can create a window.
+ bool create_window_;
+#elif defined(USE_X11)
+ // Data is stored in the |clipboard_data_| map until it is saved to the system
+ // clipboard. The Store* functions save data to the |clipboard_data_| map. The
+ // SetGtkClipboard function replaces whatever is on the system clipboard with
+ // the contents of |clipboard_data_|.
+ // The Write* functions make a deep copy of the data passed to them an store
+ // it in |clipboard_data_|.
+
+ // Write changes to gtk clipboard.
+ void SetGtkClipboard();
+ // Insert a mapping into clipboard_data_.
+ void InsertMapping(const char* key, char* data, size_t data_len);
+
+ // find the gtk clipboard for the passed buffer enum
+ GtkClipboard* LookupBackingClipboard(Buffer clipboard) const;
+
+ TargetMap* clipboard_data_;
+ GtkClipboard* clipboard_;
+ GtkClipboard* primary_selection_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(Clipboard);
+};
+
+#endif // APP_CLIPBOARD_CLIPBOARD_H_
diff --git a/app/clipboard/clipboard_linux.cc b/app/clipboard/clipboard_linux.cc
new file mode 100644
index 0000000..8b30ffd
--- /dev/null
+++ b/app/clipboard/clipboard_linux.cc
@@ -0,0 +1,410 @@
+// Copyright (c) 2009 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 "app/clipboard/clipboard.h"
+
+#include <gtk/gtk.h>
+#include <map>
+#include <set>
+#include <string>
+#include <utility>
+
+#include "base/file_path.h"
+#include "base/gfx/size.h"
+#include "base/scoped_ptr.h"
+#include "base/linux_util.h"
+#include "base/string_util.h"
+
+namespace {
+
+const char kMimeBmp[] = "image/bmp";
+const char kMimeHtml[] = "text/html";
+const char kMimeText[] = "text/plain";
+const char kMimeURI[] = "text/uri-list";
+const char kMimeWebkitSmartPaste[] = "chromium/x-webkit-paste";
+
+std::string GdkAtomToString(const GdkAtom& atom) {
+ gchar* name = gdk_atom_name(atom);
+ std::string rv(name);
+ g_free(name);
+ return rv;
+}
+
+GdkAtom StringToGdkAtom(const std::string& str) {
+ return gdk_atom_intern(str.c_str(), false);
+}
+
+// GtkClipboardGetFunc callback.
+// GTK will call this when an application wants data we copied to the clipboard.
+void GetData(GtkClipboard* clipboard,
+ GtkSelectionData* selection_data,
+ guint info,
+ gpointer user_data) {
+ Clipboard::TargetMap* data_map =
+ reinterpret_cast<Clipboard::TargetMap*>(user_data);
+
+ std::string target_string = GdkAtomToString(selection_data->target);
+ Clipboard::TargetMap::iterator iter = data_map->find(target_string);
+
+ if (iter == data_map->end())
+ return;
+
+ if (target_string == kMimeBmp) {
+ gtk_selection_data_set_pixbuf(selection_data,
+ reinterpret_cast<GdkPixbuf*>(iter->second.first));
+ } else if (target_string == kMimeURI) {
+ gchar* uri_list[2];
+ uri_list[0] = reinterpret_cast<gchar*>(iter->second.first);
+ uri_list[1] = NULL;
+ gtk_selection_data_set_uris(selection_data, uri_list);
+ } else {
+ gtk_selection_data_set(selection_data, selection_data->target, 8,
+ reinterpret_cast<guchar*>(iter->second.first),
+ iter->second.second);
+ }
+}
+
+// GtkClipboardClearFunc callback.
+// We are guaranteed this will be called exactly once for each call to
+// gtk_clipboard_set_with_data
+void ClearData(GtkClipboard* clipboard,
+ gpointer user_data) {
+ Clipboard::TargetMap* map =
+ reinterpret_cast<Clipboard::TargetMap*>(user_data);
+ std::set<char*> ptrs;
+
+ for (Clipboard::TargetMap::iterator iter = map->begin();
+ iter != map->end(); ++iter) {
+ if (iter->first == kMimeBmp)
+ g_object_unref(reinterpret_cast<GdkPixbuf*>(iter->second.first));
+ else
+ ptrs.insert(iter->second.first);
+ }
+
+ for (std::set<char*>::iterator iter = ptrs.begin();
+ iter != ptrs.end(); ++iter) {
+ delete[] *iter;
+ }
+
+ delete map;
+}
+
+// Called on GdkPixbuf destruction; see WriteBitmap().
+void GdkPixbufFree(guchar* pixels, gpointer data) {
+ free(pixels);
+}
+
+} // namespace
+
+Clipboard::Clipboard() {
+ clipboard_ = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
+ primary_selection_ = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
+}
+
+Clipboard::~Clipboard() {
+ // TODO(estade): do we want to save clipboard data after we exit?
+ // gtk_clipboard_set_can_store and gtk_clipboard_store work
+ // but have strangely awful performance.
+}
+
+void Clipboard::WriteObjects(const ObjectMap& objects) {
+ clipboard_data_ = new TargetMap();
+
+ for (ObjectMap::const_iterator iter = objects.begin();
+ iter != objects.end(); ++iter) {
+ DispatchObject(static_cast<ObjectType>(iter->first), iter->second);
+ }
+
+ SetGtkClipboard();
+}
+
+// When a URL is copied from a render view context menu (via "copy link
+// location", for example), we additionally stick it in the X clipboard. This
+// matches other linux browsers.
+void Clipboard::DidWriteURL(const std::string& utf8_text) {
+ gtk_clipboard_set_text(primary_selection_, utf8_text.c_str(),
+ utf8_text.length());
+}
+
+// Take ownership of the GTK clipboard and inform it of the targets we support.
+void Clipboard::SetGtkClipboard() {
+ scoped_array<GtkTargetEntry> targets(
+ new GtkTargetEntry[clipboard_data_->size()]);
+
+ int i = 0;
+ for (Clipboard::TargetMap::iterator iter = clipboard_data_->begin();
+ iter != clipboard_data_->end(); ++iter, ++i) {
+ targets[i].target = static_cast<gchar*>(malloc(iter->first.size() + 1));
+ memcpy(targets[i].target, iter->first.data(), iter->first.size());
+ targets[i].target[iter->first.size()] = '\0';
+
+ targets[i].flags = 0;
+ targets[i].info = i;
+ }
+
+ gtk_clipboard_set_with_data(clipboard_, targets.get(),
+ clipboard_data_->size(),
+ GetData, ClearData,
+ clipboard_data_);
+
+ for (size_t i = 0; i < clipboard_data_->size(); i++)
+ free(targets[i].target);
+}
+
+void Clipboard::WriteText(const char* text_data, size_t text_len) {
+ char* data = new char[text_len];
+ memcpy(data, text_data, text_len);
+
+ InsertMapping(kMimeText, data, text_len);
+ InsertMapping("TEXT", data, text_len);
+ InsertMapping("STRING", data, text_len);
+ InsertMapping("UTF8_STRING", data, text_len);
+ InsertMapping("COMPOUND_TEXT", data, text_len);
+}
+
+void Clipboard::WriteHTML(const char* markup_data,
+ size_t markup_len,
+ const char* url_data,
+ size_t url_len) {
+ // TODO(estade): might not want to ignore |url_data|
+ char* data = new char[markup_len];
+ memcpy(data, markup_data, markup_len);
+
+ InsertMapping(kMimeHtml, data, markup_len);
+}
+
+// Write an extra flavor that signifies WebKit was the last to modify the
+// pasteboard. This flavor has no data.
+void Clipboard::WriteWebSmartPaste() {
+ InsertMapping(kMimeWebkitSmartPaste, NULL, 0);
+}
+
+void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) {
+ const gfx::Size* size = reinterpret_cast<const gfx::Size*>(size_data);
+
+ guchar* data = base::BGRAToRGBA(reinterpret_cast<const uint8_t*>(pixel_data),
+ size->width(), size->height(), 0);
+
+ GdkPixbuf* pixbuf =
+ gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, TRUE,
+ 8, size->width(), size->height(),
+ size->width() * 4, GdkPixbufFree, NULL);
+ // We store the GdkPixbuf*, and the size_t half of the pair is meaningless.
+ // Note that this contrasts with the vast majority of entries in our target
+ // map, which directly store the data and its length.
+ InsertMapping(kMimeBmp, reinterpret_cast<char*>(pixbuf), 0);
+}
+
+void Clipboard::WriteBookmark(const char* title_data, size_t title_len,
+ const char* url_data, size_t url_len) {
+ // Write as plain text.
+ WriteText(url_data, url_len);
+
+ // Write as a URI.
+ char* data = new char[url_len + 1];
+ memcpy(data, url_data, url_len);
+ data[url_len] = NULL;
+ InsertMapping(kMimeURI, data, url_len + 1);
+}
+
+void Clipboard::WriteFiles(const char* file_data, size_t file_len) {
+ NOTIMPLEMENTED();
+}
+
+void Clipboard::WriteData(const char* format_name, size_t format_len,
+ const char* data_data, size_t data_len) {
+ char* data = new char[data_len];
+ memcpy(data, data_data, data_len);
+ std::string format(format_name, format_len);
+ InsertMapping(format.c_str(), data, data_len);
+}
+
+// We do not use gtk_clipboard_wait_is_target_available because of
+// a bug with the gtk clipboard. It caches the available targets
+// and does not always refresh the cache when it is appropriate.
+bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format,
+ Clipboard::Buffer buffer) const {
+ GtkClipboard* clipboard = LookupBackingClipboard(buffer);
+ if (clipboard == NULL)
+ return false;
+
+ bool format_is_plain_text = GetPlainTextFormatType() == format;
+ if (format_is_plain_text) {
+ // This tries a number of common text targets.
+ if (gtk_clipboard_wait_is_text_available(clipboard))
+ return true;
+ }
+
+ bool retval = false;
+ GdkAtom* targets = NULL;
+ GtkSelectionData* data =
+ gtk_clipboard_wait_for_contents(clipboard,
+ gdk_atom_intern("TARGETS", false));
+
+ if (!data)
+ return false;
+
+ int num = 0;
+ gtk_selection_data_get_targets(data, &targets, &num);
+
+ // Some programs post data to the clipboard without any targets. If this is
+ // the case we attempt to make sense of the contents as text. This is pretty
+ // unfortunate since it means we have to actually copy the data to see if it
+ // is available, but at least this path shouldn't be hit for conforming
+ // programs.
+ if (num <= 0) {
+ if (format_is_plain_text) {
+ gchar* text = gtk_clipboard_wait_for_text(clipboard);
+ if (text) {
+ g_free(text);
+ retval = true;
+ }
+ }
+ }
+
+ GdkAtom format_atom = StringToGdkAtom(format);
+
+ for (int i = 0; i < num; i++) {
+ if (targets[i] == format_atom) {
+ retval = true;
+ break;
+ }
+ }
+
+ g_free(targets);
+ gtk_selection_data_free(data);
+
+ return retval;
+}
+
+bool Clipboard::IsFormatAvailableByString(const std::string& format,
+ Clipboard::Buffer buffer) const {
+ return IsFormatAvailable(format, buffer);
+}
+
+void Clipboard::ReadText(Clipboard::Buffer buffer, string16* result) const {
+ GtkClipboard* clipboard = LookupBackingClipboard(buffer);
+ if (clipboard == NULL)
+ return;
+
+ result->clear();
+ gchar* text = gtk_clipboard_wait_for_text(clipboard);
+
+ if (text == NULL)
+ return;
+
+ // TODO(estade): do we want to handle the possible error here?
+ UTF8ToUTF16(text, strlen(text), result);
+ g_free(text);
+}
+
+void Clipboard::ReadAsciiText(Clipboard::Buffer buffer,
+ std::string* result) const {
+ GtkClipboard* clipboard = LookupBackingClipboard(buffer);
+ if (clipboard == NULL)
+ return;
+
+ result->clear();
+ gchar* text = gtk_clipboard_wait_for_text(clipboard);
+
+ if (text == NULL)
+ return;
+
+ result->assign(text);
+ g_free(text);
+}
+
+void Clipboard::ReadFile(FilePath* file) const {
+ *file = FilePath();
+}
+
+// TODO(estade): handle different charsets.
+// TODO(port): set *src_url.
+void Clipboard::ReadHTML(Clipboard::Buffer buffer, string16* markup,
+ std::string* src_url) const {
+ GtkClipboard* clipboard = LookupBackingClipboard(buffer);
+ if (clipboard == NULL)
+ return;
+ markup->clear();
+
+ GtkSelectionData* data = gtk_clipboard_wait_for_contents(clipboard,
+ StringToGdkAtom(GetHtmlFormatType()));
+
+ if (!data)
+ return;
+
+ UTF8ToUTF16(reinterpret_cast<char*>(data->data), data->length, markup);
+ gtk_selection_data_free(data);
+}
+
+void Clipboard::ReadBookmark(string16* title, std::string* url) const {
+ // TODO(estade): implement this.
+}
+
+void Clipboard::ReadData(const std::string& format, std::string* result) {
+ GtkSelectionData* data =
+ gtk_clipboard_wait_for_contents(clipboard_, StringToGdkAtom(format));
+ if (!data)
+ return;
+ result->assign(reinterpret_cast<char*>(data->data), data->length);
+ gtk_selection_data_free(data);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetPlainTextFormatType() {
+ return GdkAtomToString(GDK_TARGET_STRING);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetPlainTextWFormatType() {
+ return GetPlainTextFormatType();
+}
+
+// static
+Clipboard::FormatType Clipboard::GetHtmlFormatType() {
+ return std::string(kMimeHtml);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetWebKitSmartPasteFormatType() {
+ return std::string(kMimeWebkitSmartPaste);
+}
+
+// Insert the key/value pair in the clipboard_data structure. If
+// the mapping already exists, it frees the associated data. Don't worry
+// about double freeing because if the same key is inserted into the
+// map twice, it must have come from different Write* functions and the
+// data pointer cannot be the same.
+void Clipboard::InsertMapping(const char* key,
+ char* data,
+ size_t data_len) {
+ TargetMap::iterator iter = clipboard_data_->find(key);
+
+ if (iter != clipboard_data_->end()) {
+ if (strcmp(kMimeBmp, key) == 0)
+ g_object_unref(reinterpret_cast<GdkPixbuf*>(iter->second.first));
+ else
+ delete[] iter->second.first;
+ }
+
+ (*clipboard_data_)[key] = std::make_pair(data, data_len);
+}
+
+GtkClipboard* Clipboard::LookupBackingClipboard(Buffer clipboard) const {
+ GtkClipboard* result;
+
+ switch (clipboard) {
+ case BUFFER_STANDARD:
+ result = clipboard_;
+ break;
+ case BUFFER_SELECTION:
+ result = primary_selection_;
+ break;
+ default:
+ NOTREACHED();
+ result = NULL;
+ break;
+ }
+ return result;
+}
diff --git a/app/clipboard/clipboard_mac.mm b/app/clipboard/clipboard_mac.mm
new file mode 100644
index 0000000..77f286a
--- /dev/null
+++ b/app/clipboard/clipboard_mac.mm
@@ -0,0 +1,284 @@
+// Copyright (c) 2006-2008 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 "app/clipboard/clipboard.h"
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/sys_string_conversions.h"
+
+namespace {
+
+// Would be nice if this were in UTCoreTypes.h, but it isn't
+const NSString* kUTTypeURLName = @"public.url-name";
+
+// Tells us if WebKit was the last to write to the pasteboard. There's no
+// actual data associated with this type.
+const NSString *kWebSmartPastePboardType = @"NeXT smart paste pasteboard type";
+
+NSPasteboard* GetPasteboard() {
+ // The pasteboard should not be nil in a UI session, but this handy DCHECK
+ // can help track down problems if someone tries using clipboard code outside
+ // of a UI session.
+ NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
+ DCHECK(pasteboard);
+ return pasteboard;
+}
+
+} // namespace
+
+Clipboard::Clipboard() {
+}
+
+Clipboard::~Clipboard() {
+}
+
+void Clipboard::WriteObjects(const ObjectMap& objects) {
+ NSPasteboard* pb = GetPasteboard();
+ [pb declareTypes:[NSArray array] owner:nil];
+
+ for (ObjectMap::const_iterator iter = objects.begin();
+ iter != objects.end(); ++iter) {
+ DispatchObject(static_cast<ObjectType>(iter->first), iter->second);
+ }
+
+}
+
+void Clipboard::WriteText(const char* text_data, size_t text_len) {
+ std::string text_str(text_data, text_len);
+ NSString *text = base::SysUTF8ToNSString(text_str);
+ NSPasteboard* pb = GetPasteboard();
+ [pb addTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
+ [pb setString:text forType:NSStringPboardType];
+}
+
+void Clipboard::WriteHTML(const char* markup_data,
+ size_t markup_len,
+ const char* url_data,
+ size_t url_len) {
+ std::string html_fragment_str(markup_data, markup_len);
+ NSString *html_fragment = base::SysUTF8ToNSString(html_fragment_str);
+
+ // TODO(avi): url_data?
+ NSPasteboard* pb = GetPasteboard();
+ [pb addTypes:[NSArray arrayWithObject:NSHTMLPboardType] owner:nil];
+ [pb setString:html_fragment forType:NSHTMLPboardType];
+}
+
+void Clipboard::WriteBookmark(const char* title_data,
+ size_t title_len,
+ const char* url_data,
+ size_t url_len) {
+ std::string title_str(title_data, title_len);
+ NSString *title = base::SysUTF8ToNSString(title_str);
+ std::string url_str(url_data, url_len);
+ NSString *url = base::SysUTF8ToNSString(url_str);
+
+ // TODO(playmobil): In the Windows version of this function, an HTML
+ // representation of the bookmark is also added to the clipboard, to support
+ // drag and drop of web shortcuts. I don't think we need to do this on the
+ // Mac, but we should double check later on.
+ NSURL* nsurl = [NSURL URLWithString:url];
+
+ NSPasteboard* pb = GetPasteboard();
+ // passing UTIs into the pasteboard methods is valid >= 10.5
+ [pb addTypes:[NSArray arrayWithObjects:NSURLPboardType,
+ kUTTypeURLName,
+ nil]
+ owner:nil];
+ [nsurl writeToPasteboard:pb];
+ [pb setString:title forType:kUTTypeURLName];
+}
+
+void Clipboard::WriteFiles(const char* file_data, size_t file_len) {
+ NSMutableArray* fileList = [NSMutableArray arrayWithCapacity:1];
+
+ // Offset of current filename from start of file_data array.
+ size_t current_filename_offset = 0;
+
+ // file_data is double null terminated (see table at top of clipboard.h).
+ // So this loop can ignore the second null terminator, thus file_len - 1.
+ // TODO(playmobil): If we need a loop like this on other platforms then split
+ // this out into a common function that outputs a std::vector<const char*>.
+ for (size_t i = 0; i < file_len - 1; ++i) {
+ if (file_data[i] == '\0') {
+ const char* filename = &file_data[current_filename_offset];
+ [fileList addObject:[NSString stringWithUTF8String:filename]];
+
+ current_filename_offset = i + 1;
+ continue;
+ }
+ }
+
+ NSPasteboard* pb = GetPasteboard();
+ [pb addTypes:[NSArray arrayWithObject:NSFilenamesPboardType] owner:nil];
+ [pb setPropertyList:fileList forType:NSFilenamesPboardType];
+}
+
+// Write an extra flavor that signifies WebKit was the last to modify the
+// pasteboard. This flavor has no data.
+void Clipboard::WriteWebSmartPaste() {
+ NSPasteboard* pb = GetPasteboard();
+ NSString* format = base::SysUTF8ToNSString(GetWebKitSmartPasteFormatType());
+ [pb addTypes:[NSArray arrayWithObject:format] owner:nil];
+ [pb setData:nil forType:format];
+}
+
+bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format,
+ Clipboard::Buffer buffer) const {
+ DCHECK_EQ(buffer, BUFFER_STANDARD);
+ NSString* format_ns = base::SysUTF8ToNSString(format);
+
+ NSPasteboard* pb = GetPasteboard();
+ NSArray* types = [pb types];
+
+ return [types containsObject:format_ns];
+}
+
+void Clipboard::ReadText(Clipboard::Buffer buffer, string16* result) const {
+ DCHECK_EQ(buffer, BUFFER_STANDARD);
+ NSPasteboard* pb = GetPasteboard();
+ NSString* contents = [pb stringForType:NSStringPboardType];
+
+ UTF8ToUTF16([contents UTF8String],
+ [contents lengthOfBytesUsingEncoding:NSUTF8StringEncoding],
+ result);
+}
+
+void Clipboard::ReadAsciiText(Clipboard::Buffer buffer,
+ std::string* result) const {
+ DCHECK_EQ(buffer, BUFFER_STANDARD);
+ NSPasteboard* pb = GetPasteboard();
+ NSString* contents = [pb stringForType:NSStringPboardType];
+
+ if (!contents)
+ result->clear();
+ else
+ result->assign([contents UTF8String]);
+}
+
+void Clipboard::ReadHTML(Clipboard::Buffer buffer, string16* markup,
+ std::string* src_url) const {
+ DCHECK_EQ(buffer, BUFFER_STANDARD);
+ if (markup) {
+ NSPasteboard* pb = GetPasteboard();
+ NSArray *supportedTypes = [NSArray arrayWithObjects:NSHTMLPboardType,
+ NSStringPboardType,
+ nil];
+ NSString *bestType = [pb availableTypeFromArray:supportedTypes];
+ NSString* contents = [pb stringForType:bestType];
+ UTF8ToUTF16([contents UTF8String],
+ [contents lengthOfBytesUsingEncoding:NSUTF8StringEncoding],
+ markup);
+ }
+
+ // TODO(avi): src_url?
+ if (src_url)
+ src_url->clear();
+}
+
+void Clipboard::ReadBookmark(string16* title, std::string* url) const {
+ NSPasteboard* pb = GetPasteboard();
+
+ if (title) {
+ NSString* contents = [pb stringForType:kUTTypeURLName];
+ UTF8ToUTF16([contents UTF8String],
+ [contents lengthOfBytesUsingEncoding:NSUTF8StringEncoding],
+ title);
+ }
+
+ if (url) {
+ NSString* url_string = [[NSURL URLFromPasteboard:pb] absoluteString];
+ if (!url_string)
+ url->clear();
+ else
+ url->assign([url_string UTF8String]);
+ }
+}
+
+void Clipboard::ReadFile(FilePath* file) const {
+ if (!file) {
+ NOTREACHED();
+ return;
+ }
+
+ *file = FilePath();
+ std::vector<FilePath> files;
+ ReadFiles(&files);
+
+ // Take the first file, if available.
+ if (!files.empty())
+ *file = files[0];
+}
+
+void Clipboard::ReadFiles(std::vector<FilePath>* files) const {
+ if (!files) {
+ NOTREACHED();
+ return;
+ }
+
+ files->clear();
+
+ NSPasteboard* pb = GetPasteboard();
+ NSArray* fileList = [pb propertyListForType:NSFilenamesPboardType];
+
+ for (unsigned int i = 0; i < [fileList count]; ++i) {
+ std::string file = [[fileList objectAtIndex:i] UTF8String];
+ files->push_back(FilePath(file));
+ }
+}
+
+// static
+Clipboard::FormatType Clipboard::GetUrlFormatType() {
+ static const std::string type = base::SysNSStringToUTF8(NSURLPboardType);
+ return type;
+}
+
+// static
+Clipboard::FormatType Clipboard::GetUrlWFormatType() {
+ static const std::string type = base::SysNSStringToUTF8(NSURLPboardType);
+ return type;
+}
+
+// static
+Clipboard::FormatType Clipboard::GetPlainTextFormatType() {
+ static const std::string type = base::SysNSStringToUTF8(NSStringPboardType);
+ return type;
+}
+
+// static
+Clipboard::FormatType Clipboard::GetPlainTextWFormatType() {
+ static const std::string type = base::SysNSStringToUTF8(NSStringPboardType);
+ return type;
+}
+
+// static
+Clipboard::FormatType Clipboard::GetFilenameFormatType() {
+ static const std::string type =
+ base::SysNSStringToUTF8(NSFilenamesPboardType);
+ return type;
+}
+
+// static
+Clipboard::FormatType Clipboard::GetFilenameWFormatType() {
+ static const std::string type =
+ base::SysNSStringToUTF8(NSFilenamesPboardType);
+ return type;
+}
+
+// static
+Clipboard::FormatType Clipboard::GetHtmlFormatType() {
+ static const std::string type = base::SysNSStringToUTF8(NSHTMLPboardType);
+ return type;
+}
+
+// static
+Clipboard::FormatType Clipboard::GetWebKitSmartPasteFormatType() {
+ static const std::string type =
+ base::SysNSStringToUTF8(kWebSmartPastePboardType);
+ return type;
+}
diff --git a/app/clipboard/clipboard_unittest.cc b/app/clipboard/clipboard_unittest.cc
new file mode 100644
index 0000000..5b1d48a8
--- /dev/null
+++ b/app/clipboard/clipboard_unittest.cc
@@ -0,0 +1,337 @@
+// Copyright (c) 2006-2008 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 <string>
+
+#include "app/clipboard/clipboard.h"
+#include "app/clipboard/scoped_clipboard_writer.h"
+#include "base/basictypes.h"
+#include "base/gfx/size.h"
+#include "base/message_loop.h"
+#include "base/pickle.h"
+#include "base/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+#if defined(OS_WIN)
+class ClipboardTest : public PlatformTest {
+ protected:
+ virtual void SetUp() {
+ message_loop_.reset(new MessageLoopForUI());
+ }
+ virtual void TearDown() {
+ }
+
+ private:
+ scoped_ptr<MessageLoop> message_loop_;
+};
+#elif defined(OS_POSIX)
+typedef PlatformTest ClipboardTest;
+#endif // defined(OS_WIN)
+
+TEST_F(ClipboardTest, ClearTest) {
+ Clipboard clipboard;
+
+ {
+ ScopedClipboardWriter clipboard_writer(&clipboard);
+ clipboard_writer.WriteText(ASCIIToUTF16("clear me"));
+ }
+
+ {
+ ScopedClipboardWriter clipboard_writer(&clipboard);
+ clipboard_writer.WriteHTML(ASCIIToUTF16("<b>broom</b>"), "");
+ }
+
+ EXPECT_FALSE(clipboard.IsFormatAvailable(
+ Clipboard::GetPlainTextWFormatType(), Clipboard::BUFFER_STANDARD));
+ EXPECT_FALSE(clipboard.IsFormatAvailable(
+ Clipboard::GetPlainTextFormatType(), Clipboard::BUFFER_STANDARD));
+}
+
+TEST_F(ClipboardTest, TextTest) {
+ Clipboard clipboard;
+
+ string16 text(ASCIIToUTF16("This is a string16!#$")), text_result;
+ std::string ascii_text;
+
+ {
+ ScopedClipboardWriter clipboard_writer(&clipboard);
+ clipboard_writer.WriteText(text);
+ }
+
+ EXPECT_TRUE(clipboard.IsFormatAvailable(
+ Clipboard::GetPlainTextWFormatType(), Clipboard::BUFFER_STANDARD));
+ EXPECT_TRUE(clipboard.IsFormatAvailable(Clipboard::GetPlainTextFormatType(),
+ Clipboard::BUFFER_STANDARD));
+ clipboard.ReadText(Clipboard::BUFFER_STANDARD, &text_result);
+
+ EXPECT_EQ(text, text_result);
+ clipboard.ReadAsciiText(Clipboard::BUFFER_STANDARD, &ascii_text);
+ EXPECT_EQ(UTF16ToUTF8(text), ascii_text);
+}
+
+TEST_F(ClipboardTest, HTMLTest) {
+ Clipboard clipboard;
+
+ string16 markup(ASCIIToUTF16("<string>Hi!</string>")), markup_result;
+ std::string url("http://www.example.com/"), url_result;
+
+ {
+ ScopedClipboardWriter clipboard_writer(&clipboard);
+ clipboard_writer.WriteHTML(markup, url);
+ }
+
+ EXPECT_TRUE(clipboard.IsFormatAvailable(Clipboard::GetHtmlFormatType(),
+ Clipboard::BUFFER_STANDARD));
+ clipboard.ReadHTML(Clipboard::BUFFER_STANDARD, &markup_result, &url_result);
+ EXPECT_EQ(markup, markup_result);
+#if defined(OS_WIN)
+ // TODO(playmobil): It's not clear that non windows clipboards need to support
+ // this.
+ EXPECT_EQ(url, url_result);
+#endif // defined(OS_WIN)
+}
+
+TEST_F(ClipboardTest, TrickyHTMLTest) {
+ Clipboard clipboard;
+
+ string16 markup(ASCIIToUTF16("<em>Bye!<!--EndFragment --></em>")),
+ markup_result;
+ std::string url, url_result;
+
+ {
+ ScopedClipboardWriter clipboard_writer(&clipboard);
+ clipboard_writer.WriteHTML(markup, url);
+ }
+
+ EXPECT_TRUE(clipboard.IsFormatAvailable(Clipboard::GetHtmlFormatType(),
+ Clipboard::BUFFER_STANDARD));
+ clipboard.ReadHTML(Clipboard::BUFFER_STANDARD, &markup_result, &url_result);
+ EXPECT_EQ(markup, markup_result);
+#if defined(OS_WIN)
+ // TODO(playmobil): It's not clear that non windows clipboards need to support
+ // this.
+ EXPECT_EQ(url, url_result);
+#endif // defined(OS_WIN)
+}
+
+// TODO(estade): Port the following test (decide what target we use for urls)
+#if !defined(OS_LINUX)
+TEST_F(ClipboardTest, BookmarkTest) {
+ Clipboard clipboard;
+
+ string16 title(ASCIIToUTF16("The Example Company")), title_result;
+ std::string url("http://www.example.com/"), url_result;
+
+ {
+ ScopedClipboardWriter clipboard_writer(&clipboard);
+ clipboard_writer.WriteBookmark(title, url);
+ }
+
+ EXPECT_TRUE(clipboard.IsFormatAvailable(Clipboard::GetUrlWFormatType(),
+ Clipboard::BUFFER_STANDARD));
+ clipboard.ReadBookmark(&title_result, &url_result);
+ EXPECT_EQ(title, title_result);
+ EXPECT_EQ(url, url_result);
+}
+#endif // defined(OS_WIN)
+
+TEST_F(ClipboardTest, MultiFormatTest) {
+ Clipboard clipboard;
+
+ string16 text(ASCIIToUTF16("Hi!")), text_result;
+ string16 markup(ASCIIToUTF16("<strong>Hi!</string>")), markup_result;
+ std::string url("http://www.example.com/"), url_result;
+ std::string ascii_text;
+
+ {
+ ScopedClipboardWriter clipboard_writer(&clipboard);
+ clipboard_writer.WriteHTML(markup, url);
+ clipboard_writer.WriteText(text);
+ }
+
+ EXPECT_TRUE(clipboard.IsFormatAvailable(Clipboard::GetHtmlFormatType(),
+ Clipboard::BUFFER_STANDARD));
+ EXPECT_TRUE(clipboard.IsFormatAvailable(
+ Clipboard::GetPlainTextWFormatType(), Clipboard::BUFFER_STANDARD));
+ EXPECT_TRUE(clipboard.IsFormatAvailable(
+ Clipboard::GetPlainTextFormatType(), Clipboard::BUFFER_STANDARD));
+ clipboard.ReadHTML(Clipboard::BUFFER_STANDARD, &markup_result, &url_result);
+ EXPECT_EQ(markup, markup_result);
+#if defined(OS_WIN)
+ // TODO(playmobil): It's not clear that non windows clipboards need to support
+ // this.
+ EXPECT_EQ(url, url_result);
+#endif // defined(OS_WIN)
+ clipboard.ReadText(Clipboard::BUFFER_STANDARD, &text_result);
+ EXPECT_EQ(text, text_result);
+ clipboard.ReadAsciiText(Clipboard::BUFFER_STANDARD, &ascii_text);
+ EXPECT_EQ(UTF16ToUTF8(text), ascii_text);
+}
+
+// TODO(estade): Port the following tests (decide what targets we use for files)
+#if !defined(OS_LINUX)
+// Files for this test don't actually need to exist on the file system, just
+// don't try to use a non-existent file you've retrieved from the clipboard.
+TEST_F(ClipboardTest, FileTest) {
+ Clipboard clipboard;
+#if defined(OS_WIN)
+ FilePath file(L"C:\\Downloads\\My Downloads\\A Special File.txt");
+#elif defined(OS_MACOSX)
+ // OS X will print a warning message if we stick a non-existant file on the
+ // clipboard.
+ FilePath file("/usr/bin/make");
+#endif // defined(OS_MACOSX)
+
+ {
+ ScopedClipboardWriter clipboard_writer(&clipboard);
+ clipboard_writer.WriteFile(file);
+ }
+
+ FilePath out_file;
+ clipboard.ReadFile(&out_file);
+ EXPECT_EQ(file.value(), out_file.value());
+}
+
+TEST_F(ClipboardTest, MultipleFilesTest) {
+ Clipboard clipboard;
+
+#if defined(OS_WIN)
+ FilePath file1(L"C:\\Downloads\\My Downloads\\File 1.exe");
+ FilePath file2(L"C:\\Downloads\\My Downloads\\File 2.pdf");
+ FilePath file3(L"C:\\Downloads\\My Downloads\\File 3.doc");
+#elif defined(OS_MACOSX)
+ // OS X will print a warning message if we stick a non-existant file on the
+ // clipboard.
+ FilePath file1("/usr/bin/make");
+ FilePath file2("/usr/bin/man");
+ FilePath file3("/usr/bin/perl");
+#endif // defined(OS_MACOSX)
+ std::vector<FilePath> files;
+ files.push_back(file1);
+ files.push_back(file2);
+ files.push_back(file3);
+
+ {
+ ScopedClipboardWriter clipboard_writer(&clipboard);
+ clipboard_writer.WriteFiles(files);
+ }
+
+ std::vector<FilePath> out_files;
+ clipboard.ReadFiles(&out_files);
+
+ EXPECT_EQ(files.size(), out_files.size());
+ for (size_t i = 0; i < out_files.size(); ++i)
+ EXPECT_EQ(files[i].value(), out_files[i].value());
+}
+#endif // !defined(OS_LINUX)
+
+TEST_F(ClipboardTest, URLTest) {
+ Clipboard clipboard;
+
+ string16 url(ASCIIToUTF16("http://www.google.com/"));
+
+ {
+ ScopedClipboardWriter clipboard_writer(&clipboard);
+ clipboard_writer.WriteURL(url);
+ }
+
+ EXPECT_TRUE(clipboard.IsFormatAvailable(
+ Clipboard::GetPlainTextWFormatType(), Clipboard::BUFFER_STANDARD));
+ EXPECT_TRUE(clipboard.IsFormatAvailable(Clipboard::GetPlainTextFormatType(),
+ Clipboard::BUFFER_STANDARD));
+ string16 text_result;
+ clipboard.ReadText(Clipboard::BUFFER_STANDARD, &text_result);
+
+ EXPECT_EQ(text_result, url);
+
+ std::string ascii_text;
+ clipboard.ReadAsciiText(Clipboard::BUFFER_STANDARD, &ascii_text);
+ EXPECT_EQ(UTF16ToUTF8(url), ascii_text);
+
+#if defined(OS_LINUX)
+ ascii_text.clear();
+ clipboard.ReadAsciiText(Clipboard::BUFFER_SELECTION, &ascii_text);
+ EXPECT_EQ(UTF16ToUTF8(url), ascii_text);
+#endif // defined(OS_LINUX)
+}
+
+#if defined(OS_WIN) || defined(OS_LINUX)
+TEST_F(ClipboardTest, DataTest) {
+ Clipboard clipboard;
+ const char* format = "chromium/x-test-format";
+ std::string payload("test string");
+ Pickle write_pickle;
+ write_pickle.WriteString(payload);
+
+ {
+ ScopedClipboardWriter clipboard_writer(&clipboard);
+ clipboard_writer.WritePickledData(write_pickle, format);
+ }
+
+ ASSERT_TRUE(clipboard.IsFormatAvailableByString(
+ format, Clipboard::BUFFER_STANDARD));
+ std::string output;
+ clipboard.ReadData(format, &output);
+ ASSERT_FALSE(output.empty());
+
+ Pickle read_pickle(output.data(), output.size());
+ void* iter = NULL;
+ std::string unpickled_string;
+ ASSERT_TRUE(read_pickle.ReadString(&iter, &unpickled_string));
+ EXPECT_EQ(payload, unpickled_string);
+}
+#endif
+
+#if defined(OS_WIN) // Windows only tests.
+TEST_F(ClipboardTest, HyperlinkTest) {
+ Clipboard clipboard;
+
+ std::string title("The Example Company");
+ std::string url("http://www.example.com/"), url_result;
+ std::string html("<a href=\"http://www.example.com/\">"
+ "The Example Company</a>");
+ string16 html_result;
+
+ {
+ ScopedClipboardWriter clipboard_writer(&clipboard);
+ clipboard_writer.WriteHyperlink(title, url);
+ }
+
+ EXPECT_TRUE(clipboard.IsFormatAvailable(Clipboard::GetHtmlFormatType(),
+ Clipboard::BUFFER_STANDARD));
+ clipboard.ReadHTML(Clipboard::BUFFER_STANDARD, &html_result, &url_result);
+ EXPECT_EQ(UTF8ToUTF16(html), html_result);
+}
+
+TEST_F(ClipboardTest, WebSmartPasteTest) {
+ Clipboard clipboard;
+
+ {
+ ScopedClipboardWriter clipboard_writer(&clipboard);
+ clipboard_writer.WriteWebSmartPaste();
+ }
+
+ EXPECT_TRUE(clipboard.IsFormatAvailable(
+ Clipboard::GetWebKitSmartPasteFormatType(), Clipboard::BUFFER_STANDARD));
+}
+
+TEST_F(ClipboardTest, BitmapTest) {
+ unsigned int fake_bitmap[] = {
+ 0x46155189, 0xF6A55C8D, 0x79845674, 0xFA57BD89,
+ 0x78FD46AE, 0x87C64F5A, 0x36EDC5AF, 0x4378F568,
+ 0x91E9F63A, 0xC31EA14F, 0x69AB32DF, 0x643A3FD1,
+ };
+
+ Clipboard clipboard;
+
+ {
+ ScopedClipboardWriter clipboard_writer(&clipboard);
+ clipboard_writer.WriteBitmapFromPixels(fake_bitmap, gfx::Size(3, 4));
+ }
+
+ EXPECT_TRUE(clipboard.IsFormatAvailable(Clipboard::GetBitmapFormatType(),
+ Clipboard::BUFFER_STANDARD));
+}
+#endif // defined(OS_WIN)
diff --git a/app/clipboard/clipboard_util_win.cc b/app/clipboard/clipboard_util_win.cc
new file mode 100644
index 0000000..158ad81
--- /dev/null
+++ b/app/clipboard/clipboard_util_win.cc
@@ -0,0 +1,502 @@
+// Copyright (c) 2009 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 "app/clipboard/clipboard_util_win.h"
+
+#include <shellapi.h>
+#include <shlwapi.h>
+#include <wininet.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/scoped_handle.h"
+#include "base/string_util.h"
+
+namespace {
+
+bool GetUrlFromHDrop(IDataObject* data_object, std::wstring* url,
+ std::wstring* title) {
+ DCHECK(data_object && url && title);
+
+ STGMEDIUM medium;
+ if (FAILED(data_object->GetData(ClipboardUtil::GetCFHDropFormat(), &medium)))
+ return false;
+
+ HDROP hdrop = static_cast<HDROP>(GlobalLock(medium.hGlobal));
+
+ if (!hdrop)
+ return false;
+
+ bool success = false;
+ wchar_t filename[MAX_PATH];
+ if (DragQueryFileW(hdrop, 0, filename, arraysize(filename))) {
+ wchar_t url_buffer[INTERNET_MAX_URL_LENGTH];
+ if (0 == _wcsicmp(PathFindExtensionW(filename), L".url") &&
+ GetPrivateProfileStringW(L"InternetShortcut", L"url", 0, url_buffer,
+ arraysize(url_buffer), filename)) {
+ *url = url_buffer;
+ PathRemoveExtension(filename);
+ title->assign(PathFindFileName(filename));
+ success = true;
+ }
+ }
+
+ DragFinish(hdrop);
+ GlobalUnlock(medium.hGlobal);
+ // We don't need to call ReleaseStgMedium here because as far as I can tell,
+ // DragFinish frees the hGlobal for us.
+ return success;
+}
+
+bool SplitUrlAndTitle(const std::wstring& str, std::wstring* url,
+ std::wstring* title) {
+ DCHECK(url && title);
+ size_t newline_pos = str.find('\n');
+ bool success = false;
+ if (newline_pos != std::string::npos) {
+ *url = str.substr(0, newline_pos);
+ title->assign(str.substr(newline_pos + 1));
+ success = true;
+ } else {
+ *url = str;
+ title->assign(str);
+ success = true;
+ }
+ return success;
+}
+
+} // namespace
+
+
+FORMATETC* ClipboardUtil::GetUrlFormat() {
+ static UINT cf = RegisterClipboardFormat(CFSTR_INETURLA);
+ static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &format;
+}
+
+FORMATETC* ClipboardUtil::GetUrlWFormat() {
+ static UINT cf = RegisterClipboardFormat(CFSTR_INETURLW);
+ static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &format;
+}
+
+FORMATETC* ClipboardUtil::GetMozUrlFormat() {
+ // The format is "URL\nTitle"
+ static UINT cf = RegisterClipboardFormat(L"text/x-moz-url");
+ static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &format;
+}
+
+FORMATETC* ClipboardUtil::GetPlainTextFormat() {
+ // We don't need to register this format since it's a built in format.
+ static FORMATETC format = {CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &format;
+}
+
+FORMATETC* ClipboardUtil::GetPlainTextWFormat() {
+ // We don't need to register this format since it's a built in format.
+ static FORMATETC format = {CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1,
+ TYMED_HGLOBAL};
+ return &format;
+}
+
+FORMATETC* ClipboardUtil::GetFilenameWFormat() {
+ static UINT cf = RegisterClipboardFormat(CFSTR_FILENAMEW);
+ static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &format;
+}
+
+FORMATETC* ClipboardUtil::GetFilenameFormat() {
+ static UINT cf = RegisterClipboardFormat(CFSTR_FILENAMEA);
+ static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &format;
+}
+
+FORMATETC* ClipboardUtil::GetHtmlFormat() {
+ static UINT cf = RegisterClipboardFormat(L"HTML Format");
+ static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &format;
+}
+
+FORMATETC* ClipboardUtil::GetTextHtmlFormat() {
+ static UINT cf = RegisterClipboardFormat(L"text/html");
+ static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &format;
+}
+
+FORMATETC* ClipboardUtil::GetCFHDropFormat() {
+ // We don't need to register this format since it's a built in format.
+ static FORMATETC format = {CF_HDROP, 0, DVASPECT_CONTENT, -1,
+ TYMED_HGLOBAL};
+ return &format;
+}
+
+FORMATETC* ClipboardUtil::GetFileDescriptorFormat() {
+ static UINT cf = RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR);
+ static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &format;
+}
+
+FORMATETC* ClipboardUtil::GetFileContentFormatZero() {
+ static UINT cf = RegisterClipboardFormat(CFSTR_FILECONTENTS);
+ static FORMATETC format = {cf, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL};
+ return &format;
+}
+
+FORMATETC* ClipboardUtil::GetWebKitSmartPasteFormat() {
+ static UINT cf = RegisterClipboardFormat(L"WebKit Smart Paste Format");
+ static FORMATETC format = {cf, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL};
+ return &format;
+}
+
+
+bool ClipboardUtil::HasUrl(IDataObject* data_object) {
+ DCHECK(data_object);
+ return SUCCEEDED(data_object->QueryGetData(GetMozUrlFormat())) ||
+ SUCCEEDED(data_object->QueryGetData(GetUrlWFormat())) ||
+ SUCCEEDED(data_object->QueryGetData(GetUrlFormat())) ||
+ SUCCEEDED(data_object->QueryGetData(GetFilenameWFormat())) ||
+ SUCCEEDED(data_object->QueryGetData(GetFilenameFormat()));
+}
+
+bool ClipboardUtil::HasFilenames(IDataObject* data_object) {
+ DCHECK(data_object);
+ return SUCCEEDED(data_object->QueryGetData(GetCFHDropFormat()));
+}
+
+bool ClipboardUtil::HasFileContents(IDataObject* data_object) {
+ DCHECK(data_object);
+ return SUCCEEDED(data_object->QueryGetData(GetFileContentFormatZero()));
+}
+
+bool ClipboardUtil::HasHtml(IDataObject* data_object) {
+ DCHECK(data_object);
+ return SUCCEEDED(data_object->QueryGetData(GetHtmlFormat())) ||
+ SUCCEEDED(data_object->QueryGetData(GetTextHtmlFormat()));
+}
+
+bool ClipboardUtil::HasPlainText(IDataObject* data_object) {
+ DCHECK(data_object);
+ return SUCCEEDED(data_object->QueryGetData(GetPlainTextWFormat())) ||
+ SUCCEEDED(data_object->QueryGetData(GetPlainTextFormat()));
+}
+
+
+bool ClipboardUtil::GetUrl(IDataObject* data_object,
+ std::wstring* url, std::wstring* title) {
+ DCHECK(data_object && url && title);
+ if (!HasUrl(data_object))
+ return false;
+
+ // Try to extract a URL from |data_object| in a variety of formats.
+ STGMEDIUM store;
+ if (GetUrlFromHDrop(data_object, url, title)) {
+ return true;
+ }
+
+ if (SUCCEEDED(data_object->GetData(GetMozUrlFormat(), &store)) ||
+ SUCCEEDED(data_object->GetData(GetUrlWFormat(), &store))) {
+ // Mozilla URL format or unicode URL
+ ScopedHGlobal<wchar_t> data(store.hGlobal);
+ bool success = SplitUrlAndTitle(data.get(), url, title);
+ ReleaseStgMedium(&store);
+ if (success)
+ return true;
+ }
+
+ if (SUCCEEDED(data_object->GetData(GetUrlFormat(), &store))) {
+ // URL using ascii
+ ScopedHGlobal<char> data(store.hGlobal);
+ bool success = SplitUrlAndTitle(UTF8ToWide(data.get()), url, title);
+ ReleaseStgMedium(&store);
+ if (success)
+ return true;
+ }
+
+ if (SUCCEEDED(data_object->GetData(GetFilenameWFormat(), &store))) {
+ // filename using unicode
+ ScopedHGlobal<wchar_t> data(store.hGlobal);
+ bool success = false;
+ if (data.get() && data.get()[0] && (PathFileExists(data.get()) ||
+ PathIsUNC(data.get()))) {
+ wchar_t file_url[INTERNET_MAX_URL_LENGTH];
+ DWORD file_url_len = sizeof(file_url) / sizeof(file_url[0]);
+ if (SUCCEEDED(::UrlCreateFromPathW(data.get(), file_url, &file_url_len,
+ 0))) {
+ *url = file_url;
+ title->assign(file_url);
+ success = true;
+ }
+ }
+ ReleaseStgMedium(&store);
+ if (success)
+ return true;
+ }
+
+ if (SUCCEEDED(data_object->GetData(GetFilenameFormat(), &store))) {
+ // filename using ascii
+ ScopedHGlobal<char> data(store.hGlobal);
+ bool success = false;
+ if (data.get() && data.get()[0] && (PathFileExistsA(data.get()) ||
+ PathIsUNCA(data.get()))) {
+ char file_url[INTERNET_MAX_URL_LENGTH];
+ DWORD file_url_len = sizeof(file_url) / sizeof(file_url[0]);
+ if (SUCCEEDED(::UrlCreateFromPathA(data.get(),
+ file_url,
+ &file_url_len,
+ 0))) {
+ *url = UTF8ToWide(file_url);
+ title->assign(*url);
+ success = true;
+ }
+ }
+ ReleaseStgMedium(&store);
+ if (success)
+ return true;
+ }
+
+ return false;
+}
+
+bool ClipboardUtil::GetFilenames(IDataObject* data_object,
+ std::vector<std::wstring>* filenames) {
+ DCHECK(data_object && filenames);
+ if (!HasFilenames(data_object))
+ return false;
+
+ STGMEDIUM medium;
+ if (FAILED(data_object->GetData(GetCFHDropFormat(), &medium)))
+ return false;
+
+ HDROP hdrop = static_cast<HDROP>(GlobalLock(medium.hGlobal));
+ if (!hdrop)
+ return false;
+
+ const int kMaxFilenameLen = 4096;
+ const unsigned num_files = DragQueryFileW(hdrop, 0xffffffff, 0, 0);
+ for (unsigned int i = 0; i < num_files; ++i) {
+ wchar_t filename[kMaxFilenameLen];
+ if (!DragQueryFileW(hdrop, i, filename, kMaxFilenameLen))
+ continue;
+ filenames->push_back(filename);
+ }
+
+ DragFinish(hdrop);
+ GlobalUnlock(medium.hGlobal);
+ // We don't need to call ReleaseStgMedium here because as far as I can tell,
+ // DragFinish frees the hGlobal for us.
+ return true;
+}
+
+bool ClipboardUtil::GetPlainText(IDataObject* data_object,
+ std::wstring* plain_text) {
+ DCHECK(data_object && plain_text);
+ if (!HasPlainText(data_object))
+ return false;
+
+ STGMEDIUM store;
+ bool success = false;
+ if (SUCCEEDED(data_object->GetData(GetPlainTextWFormat(), &store))) {
+ // Unicode text
+ ScopedHGlobal<wchar_t> data(store.hGlobal);
+ plain_text->assign(data.get());
+ ReleaseStgMedium(&store);
+ success = true;
+ } else if (SUCCEEDED(data_object->GetData(GetPlainTextFormat(), &store))) {
+ // ascii text
+ ScopedHGlobal<char> data(store.hGlobal);
+ plain_text->assign(UTF8ToWide(data.get()));
+ ReleaseStgMedium(&store);
+ success = true;
+ } else {
+ // If a file is dropped on the window, it does not provide either of the
+ // plain text formats, so here we try to forcibly get a url.
+ std::wstring title;
+ success = GetUrl(data_object, plain_text, &title);
+ }
+
+ return success;
+}
+
+bool ClipboardUtil::GetHtml(IDataObject* data_object,
+ std::wstring* html, std::string* base_url) {
+ DCHECK(data_object && html && base_url);
+
+ if (SUCCEEDED(data_object->QueryGetData(GetHtmlFormat()))) {
+ STGMEDIUM store;
+ if (SUCCEEDED(data_object->GetData(GetHtmlFormat(), &store))) {
+ // MS CF html
+ ScopedHGlobal<char> data(store.hGlobal);
+
+ std::string html_utf8;
+ CFHtmlToHtml(std::string(data.get(), data.Size()), &html_utf8, base_url);
+ html->assign(UTF8ToWide(html_utf8));
+
+ ReleaseStgMedium(&store);
+ return true;
+ }
+ }
+
+ if (FAILED(data_object->QueryGetData(GetTextHtmlFormat())))
+ return false;
+
+ STGMEDIUM store;
+ if (FAILED(data_object->GetData(GetTextHtmlFormat(), &store)))
+ return false;
+
+ // text/html
+ ScopedHGlobal<wchar_t> data(store.hGlobal);
+ html->assign(data.get());
+ ReleaseStgMedium(&store);
+ return true;
+}
+
+bool ClipboardUtil::GetFileContents(IDataObject* data_object,
+ std::wstring* filename, std::string* file_contents) {
+ DCHECK(data_object && filename && file_contents);
+ bool has_data =
+ SUCCEEDED(data_object->QueryGetData(GetFileContentFormatZero())) ||
+ SUCCEEDED(data_object->QueryGetData(GetFileDescriptorFormat()));
+
+ if (!has_data)
+ return false;
+
+ STGMEDIUM content;
+ // The call to GetData can be very slow depending on what is in
+ // |data_object|.
+ if (SUCCEEDED(data_object->GetData(GetFileContentFormatZero(), &content))) {
+ if (TYMED_HGLOBAL == content.tymed) {
+ ScopedHGlobal<char> data(content.hGlobal);
+ file_contents->assign(data.get(), data.Size());
+ }
+ ReleaseStgMedium(&content);
+ }
+
+ STGMEDIUM description;
+ if (SUCCEEDED(data_object->GetData(GetFileDescriptorFormat(),
+ &description))) {
+ ScopedHGlobal<FILEGROUPDESCRIPTOR> fgd(description.hGlobal);
+ // We expect there to be at least one file in here.
+ DCHECK_GE(fgd->cItems, 1u);
+ filename->assign(fgd->fgd[0].cFileName);
+ ReleaseStgMedium(&description);
+ }
+ return true;
+}
+
+// HtmlToCFHtml and CFHtmlToHtml are based on similar methods in
+// WebCore/platform/win/ClipboardUtilitiesWin.cpp.
+/*
+ * Copyright (C) 2007, 2008 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.
+ */
+
+// Helper method for converting from text/html to MS CF_HTML.
+// Documentation for the CF_HTML format is available at
+// http://msdn.microsoft.com/en-us/library/aa767917(VS.85).aspx
+std::string ClipboardUtil::HtmlToCFHtml(const std::string& html,
+ const std::string& base_url) {
+ if (html.empty())
+ return std::string();
+
+ #define MAX_DIGITS 10
+ #define MAKE_NUMBER_FORMAT_1(digits) MAKE_NUMBER_FORMAT_2(digits)
+ #define MAKE_NUMBER_FORMAT_2(digits) "%0" #digits "u"
+ #define NUMBER_FORMAT MAKE_NUMBER_FORMAT_1(MAX_DIGITS)
+
+ static const char* header = "Version:0.9\r\n"
+ "StartHTML:" NUMBER_FORMAT "\r\n"
+ "EndHTML:" NUMBER_FORMAT "\r\n"
+ "StartFragment:" NUMBER_FORMAT "\r\n"
+ "EndFragment:" NUMBER_FORMAT "\r\n";
+ static const char* source_url_prefix = "SourceURL:";
+
+ static const char* start_markup =
+ "<html>\r\n<body>\r\n<!--StartFragment-->\r\n";
+ static const char* end_markup =
+ "\r\n<!--EndFragment-->\r\n</body>\r\n</html>";
+
+ // Calculate offsets
+ size_t start_html_offset = strlen(header) - strlen(NUMBER_FORMAT) * 4 +
+ MAX_DIGITS * 4;
+ if (!base_url.empty()) {
+ start_html_offset += strlen(source_url_prefix) +
+ base_url.length() + 2; // Add 2 for \r\n.
+ }
+ size_t start_fragment_offset = start_html_offset + strlen(start_markup);
+ size_t end_fragment_offset = start_fragment_offset + html.length();
+ size_t end_html_offset = end_fragment_offset + strlen(end_markup);
+
+ std::string result = StringPrintf(header, start_html_offset,
+ end_html_offset, start_fragment_offset, end_fragment_offset);
+ if (!base_url.empty()) {
+ result.append(source_url_prefix);
+ result.append(base_url);
+ result.append("\r\n");
+ }
+ result.append(start_markup);
+ result.append(html);
+ result.append(end_markup);
+
+ #undef MAX_DIGITS
+ #undef MAKE_NUMBER_FORMAT_1
+ #undef MAKE_NUMBER_FORMAT_2
+ #undef NUMBER_FORMAT
+
+ return result;
+}
+
+// Helper method for converting from MS CF_HTML to text/html.
+void ClipboardUtil::CFHtmlToHtml(const std::string& cf_html,
+ std::string* html,
+ std::string* base_url) {
+ // Obtain base_url if present.
+ if (base_url) {
+ static std::string src_url_str("SourceURL:");
+ size_t line_start = cf_html.find(src_url_str);
+ if (line_start != std::string::npos) {
+ size_t src_end = cf_html.find("\n", line_start);
+ size_t src_start = line_start + src_url_str.length();
+ if (src_end != std::string::npos && src_start != std::string::npos) {
+ *base_url = cf_html.substr(src_start, src_end - src_start);
+ TrimWhitespace(*base_url, TRIM_ALL, base_url);
+ }
+ }
+ }
+
+ // Find the markup between "<!--StartFragment -->" and "<!--EndFragment-->".
+ if (html) {
+ std::string cf_html_lower = StringToLowerASCII(cf_html);
+ size_t markup_start = cf_html_lower.find("<html", 0);
+ size_t tag_start = cf_html.find("StartFragment", markup_start);
+ size_t fragment_start = cf_html.find('>', tag_start) + 1;
+ size_t tag_end = cf_html.rfind("EndFragment", std::string::npos);
+ size_t fragment_end = cf_html.rfind('<', tag_end);
+ if (fragment_start != std::string::npos &&
+ fragment_end != std::string::npos) {
+ *html = cf_html.substr(fragment_start, fragment_end - fragment_start);
+ TrimWhitespace(*html, TRIM_ALL, html);
+ }
+ }
+}
diff --git a/app/clipboard/clipboard_util_win.h b/app/clipboard/clipboard_util_win.h
new file mode 100644
index 0000000..5bbe8f0
--- /dev/null
+++ b/app/clipboard/clipboard_util_win.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2009 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.
+//
+// Some helper functions for working with the clipboard and IDataObjects.
+
+#ifndef APP_CLIPBOARD_CLIPBOARD_UTIL_WIN_H_
+#define APP_CLIPBOARD_CLIPBOARD_UTIL_WIN_H_
+
+#include <shlobj.h>
+#include <string>
+#include <vector>
+
+class ClipboardUtil {
+ public:
+ /////////////////////////////////////////////////////////////////////////////
+ // Clipboard formats.
+ static FORMATETC* GetUrlFormat();
+ static FORMATETC* GetUrlWFormat();
+ static FORMATETC* GetMozUrlFormat();
+ static FORMATETC* GetPlainTextFormat();
+ static FORMATETC* GetPlainTextWFormat();
+ static FORMATETC* GetFilenameFormat();
+ static FORMATETC* GetFilenameWFormat();
+ // MS HTML Format
+ static FORMATETC* GetHtmlFormat();
+ // Firefox text/html
+ static FORMATETC* GetTextHtmlFormat();
+ static FORMATETC* GetCFHDropFormat();
+ static FORMATETC* GetFileDescriptorFormat();
+ static FORMATETC* GetFileContentFormatZero();
+ static FORMATETC* GetWebKitSmartPasteFormat();
+
+ /////////////////////////////////////////////////////////////////////////////
+ // These methods check to see if |data_object| has the requested type.
+ // Returns true if it does.
+ static bool HasUrl(IDataObject* data_object);
+ static bool HasFilenames(IDataObject* data_object);
+ static bool HasPlainText(IDataObject* data_object);
+ static bool HasFileContents(IDataObject* data_object);
+ static bool HasHtml(IDataObject* data_object);
+
+ /////////////////////////////////////////////////////////////////////////////
+ // Helper methods to extract information from an IDataObject. These methods
+ // return true if the requested data type is found in |data_object|.
+ static bool GetUrl(IDataObject* data_object,
+ std::wstring* url, std::wstring* title);
+ static bool GetFilenames(IDataObject* data_object,
+ std::vector<std::wstring>* filenames);
+ static bool GetPlainText(IDataObject* data_object, std::wstring* plain_text);
+ static bool GetHtml(IDataObject* data_object, std::wstring* text_html,
+ std::string* base_url);
+ static bool GetFileContents(IDataObject* data_object,
+ std::wstring* filename,
+ std::string* file_contents);
+
+ // A helper method for converting between MS CF_HTML format and plain
+ // text/html.
+ static std::string HtmlToCFHtml(const std::string& html,
+ const std::string& base_url);
+ static void CFHtmlToHtml(const std::string& cf_html, std::string* html,
+ std::string* base_url);
+};
+
+#endif // APP_CLIPBOARD_CLIPBOARD_UTIL_WIN_H_
diff --git a/app/clipboard/clipboard_win.cc b/app/clipboard/clipboard_win.cc
new file mode 100644
index 0000000..288c820
--- /dev/null
+++ b/app/clipboard/clipboard_win.cc
@@ -0,0 +1,686 @@
+// Copyright (c) 2009 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.
+
+// Many of these functions are based on those found in
+// webkit/port/platform/PasteboardWin.cpp
+
+#include "app/clipboard/clipboard.h"
+
+#include <shlobj.h>
+#include <shellapi.h>
+
+#include "app/clipboard/clipboard_util_win.h"
+#include "base/file_path.h"
+#include "base/gfx/size.h"
+#include "base/lock.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/shared_memory.h"
+#include "base/string_util.h"
+
+namespace {
+
+// A scoper to manage acquiring and automatically releasing the clipboard.
+class ScopedClipboard {
+ public:
+ ScopedClipboard() : opened_(false) { }
+
+ ~ScopedClipboard() {
+ if (opened_)
+ Release();
+ }
+
+ bool Acquire(HWND owner) {
+ const int kMaxAttemptsToOpenClipboard = 5;
+
+ if (opened_) {
+ NOTREACHED();
+ return false;
+ }
+
+ // Attempt to open the clipboard, which will acquire the Windows clipboard
+ // lock. This may fail if another process currently holds this lock.
+ // We're willing to try a few times in the hopes of acquiring it.
+ //
+ // This turns out to be an issue when using remote desktop because the
+ // rdpclip.exe process likes to read what we've written to the clipboard and
+ // send it to the RDP client. If we open and close the clipboard in quick
+ // succession, we might be trying to open it while rdpclip.exe has it open,
+ // See Bug 815425.
+ //
+ // In fact, we believe we'll only spin this loop over remote desktop. In
+ // normal situations, the user is initiating clipboard operations and there
+ // shouldn't be contention.
+
+ for (int attempts = 0; attempts < kMaxAttemptsToOpenClipboard; ++attempts) {
+ // If we didn't manage to open the clipboard, sleep a bit and be hopeful.
+ if (attempts != 0)
+ ::Sleep(5);
+
+ if (::OpenClipboard(owner)) {
+ opened_ = true;
+ return true;
+ }
+ }
+
+ // We failed to acquire the clipboard.
+ return false;
+ }
+
+ void Release() {
+ if (opened_) {
+ ::CloseClipboard();
+ opened_ = false;
+ } else {
+ NOTREACHED();
+ }
+ }
+
+ private:
+ bool opened_;
+};
+
+LRESULT CALLBACK ClipboardOwnerWndProc(HWND hwnd,
+ UINT message,
+ WPARAM wparam,
+ LPARAM lparam) {
+ LRESULT lresult = 0;
+
+ switch (message) {
+ case WM_RENDERFORMAT:
+ // This message comes when SetClipboardData was sent a null data handle
+ // and now it's come time to put the data on the clipboard.
+ // We always set data, so there isn't a need to actually do anything here.
+ break;
+ case WM_RENDERALLFORMATS:
+ // This message comes when SetClipboardData was sent a null data handle
+ // and now this application is about to quit, so it must put data on
+ // the clipboard before it exits.
+ // We always set data, so there isn't a need to actually do anything here.
+ break;
+ case WM_DRAWCLIPBOARD:
+ break;
+ case WM_DESTROY:
+ break;
+ case WM_CHANGECBCHAIN:
+ break;
+ default:
+ lresult = DefWindowProc(hwnd, message, wparam, lparam);
+ break;
+ }
+ return lresult;
+}
+
+template <typename charT>
+HGLOBAL CreateGlobalData(const std::basic_string<charT>& str) {
+ HGLOBAL data =
+ ::GlobalAlloc(GMEM_MOVEABLE, ((str.size() + 1) * sizeof(charT)));
+ if (data) {
+ charT* raw_data = static_cast<charT*>(::GlobalLock(data));
+ memcpy(raw_data, str.data(), str.size() * sizeof(charT));
+ raw_data[str.size()] = '\0';
+ ::GlobalUnlock(data);
+ }
+ return data;
+};
+
+} // namespace
+
+Clipboard::Clipboard() : create_window_(false) {
+ if (MessageLoop::current()->type() == MessageLoop::TYPE_UI) {
+ // Make a dummy HWND to be the clipboard's owner.
+ WNDCLASSEX wcex = {0};
+ wcex.cbSize = sizeof(WNDCLASSEX);
+ wcex.lpfnWndProc = ClipboardOwnerWndProc;
+ wcex.hInstance = GetModuleHandle(NULL);
+ wcex.lpszClassName = L"ClipboardOwnerWindowClass";
+ ::RegisterClassEx(&wcex);
+ create_window_ = true;
+ }
+
+ clipboard_owner_ = NULL;
+}
+
+Clipboard::~Clipboard() {
+ if (clipboard_owner_)
+ ::DestroyWindow(clipboard_owner_);
+ clipboard_owner_ = NULL;
+}
+
+void Clipboard::WriteObjects(const ObjectMap& objects) {
+ WriteObjects(objects, NULL);
+}
+
+void Clipboard::WriteObjects(const ObjectMap& objects,
+ base::ProcessHandle process) {
+ ScopedClipboard clipboard;
+ if (!clipboard.Acquire(GetClipboardWindow()))
+ return;
+
+ ::EmptyClipboard();
+
+ for (ObjectMap::const_iterator iter = objects.begin();
+ iter != objects.end(); ++iter) {
+ if (iter->first == CBF_SMBITMAP)
+ WriteBitmapFromSharedMemory(&(iter->second[0].front()),
+ &(iter->second[1].front()),
+ process);
+ else
+ DispatchObject(static_cast<ObjectType>(iter->first), iter->second);
+ }
+}
+
+void Clipboard::WriteText(const char* text_data, size_t text_len) {
+ string16 text;
+ UTF8ToUTF16(text_data, text_len, &text);
+ HGLOBAL glob = CreateGlobalData(text);
+
+ WriteToClipboard(CF_UNICODETEXT, glob);
+}
+
+void Clipboard::WriteHTML(const char* markup_data,
+ size_t markup_len,
+ const char* url_data,
+ size_t url_len) {
+ std::string markup(markup_data, markup_len);
+ std::string url;
+
+ if (url_len > 0)
+ url.assign(url_data, url_len);
+
+ std::string html_fragment = ClipboardUtil::HtmlToCFHtml(markup, url);
+ HGLOBAL glob = CreateGlobalData(html_fragment);
+
+ WriteToClipboard(StringToInt(GetHtmlFormatType()), glob);
+}
+
+void Clipboard::WriteBookmark(const char* title_data,
+ size_t title_len,
+ const char* url_data,
+ size_t url_len) {
+ std::string bookmark(title_data, title_len);
+ bookmark.append(1, L'\n');
+ bookmark.append(url_data, url_len);
+
+ string16 wide_bookmark = UTF8ToWide(bookmark);
+ HGLOBAL glob = CreateGlobalData(wide_bookmark);
+
+ WriteToClipboard(StringToInt(GetUrlWFormatType()), glob);
+}
+
+void Clipboard::WriteWebSmartPaste() {
+ DCHECK(clipboard_owner_);
+ ::SetClipboardData(StringToInt(GetWebKitSmartPasteFormatType()), NULL);
+}
+
+void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) {
+ const gfx::Size* size = reinterpret_cast<const gfx::Size*>(size_data);
+ HDC dc = ::GetDC(NULL);
+
+ // This doesn't actually cost us a memcpy when the bitmap comes from the
+ // renderer as we load it into the bitmap using setPixels which just sets a
+ // pointer. Someone has to memcpy it into GDI, it might as well be us here.
+
+ // TODO(darin): share data in gfx/bitmap_header.cc somehow
+ BITMAPINFO bm_info = {0};
+ bm_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bm_info.bmiHeader.biWidth = size->width();
+ bm_info.bmiHeader.biHeight = -size->height(); // sets vertical orientation
+ bm_info.bmiHeader.biPlanes = 1;
+ bm_info.bmiHeader.biBitCount = 32;
+ bm_info.bmiHeader.biCompression = BI_RGB;
+
+ // ::CreateDIBSection allocates memory for us to copy our bitmap into.
+ // Unfortunately, we can't write the created bitmap to the clipboard,
+ // (see http://msdn2.microsoft.com/en-us/library/ms532292.aspx)
+ void *bits;
+ HBITMAP source_hbitmap =
+ ::CreateDIBSection(dc, &bm_info, DIB_RGB_COLORS, &bits, NULL, 0);
+
+ if (bits && source_hbitmap) {
+ // Copy the bitmap out of shared memory and into GDI
+ memcpy(bits, pixel_data, 4 * size->width() * size->height());
+
+ // Now we have an HBITMAP, we can write it to the clipboard
+ WriteBitmapFromHandle(source_hbitmap, *size);
+ }
+
+ ::DeleteObject(source_hbitmap);
+ ::ReleaseDC(NULL, dc);
+}
+
+void Clipboard::WriteBitmapFromSharedMemory(const char* bitmap_data,
+ const char* size_data,
+ base::ProcessHandle process) {
+ const gfx::Size* size = reinterpret_cast<const gfx::Size*>(size_data);
+
+ // bitmap_data has an encoded shared memory object. See
+ // DuplicateRemoteHandles().
+ char* ptr = const_cast<char*>(bitmap_data);
+ scoped_ptr<const base::SharedMemory> bitmap(*
+ reinterpret_cast<const base::SharedMemory**>(ptr));
+
+ // TODO(darin): share data in gfx/bitmap_header.cc somehow.
+ BITMAPINFO bm_info = {0};
+ bm_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bm_info.bmiHeader.biWidth = size->width();
+ // Sets the vertical orientation.
+ bm_info.bmiHeader.biHeight = -size->height();
+ bm_info.bmiHeader.biPlanes = 1;
+ bm_info.bmiHeader.biBitCount = 32;
+ bm_info.bmiHeader.biCompression = BI_RGB;
+
+ HDC dc = ::GetDC(NULL);
+
+ // We can create an HBITMAP directly using the shared memory handle, saving
+ // a memcpy.
+ HBITMAP source_hbitmap =
+ ::CreateDIBSection(dc, &bm_info, DIB_RGB_COLORS, NULL,
+ bitmap->handle(), 0);
+
+ if (source_hbitmap) {
+ // Now we can write the HBITMAP to the clipboard
+ WriteBitmapFromHandle(source_hbitmap, *size);
+ }
+
+ ::DeleteObject(source_hbitmap);
+ ::ReleaseDC(NULL, dc);
+}
+
+void Clipboard::WriteBitmapFromHandle(HBITMAP source_hbitmap,
+ const gfx::Size& size) {
+ // We would like to just call ::SetClipboardData on the source_hbitmap,
+ // but that bitmap might not be of a sort we can write to the clipboard.
+ // For this reason, we create a new bitmap, copy the bits over, and then
+ // write that to the clipboard.
+
+ HDC dc = ::GetDC(NULL);
+ HDC compatible_dc = ::CreateCompatibleDC(NULL);
+ HDC source_dc = ::CreateCompatibleDC(NULL);
+
+ // This is the HBITMAP we will eventually write to the clipboard
+ HBITMAP hbitmap = ::CreateCompatibleBitmap(dc, size.width(), size.height());
+ if (!hbitmap) {
+ // Failed to create the bitmap
+ ::DeleteDC(compatible_dc);
+ ::DeleteDC(source_dc);
+ ::ReleaseDC(NULL, dc);
+ return;
+ }
+
+ HBITMAP old_hbitmap = (HBITMAP)SelectObject(compatible_dc, hbitmap);
+ HBITMAP old_source = (HBITMAP)SelectObject(source_dc, source_hbitmap);
+
+ // Now we need to blend it into an HBITMAP we can place on the clipboard
+ BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
+ ::GdiAlphaBlend(compatible_dc, 0, 0, size.width(), size.height(),
+ source_dc, 0, 0, size.width(), size.height(), bf);
+
+ // Clean up all the handles we just opened
+ ::SelectObject(compatible_dc, old_hbitmap);
+ ::SelectObject(source_dc, old_source);
+ ::DeleteObject(old_hbitmap);
+ ::DeleteObject(old_source);
+ ::DeleteDC(compatible_dc);
+ ::DeleteDC(source_dc);
+ ::ReleaseDC(NULL, dc);
+
+ WriteToClipboard(CF_BITMAP, hbitmap);
+}
+
+// Write a file or set of files to the clipboard in HDROP format. When the user
+// invokes a paste command (in a Windows explorer shell, for example), the files
+// will be copied to the paste location.
+void Clipboard::WriteFiles(const char* file_data, size_t file_len) {
+ // Calculate the amount of space we'll need store the strings and
+ // a DROPFILES struct.
+ size_t bytes = sizeof(DROPFILES) + file_len;
+
+ HANDLE hdata = ::GlobalAlloc(GMEM_MOVEABLE, bytes);
+ if (!hdata)
+ return;
+
+ char* data = static_cast<char*>(::GlobalLock(hdata));
+ DROPFILES* drop_files = reinterpret_cast<DROPFILES*>(data);
+ drop_files->pFiles = sizeof(DROPFILES);
+ drop_files->fWide = TRUE;
+
+ memcpy(data + sizeof(DROPFILES), file_data, file_len);
+
+ ::GlobalUnlock(hdata);
+ WriteToClipboard(CF_HDROP, hdata);
+}
+
+void Clipboard::WriteData(const char* format_name, size_t format_len,
+ const char* data_data, size_t data_len) {
+ std::string format(format_name, format_len);
+ CLIPFORMAT clip_format =
+ ::RegisterClipboardFormat(ASCIIToWide(format).c_str());
+
+ HGLOBAL hdata = ::GlobalAlloc(GMEM_MOVEABLE, data_len);
+ if (!hdata)
+ return;
+
+ char* data = static_cast<char*>(::GlobalLock(hdata));
+ memcpy(data, data_data, data_len);
+ ::GlobalUnlock(data);
+ WriteToClipboard(clip_format, hdata);
+}
+
+void Clipboard::WriteToClipboard(unsigned int format, HANDLE handle) {
+ DCHECK(clipboard_owner_);
+ if (handle && !::SetClipboardData(format, handle)) {
+ DCHECK(ERROR_CLIPBOARD_NOT_OPEN != GetLastError());
+ FreeData(format, handle);
+ }
+}
+
+bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format,
+ Clipboard::Buffer buffer) const {
+ DCHECK_EQ(buffer, BUFFER_STANDARD);
+ return ::IsClipboardFormatAvailable(StringToInt(format)) != FALSE;
+}
+
+bool Clipboard::IsFormatAvailableByString(
+ const std::string& ascii_format, Clipboard::Buffer buffer) const {
+ DCHECK_EQ(buffer, BUFFER_STANDARD);
+ std::wstring wide_format = ASCIIToWide(ascii_format);
+ CLIPFORMAT format = ::RegisterClipboardFormat(wide_format.c_str());
+ return ::IsClipboardFormatAvailable(format) != FALSE;
+}
+
+void Clipboard::ReadText(Clipboard::Buffer buffer, string16* result) const {
+ DCHECK_EQ(buffer, BUFFER_STANDARD);
+ if (!result) {
+ NOTREACHED();
+ return;
+ }
+
+ result->clear();
+
+ // Acquire the clipboard.
+ ScopedClipboard clipboard;
+ if (!clipboard.Acquire(GetClipboardWindow()))
+ return;
+
+ HANDLE data = ::GetClipboardData(CF_UNICODETEXT);
+ if (!data)
+ return;
+
+ result->assign(static_cast<const char16*>(::GlobalLock(data)));
+ ::GlobalUnlock(data);
+}
+
+void Clipboard::ReadAsciiText(Clipboard::Buffer buffer,
+ std::string* result) const {
+ DCHECK_EQ(buffer, BUFFER_STANDARD);
+ if (!result) {
+ NOTREACHED();
+ return;
+ }
+
+ result->clear();
+
+ // Acquire the clipboard.
+ ScopedClipboard clipboard;
+ if (!clipboard.Acquire(GetClipboardWindow()))
+ return;
+
+ HANDLE data = ::GetClipboardData(CF_TEXT);
+ if (!data)
+ return;
+
+ result->assign(static_cast<const char*>(::GlobalLock(data)));
+ ::GlobalUnlock(data);
+}
+
+void Clipboard::ReadHTML(Clipboard::Buffer buffer, string16* markup,
+ std::string* src_url) const {
+ DCHECK_EQ(buffer, BUFFER_STANDARD);
+ if (markup)
+ markup->clear();
+
+ if (src_url)
+ src_url->clear();
+
+ // Acquire the clipboard.
+ ScopedClipboard clipboard;
+ if (!clipboard.Acquire(GetClipboardWindow()))
+ return;
+
+ HANDLE data = ::GetClipboardData(StringToInt(GetHtmlFormatType()));
+ if (!data)
+ return;
+
+ std::string html_fragment(static_cast<const char*>(::GlobalLock(data)));
+ ::GlobalUnlock(data);
+
+ std::string markup_utf8;
+ ClipboardUtil::CFHtmlToHtml(html_fragment, markup ? &markup_utf8 : NULL,
+ src_url);
+ if (markup)
+ markup->assign(UTF8ToWide(markup_utf8));
+}
+
+void Clipboard::ReadBookmark(string16* title, std::string* url) const {
+ if (title)
+ title->clear();
+
+ if (url)
+ url->clear();
+
+ // Acquire the clipboard.
+ ScopedClipboard clipboard;
+ if (!clipboard.Acquire(GetClipboardWindow()))
+ return;
+
+ HANDLE data = ::GetClipboardData(StringToInt(GetUrlWFormatType()));
+ if (!data)
+ return;
+
+ string16 bookmark(static_cast<const char16*>(::GlobalLock(data)));
+ ::GlobalUnlock(data);
+
+ ParseBookmarkClipboardFormat(bookmark, title, url);
+}
+
+// Read a file in HDROP format from the clipboard.
+void Clipboard::ReadFile(FilePath* file) const {
+ if (!file) {
+ NOTREACHED();
+ return;
+ }
+
+ *file = FilePath();
+ std::vector<FilePath> files;
+ ReadFiles(&files);
+
+ // Take the first file, if available.
+ if (!files.empty())
+ *file = files[0];
+}
+
+// Read a set of files in HDROP format from the clipboard.
+void Clipboard::ReadFiles(std::vector<FilePath>* files) const {
+ if (!files) {
+ NOTREACHED();
+ return;
+ }
+
+ files->clear();
+
+ ScopedClipboard clipboard;
+ if (!clipboard.Acquire(GetClipboardWindow()))
+ return;
+
+ HDROP drop = static_cast<HDROP>(::GetClipboardData(CF_HDROP));
+ if (!drop)
+ return;
+
+ // Count of files in the HDROP.
+ int count = ::DragQueryFile(drop, 0xffffffff, NULL, 0);
+
+ if (count) {
+ for (int i = 0; i < count; ++i) {
+ int size = ::DragQueryFile(drop, i, NULL, 0) + 1;
+ std::wstring file;
+ ::DragQueryFile(drop, i, WriteInto(&file, size), size);
+ files->push_back(FilePath(file));
+ }
+ }
+}
+
+void Clipboard::ReadData(const std::string& format, std::string* result) {
+ if (!result) {
+ NOTREACHED();
+ return;
+ }
+
+ CLIPFORMAT clip_format =
+ ::RegisterClipboardFormat(ASCIIToWide(format).c_str());
+
+ ScopedClipboard clipboard;
+ if (!clipboard.Acquire(GetClipboardWindow()))
+ return;
+
+ HANDLE data = ::GetClipboardData(clip_format);
+ if (!data)
+ return;
+
+ result->assign(static_cast<const char*>(::GlobalLock(data)),
+ ::GlobalSize(data));
+ ::GlobalUnlock(data);
+}
+
+// static
+void Clipboard::ParseBookmarkClipboardFormat(const string16& bookmark,
+ string16* title,
+ std::string* url) {
+ const string16 kDelim = ASCIIToUTF16("\r\n");
+
+ const size_t title_end = bookmark.find_first_of(kDelim);
+ if (title)
+ title->assign(bookmark.substr(0, title_end));
+
+ if (url) {
+ const size_t url_start = bookmark.find_first_not_of(kDelim, title_end);
+ if (url_start != string16::npos)
+ *url = UTF16ToUTF8(bookmark.substr(url_start, string16::npos));
+ }
+}
+
+// static
+Clipboard::FormatType Clipboard::GetUrlFormatType() {
+ return IntToString(ClipboardUtil::GetUrlFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetUrlWFormatType() {
+ return IntToString(ClipboardUtil::GetUrlWFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetMozUrlFormatType() {
+ return IntToString(ClipboardUtil::GetMozUrlFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetPlainTextFormatType() {
+ return IntToString(ClipboardUtil::GetPlainTextFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetPlainTextWFormatType() {
+ return IntToString(ClipboardUtil::GetPlainTextWFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetFilenameFormatType() {
+ return IntToString(ClipboardUtil::GetFilenameFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetFilenameWFormatType() {
+ return IntToString(ClipboardUtil::GetFilenameWFormat()->cfFormat);
+}
+
+// MS HTML Format
+// static
+Clipboard::FormatType Clipboard::GetHtmlFormatType() {
+ return IntToString(ClipboardUtil::GetHtmlFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetBitmapFormatType() {
+ return IntToString(CF_BITMAP);
+}
+
+// Firefox text/html
+// static
+Clipboard::FormatType Clipboard::GetTextHtmlFormatType() {
+ return IntToString(ClipboardUtil::GetTextHtmlFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetCFHDropFormatType() {
+ return IntToString(ClipboardUtil::GetCFHDropFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetFileDescriptorFormatType() {
+ return IntToString(ClipboardUtil::GetFileDescriptorFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetFileContentFormatZeroType() {
+ return IntToString(ClipboardUtil::GetFileContentFormatZero()->cfFormat);
+}
+
+// static
+void Clipboard::DuplicateRemoteHandles(base::ProcessHandle process,
+ ObjectMap* objects) {
+ for (ObjectMap::iterator iter = objects->begin(); iter != objects->end();
+ ++iter) {
+ if (iter->first == CBF_SMBITMAP) {
+ // There is a shared memory handle encoded on the first ObjectMapParam.
+ // Use it to open a local handle to the memory.
+ char* bitmap_data = &(iter->second[0].front());
+ base::SharedMemoryHandle* remote_bitmap_handle =
+ reinterpret_cast<base::SharedMemoryHandle*>(bitmap_data);
+
+ base::SharedMemory* bitmap = new base::SharedMemory(*remote_bitmap_handle,
+ false, process);
+
+ // We store the object where the remote handle was located so it can
+ // be retrieved by the UI thread (see WriteBitmapFromSharedMemory()).
+ iter->second[0].clear();
+ for (size_t i = 0; i < sizeof(bitmap); i++)
+ iter->second[0].push_back(reinterpret_cast<char*>(&bitmap)[i]);
+ }
+ }
+}
+
+// static
+Clipboard::FormatType Clipboard::GetWebKitSmartPasteFormatType() {
+ return IntToString(ClipboardUtil::GetWebKitSmartPasteFormat()->cfFormat);
+}
+
+// static
+void Clipboard::FreeData(unsigned int format, HANDLE data) {
+ if (format == CF_BITMAP)
+ ::DeleteObject(static_cast<HBITMAP>(data));
+ else
+ ::GlobalFree(data);
+}
+
+HWND Clipboard::GetClipboardWindow() const {
+ if (!clipboard_owner_ && create_window_) {
+ clipboard_owner_ = ::CreateWindow(L"ClipboardOwnerWindowClass",
+ L"ClipboardOwnerWindow",
+ 0, 0, 0, 0, 0,
+ HWND_MESSAGE,
+ 0, 0, 0);
+ }
+ return clipboard_owner_;
+}
diff --git a/app/clipboard/scoped_clipboard_writer.cc b/app/clipboard/scoped_clipboard_writer.cc
new file mode 100644
index 0000000..8c51afe
--- /dev/null
+++ b/app/clipboard/scoped_clipboard_writer.cc
@@ -0,0 +1,175 @@
+// Copyright (c) 2009 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.
+
+// This file implements the ScopedClipboardWriter class. Documentation on its
+// purpose can be found in our header. Documentation on the format of the
+// parameters for each clipboard target can be found in clipboard.h.
+
+#include "app/clipboard/scoped_clipboard_writer.h"
+
+#include "base/gfx/size.h"
+#include "base/pickle.h"
+#include "base/string_util.h"
+
+ScopedClipboardWriter::ScopedClipboardWriter(Clipboard* clipboard)
+ : clipboard_(clipboard) {
+}
+
+ScopedClipboardWriter::~ScopedClipboardWriter() {
+ if (!objects_.empty() && clipboard_) {
+ clipboard_->WriteObjects(objects_);
+ if (url_text_.length())
+ clipboard_->DidWriteURL(url_text_);
+ }
+}
+
+void ScopedClipboardWriter::WriteText(const string16& text) {
+ WriteTextOrURL(text, false);
+}
+
+void ScopedClipboardWriter::WriteURL(const string16& text) {
+ WriteTextOrURL(text, true);
+}
+
+void ScopedClipboardWriter::WriteHTML(const string16& markup,
+ const std::string& source_url) {
+ if (markup.empty())
+ return;
+
+ std::string utf8_markup = UTF16ToUTF8(markup);
+
+ Clipboard::ObjectMapParams parameters;
+ parameters.push_back(
+ Clipboard::ObjectMapParam(utf8_markup.begin(),
+ utf8_markup.end()));
+ if (!source_url.empty()) {
+ parameters.push_back(Clipboard::ObjectMapParam(source_url.begin(),
+ source_url.end()));
+ }
+
+ objects_[Clipboard::CBF_HTML] = parameters;
+}
+
+void ScopedClipboardWriter::WriteBookmark(const string16& bookmark_title,
+ const std::string& url) {
+ if (bookmark_title.empty() || url.empty())
+ return;
+
+ std::string utf8_markup = UTF16ToUTF8(bookmark_title);
+
+ Clipboard::ObjectMapParams parameters;
+ parameters.push_back(Clipboard::ObjectMapParam(utf8_markup.begin(),
+ utf8_markup.end()));
+ parameters.push_back(Clipboard::ObjectMapParam(url.begin(), url.end()));
+ objects_[Clipboard::CBF_BOOKMARK] = parameters;
+}
+
+void ScopedClipboardWriter::WriteHyperlink(const std::string& anchor_text,
+ const std::string& url) {
+ if (anchor_text.empty() || url.empty())
+ return;
+
+ // Construct the hyperlink.
+ std::string html("<a href=\"");
+ html.append(url);
+ html.append("\">");
+ html.append(anchor_text);
+ html.append("</a>");
+ WriteHTML(UTF8ToUTF16(html), std::string());
+}
+
+void ScopedClipboardWriter::WriteFile(const FilePath& file) {
+ WriteFiles(std::vector<FilePath>(1, file));
+}
+
+// Save the filenames as a string separated by nulls and terminated with an
+// extra null.
+void ScopedClipboardWriter::WriteFiles(const std::vector<FilePath>& files) {
+ if (files.empty())
+ return;
+
+ Clipboard::ObjectMapParam parameter;
+
+ for (std::vector<FilePath>::const_iterator iter = files.begin();
+ iter != files.end(); ++iter) {
+ FilePath filepath = *iter;
+ FilePath::StringType filename = filepath.value();
+
+ size_t data_length = filename.length() * sizeof(FilePath::CharType);
+ const char* data = reinterpret_cast<const char*>(filename.data());
+ const char* data_end = data + data_length;
+
+ for (const char* ch = data; ch < data_end; ++ch)
+ parameter.push_back(*ch);
+
+ // NUL-terminate the string.
+ for (size_t i = 0; i < sizeof(FilePath::CharType); ++i)
+ parameter.push_back('\0');
+ }
+
+ // NUL-terminate the string list.
+ for (size_t i = 0; i < sizeof(FilePath::CharType); ++i)
+ parameter.push_back('\0');
+
+ Clipboard::ObjectMapParams parameters;
+ parameters.push_back(parameter);
+ objects_[Clipboard::CBF_FILES] = parameters;
+}
+
+void ScopedClipboardWriter::WriteWebSmartPaste() {
+ objects_[Clipboard::CBF_WEBKIT] = Clipboard::ObjectMapParams();
+}
+
+void ScopedClipboardWriter::WriteBitmapFromPixels(const void* pixels,
+ const gfx::Size& size) {
+ Clipboard::ObjectMapParam pixels_parameter, size_parameter;
+ const char* pixels_data = reinterpret_cast<const char*>(pixels);
+ size_t pixels_length = 4 * size.width() * size.height();
+ for (size_t i = 0; i < pixels_length; i++)
+ pixels_parameter.push_back(pixels_data[i]);
+
+ const char* size_data = reinterpret_cast<const char*>(&size);
+ size_t size_length = sizeof(gfx::Size);
+ for (size_t i = 0; i < size_length; i++)
+ size_parameter.push_back(size_data[i]);
+
+ Clipboard::ObjectMapParams parameters;
+ parameters.push_back(pixels_parameter);
+ parameters.push_back(size_parameter);
+ objects_[Clipboard::CBF_BITMAP] = parameters;
+}
+
+void ScopedClipboardWriter::WritePickledData(const Pickle& pickle,
+ Clipboard::FormatType format) {
+ Clipboard::ObjectMapParam format_parameter(format.begin(), format.end());
+ Clipboard::ObjectMapParam data_parameter;
+
+ data_parameter.resize(pickle.size());
+ memcpy(const_cast<char*>(&data_parameter.front()),
+ pickle.data(), pickle.size());
+
+ Clipboard::ObjectMapParams parameters;
+ parameters.push_back(format_parameter);
+ parameters.push_back(data_parameter);
+ objects_[Clipboard::CBF_DATA] = parameters;
+}
+
+void ScopedClipboardWriter::WriteTextOrURL(const string16& text, bool is_url) {
+ if (text.empty())
+ return;
+
+ std::string utf8_text = UTF16ToUTF8(text);
+
+ Clipboard::ObjectMapParams parameters;
+ parameters.push_back(Clipboard::ObjectMapParam(utf8_text.begin(),
+ utf8_text.end()));
+ objects_[Clipboard::CBF_TEXT] = parameters;
+
+ if (is_url) {
+ url_text_ = utf8_text;
+ } else {
+ url_text_.clear();
+ }
+}
+
diff --git a/app/clipboard/scoped_clipboard_writer.h b/app/clipboard/scoped_clipboard_writer.h
new file mode 100644
index 0000000..4876a00
--- /dev/null
+++ b/app/clipboard/scoped_clipboard_writer.h
@@ -0,0 +1,85 @@
+// Copyright (c) 2006-2008 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.
+
+// This file declares the ScopedClipboardWriter class, a wrapper around
+// the Clipboard class which simplifies writing data to the system clipboard.
+// Upon deletion the class atomically writes all data to |clipboard_|,
+// avoiding any potential race condition with other processes that are also
+// writing to the system clipboard.
+
+#ifndef APP_CLIPBOARD_SCOPED_CLIPBOARD_WRITER_H_
+#define APP_CLIPBOARD_SCOPED_CLIPBOARD_WRITER_H_
+
+#include <string>
+#include <vector>
+
+#include "app/clipboard/clipboard.h"
+#include "base/file_path.h"
+#include "base/string16.h"
+
+class Pickle;
+
+// This class is a wrapper for |Clipboard| that handles packing data
+// into a Clipboard::ObjectMap.
+// NB: You should probably NOT be using this class if you include
+// webkit_glue.h. Use ScopedClipboardWriterGlue instead.
+class ScopedClipboardWriter {
+ public:
+ // Create an instance that is a simple wrapper around clipboard.
+ ScopedClipboardWriter(Clipboard* clipboard);
+
+ ~ScopedClipboardWriter();
+
+ // Converts |text| to UTF-8 and adds it to the clipboard.
+ void WriteText(const string16& text);
+
+ // Converts the text of the URL to UTF-8 and adds it to the clipboard, then
+ // notifies the Clipboard that we just wrote a URL.
+ void WriteURL(const string16& text);
+
+ // Adds HTML to the clipboard. The url parameter is optional, but especially
+ // useful if the HTML fragment contains relative links.
+ void WriteHTML(const string16& markup, const std::string& source_url);
+
+ // Adds a bookmark to the clipboard.
+ void WriteBookmark(const string16& bookmark_title,
+ const std::string& url);
+
+ // Adds an html hyperlink (<a href>) to the clipboard. |anchor_text| should
+ // be escaped prior to being passed in.
+ void WriteHyperlink(const std::string& anchor_text, const std::string& url);
+
+ // Adds a file or group of files to the clipboard.
+ void WriteFile(const FilePath& file);
+ void WriteFiles(const std::vector<FilePath>& files);
+
+ // Used by WebKit to determine whether WebKit wrote the clipboard last
+ void WriteWebSmartPaste();
+
+ // Adds a bitmap to the clipboard
+ // Pixel format is assumed to be 32-bit BI_RGB.
+ void WriteBitmapFromPixels(const void* pixels, const gfx::Size& size);
+
+ // Adds arbitrary data to clipboard.
+ void WritePickledData(const Pickle& pickle, Clipboard::FormatType format);
+
+ protected:
+ // Converts |text| to UTF-8 and adds it to the clipboard. If it's a URL, we
+ // also notify the clipboard of that fact.
+ void WriteTextOrURL(const string16& text, bool is_url);
+
+ // We accumulate the data passed to the various targets in the |objects_|
+ // vector, and pass it to Clipboard::WriteObjects() during object destruction.
+ Clipboard::ObjectMap objects_;
+ Clipboard* clipboard_;
+
+ // We keep around the UTF-8 text of the URL in order to pass it to
+ // Clipboard::DidWriteURL().
+ std::string url_text_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedClipboardWriter);
+};
+
+#endif // APP_CLIPBOARD_SCOPED_CLIPBOARD_WRITER_H_