summaryrefslogtreecommitdiffstats
path: root/ui/base
diff options
context:
space:
mode:
Diffstat (limited to 'ui/base')
-rw-r--r--ui/base/clipboard/clipboard.cc183
-rw-r--r--ui/base/clipboard/clipboard.h276
-rw-r--r--ui/base/clipboard/clipboard_linux.cc421
-rw-r--r--ui/base/clipboard/clipboard_mac.mm333
-rw-r--r--ui/base/clipboard/clipboard_unittest.cc432
-rw-r--r--ui/base/clipboard/clipboard_util_win.cc544
-rw-r--r--ui/base/clipboard/clipboard_util_win.h70
-rw-r--r--ui/base/clipboard/clipboard_win.cc606
-rw-r--r--ui/base/clipboard/scoped_clipboard_writer.cc140
-rw-r--r--ui/base/clipboard/scoped_clipboard_writer.h85
-rw-r--r--ui/base/ui_base.gypi10
11 files changed, 3100 insertions, 0 deletions
diff --git a/ui/base/clipboard/clipboard.cc b/ui/base/clipboard/clipboard.cc
new file mode 100644
index 0000000..4b9bf603
--- /dev/null
+++ b/ui/base/clipboard/clipboard.cc
@@ -0,0 +1,183 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/clipboard/clipboard.h"
+
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "gfx/size.h"
+
+namespace ui {
+
+namespace {
+
+// A compromised renderer could send us bad data, so validate it.
+// This function only checks that the size parameter makes sense, the caller
+// is responsible for further validating the bitmap buffer against
+// |bitmap_bytes|.
+//
+// |params| - Clipboard bitmap contents to validate.
+// |bitmap_bytes| - On return contains the number of bytes needed to store
+// the bitmap data or -1 if the data is invalid.
+// returns: true if the bitmap size is valid, false otherwise.
+bool IsBitmapSafe(const Clipboard::ObjectMapParams& params,
+ uint32* bitmap_bytes) {
+ *bitmap_bytes = -1;
+ if (params[1].size() != sizeof(gfx::Size))
+ return false;
+ const gfx::Size* size =
+ reinterpret_cast<const gfx::Size*>(&(params[1].front()));
+ uint32 total_size = size->width();
+ // Using INT_MAX not SIZE_T_MAX to put a reasonable bound on things.
+ if (INT_MAX / size->width() <= size->height())
+ return false;
+ total_size *= size->height();
+ if (INT_MAX / total_size <= 4)
+ return false;
+ total_size *= 4;
+ *bitmap_bytes = total_size;
+ return true;
+}
+
+// Validates a plain bitmap on the clipboard.
+// Returns true if the clipboard data makes sense and it's safe to access the
+// bitmap.
+bool ValidatePlainBitmap(const Clipboard::ObjectMapParams& params) {
+ uint32 bitmap_bytes = -1;
+ if (!IsBitmapSafe(params, &bitmap_bytes))
+ return false;
+ if (bitmap_bytes != params[0].size())
+ return false;
+ return true;
+}
+
+// Valides a shared bitmap on the clipboard.
+// Returns true if the clipboard data makes sense and it's safe to access the
+// bitmap.
+bool ValidateAndMapSharedBitmap(const Clipboard::ObjectMapParams& params,
+ base::SharedMemory* bitmap_data) {
+ using base::SharedMemory;
+ uint32 bitmap_bytes = -1;
+ if (!IsBitmapSafe(params, &bitmap_bytes))
+ return false;
+
+ if (!bitmap_data || !SharedMemory::IsHandleValid(bitmap_data->handle()))
+ return false;
+
+ if (!bitmap_data->Map(bitmap_bytes)) {
+ PLOG(ERROR) << "Failed to map bitmap memory";
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+void Clipboard::DispatchObject(ObjectType type, const ObjectMapParams& params) {
+ // All types apart from CBF_WEBKIT need at least 1 non-empty param.
+ if (type != CBF_WEBKIT && (params.empty() || params[0].empty()))
+ return;
+ // Some other types need a non-empty 2nd param.
+ if ((type == CBF_BOOKMARK || type == CBF_BITMAP ||
+ type == CBF_SMBITMAP || type == CBF_DATA) &&
+ (params.size() != 2 || params[1].empty()))
+ return;
+ switch (type) {
+ case CBF_TEXT:
+ WriteText(&(params[0].front()), params[0].size());
+ break;
+
+ case CBF_HTML:
+ if (params.size() == 2) {
+ if (params[1].empty())
+ return;
+ WriteHTML(&(params[0].front()), params[0].size(),
+ &(params[1].front()), params[1].size());
+ } else if (params.size() == 1) {
+ 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_WEBKIT:
+ WriteWebSmartPaste();
+ break;
+
+ case CBF_BITMAP:
+ if (!ValidatePlainBitmap(params))
+ return;
+
+ WriteBitmap(&(params[0].front()), &(params[1].front()));
+ break;
+
+ case CBF_SMBITMAP: {
+ using base::SharedMemory;
+ using base::SharedMemoryHandle;
+
+ if (params[0].size() != sizeof(SharedMemory*))
+ return;
+
+ // It's OK to cast away constness here since we map the handle as
+ // read-only.
+ const char* raw_bitmap_data_const =
+ reinterpret_cast<const char*>(&(params[0].front()));
+ char* raw_bitmap_data = const_cast<char*>(raw_bitmap_data_const);
+ scoped_ptr<SharedMemory> bitmap_data(
+ *reinterpret_cast<SharedMemory**>(raw_bitmap_data));
+
+ if (!ValidateAndMapSharedBitmap(params, bitmap_data.get()))
+ return;
+ WriteBitmap(static_cast<const char*>(bitmap_data->memory()),
+ &(params[1].front()));
+ break;
+ }
+
+#if !defined(OS_MACOSX)
+ case CBF_DATA:
+ WriteData(&(params[0].front()), params[0].size(),
+ &(params[1].front()), params[1].size());
+ break;
+#endif // !defined(OS_MACOSX)
+
+ default:
+ NOTREACHED();
+ }
+}
+
+// static
+void Clipboard::ReplaceSharedMemHandle(ObjectMap* objects,
+ base::SharedMemoryHandle bitmap_handle,
+ base::ProcessHandle process) {
+ using base::SharedMemory;
+ bool has_shared_bitmap = false;
+
+ for (ObjectMap::iterator iter = objects->begin(); iter != objects->end();
+ ++iter) {
+ if (iter->first == CBF_SMBITMAP) {
+ // The code currently only accepts sending a single bitmap over this way.
+ // Fail hard if we ever encounter more than one shared bitmap structure to
+ // fill.
+ CHECK(!has_shared_bitmap);
+
+#if defined(OS_WIN)
+ SharedMemory* bitmap = new SharedMemory(bitmap_handle, true, process);
+#else
+ SharedMemory* bitmap = new SharedMemory(bitmap_handle, true);
+#endif
+
+ // We store the shared memory object pointer so it can be retrieved by the
+ // UI thread (see DispatchObject()).
+ iter->second[0].clear();
+ for (size_t i = 0; i < sizeof(SharedMemory*); ++i)
+ iter->second[0].push_back(reinterpret_cast<char*>(&bitmap)[i]);
+ has_shared_bitmap = true;
+ }
+ }
+}
+
+} // namespace ui
diff --git a/ui/base/clipboard/clipboard.h b/ui/base/clipboard/clipboard.h
new file mode 100644
index 0000000..715e6ea
--- /dev/null
+++ b/ui/base/clipboard/clipboard.h
@@ -0,0 +1,276 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_CLIPBOARD_CLIPBOARD_H_
+#define UI_BASE_CLIPBOARD_CLIPBOARD_H_
+#pragma once
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/process.h"
+#include "base/shared_memory.h"
+#include "base/string16.h"
+
+namespace gfx {
+class Size;
+}
+
+class FilePath;
+
+#if defined(TOOLKIT_USES_GTK)
+typedef struct _GtkClipboard GtkClipboard;
+#endif
+
+namespace ui {
+
+class Clipboard {
+ public:
+ typedef std::string FormatType;
+
+ // 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 A pointer to an unmapped base::SharedMemory
+ // object containing the bitmap data.
+ // 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,
+ BUFFER_SELECTION,
+ BUFFER_DRAG,
+ };
+
+ static bool IsValidBuffer(int32 buffer) {
+ switch (buffer) {
+ case BUFFER_STANDARD:
+ return true;
+#if defined(USE_X11)
+ case BUFFER_SELECTION:
+ return true;
+#endif
+ case BUFFER_DRAG:
+ return true;
+ }
+ 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/BSD, we need to know when the clipboard is set to a URL. Most
+ // platforms don't care.
+#if defined(OS_WIN) || defined(OS_MACOSX)
+ void DidWriteURL(const std::string& utf8_text) {}
+#else // !defined(OS_WIN) && !defined(OS_MACOSX)
+ 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.
+ // TODO(dcheng): Due to platform limitations on Windows, we should make sure
+ // format is never controlled by the user.
+ 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();
+ static FormatType GetBitmapFormatType();
+
+ // Embeds a pointer to a SharedMemory object pointed to by |bitmap_handle|
+ // belonging to |process| into a shared bitmap [CBF_SMBITMAP] slot in
+ // |objects|. The pointer is deleted by DispatchObjects().
+ //
+ // On non-Windows platforms, |process| is ignored.
+ static void ReplaceSharedMemHandle(ObjectMap* objects,
+ base::SharedMemoryHandle bitmap_handle,
+ base::ProcessHandle process);
+#if defined(OS_WIN)
+ // Firefox text/html
+ static FormatType GetTextHtmlFormatType();
+ static FormatType GetCFHDropFormatType();
+ static FormatType GetFileDescriptorFormatType();
+ static FormatType GetFileContentFormatZeroType();
+#endif
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(ClipboardTest, SharedBitmapTest);
+ FRIEND_TEST_ALL_PREFIXES(ClipboardTest, EmptyHTMLTest);
+
+ 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 WriteBitmap(const char* pixel_data, const char* size_data);
+
+#if !defined(OS_MACOSX)
+ // |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 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(OS_MACOSX)
+ // The public API is via WriteObjects() which dispatches to multiple
+ // Write*() calls, but on GTK we must write all the clipboard types
+ // in a single GTK call. To support this we store the current set
+ // of data we intend to put on the clipboard on clipboard_data_ as
+ // WriteObjects is running, and then at the end call SetGtkClipboard
+ // which replaces whatever is on the system clipboard with the
+ // contents of clipboard_data_.
+
+ public:
+ typedef std::map<FormatType, std::pair<char*, size_t> > TargetMap;
+
+ private:
+ // 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);
+};
+
+} // namespace ui
+
+#endif // UI_BASE_CLIPBOARD_CLIPBOARD_H_
diff --git a/ui/base/clipboard/clipboard_linux.cc b/ui/base/clipboard/clipboard_linux.cc
new file mode 100644
index 0000000..da24c4f
--- /dev/null
+++ b/ui/base/clipboard/clipboard_linux.cc
@@ -0,0 +1,421 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/clipboard/clipboard.h"
+
+#include <gtk/gtk.h>
+#include <map>
+#include <set>
+#include <string>
+#include <utility>
+
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "base/utf_string_conversions.h"
+#include "gfx/gtk_util.h"
+#include "gfx/size.h"
+
+namespace ui {
+
+namespace {
+
+const char kMimeBmp[] = "image/bmp";
+const char kMimeHtml[] = "text/html";
+const char kMimeText[] = "text/plain";
+const char kMimeMozillaUrl[] = "text/x-moz-url";
+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 {
+ 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);
+ // The same data may be inserted under multiple keys, so use a set to
+ // uniq them.
+ 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_data_(NULL) {
+ clipboard_ = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
+ primary_selection_ = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
+}
+
+Clipboard::~Clipboard() {
+ gtk_clipboard_store(clipboard_);
+}
+
+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 = const_cast<char*>(iter->first.c_str());
+ targets[i].flags = 0;
+ targets[i].info = 0;
+ }
+
+ if (gtk_clipboard_set_with_data(clipboard_, targets.get(),
+ clipboard_data_->size(),
+ GetData, ClearData,
+ clipboard_data_)) {
+ gtk_clipboard_set_can_store(clipboard_,
+ targets.get(),
+ clipboard_data_->size());
+ }
+
+ // clipboard_data_ now owned by the GtkClipboard.
+ clipboard_data_ = NULL;
+}
+
+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): We need to expand relative links with |url_data|.
+ static const char* html_prefix = "<meta http-equiv=\"content-type\" "
+ "content=\"text/html; charset=utf-8\">";
+ size_t html_prefix_len = strlen(html_prefix);
+ size_t total_len = html_prefix_len + markup_len + 1;
+
+ char* data = new char[total_len];
+ snprintf(data, total_len, "%s", html_prefix);
+ memcpy(data + html_prefix_len, markup_data, markup_len);
+ // Some programs expect NULL-terminated data. See http://crbug.com/42624
+ data[total_len - 1] = '\0';
+
+ InsertMapping(kMimeHtml, data, total_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 =
+ gfx::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 a mozilla url (UTF16: URL, newline, title).
+ string16 url = UTF8ToUTF16(std::string(url_data, url_len) + "\n");
+ string16 title = UTF8ToUTF16(std::string(title_data, title_len));
+ int data_len = 2 * (title.length() + url.length());
+
+ char* data = new char[data_len];
+ memcpy(data, url.data(), 2 * url.length());
+ memcpy(data + 2 * url.length(), title.data(), 2 * title.length());
+ InsertMapping(kMimeMozillaUrl, data, data_len);
+}
+
+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);
+ // We assume that certain mapping types are only written by trusted code.
+ // Therefore we must upkeep their integrity.
+ if (format == kMimeBmp)
+ return;
+ char* data = new char[data_len];
+ memcpy(data, data_data, data_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;
+
+ // If the data starts with 0xFEFF, i.e., Byte Order Mark, assume it is
+ // UTF-16, otherwise assume UTF-8.
+ if (data->length >= 2 &&
+ reinterpret_cast<uint16_t*>(data->data)[0] == 0xFEFF) {
+ markup->assign(reinterpret_cast<uint16_t*>(data->data) + 1,
+ (data->length / 2) - 1);
+ } else {
+ UTF8ToUTF16(reinterpret_cast<char*>(data->data), data->length, markup);
+ }
+
+ // If there is a terminating NULL, drop it.
+ if (!markup->empty() && markup->at(markup->length() - 1) == '\0')
+ markup->resize(markup->length() - 1);
+
+ gtk_selection_data_free(data);
+}
+
+void Clipboard::ReadBookmark(string16* title, std::string* url) const {
+ // TODO(estade): implement this.
+ NOTIMPLEMENTED();
+}
+
+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::GetBitmapFormatType() {
+ return std::string(kMimeBmp);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetWebKitSmartPasteFormatType() {
+ return std::string(kMimeWebkitSmartPaste);
+}
+
+void Clipboard::InsertMapping(const char* key,
+ char* data,
+ size_t data_len) {
+ DCHECK(clipboard_data_->find(key) == clipboard_data_->end());
+ (*clipboard_data_)[key] = std::make_pair(data, data_len);
+}
+
+GtkClipboard* Clipboard::LookupBackingClipboard(Buffer clipboard) const {
+ switch (clipboard) {
+ case BUFFER_STANDARD:
+ return clipboard_;
+ case BUFFER_SELECTION:
+ return primary_selection_;
+ default:
+ NOTREACHED();
+ return NULL;
+ }
+}
+
+} // namespace ui
diff --git a/ui/base/clipboard/clipboard_mac.mm b/ui/base/clipboard/clipboard_mac.mm
new file mode 100644
index 0000000..fb62346
--- /dev/null
+++ b/ui/base/clipboard/clipboard_mac.mm
@@ -0,0 +1,333 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/clipboard/clipboard.h"
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/mac/mac_util.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/scoped_nsobject.h"
+#include "base/sys_string_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "gfx/size.h"
+#import "third_party/mozilla/NSPasteboard+Utils.h"
+
+namespace ui {
+
+namespace {
+
+// Would be nice if this were in UTCoreTypes.h, but it isn't
+NSString* const 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.
+NSString* const 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) {
+ // We need to mark it as utf-8. (see crbug.com/11957)
+ std::string html_fragment_str("<meta charset='utf-8'>");
+ html_fragment_str.append(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::WriteBitmap(const char* pixel_data, const char* size_data) {
+ const gfx::Size* size = reinterpret_cast<const gfx::Size*>(size_data);
+
+ // Safe because the image goes away before the call returns.
+ base::mac::ScopedCFTypeRef<CFDataRef> data(
+ CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
+ reinterpret_cast<const UInt8*>(pixel_data),
+ size->width()*size->height()*4,
+ kCFAllocatorNull));
+
+ base::mac::ScopedCFTypeRef<CGDataProviderRef> data_provider(
+ CGDataProviderCreateWithCFData(data));
+
+ base::mac::ScopedCFTypeRef<CGImageRef> cgimage(
+ CGImageCreate(size->width(),
+ size->height(),
+ 8,
+ 32,
+ size->width()*4,
+ base::mac::GetSRGBColorSpace(), // TODO(avi): do better
+ kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
+ data_provider,
+ NULL,
+ false,
+ kCGRenderingIntentDefault));
+ // Aggressively free storage since image buffers can potentially be very
+ // large.
+ data_provider.reset();
+ data.reset();
+
+ scoped_nsobject<NSBitmapImageRep> bitmap(
+ [[NSBitmapImageRep alloc] initWithCGImage:cgimage]);
+ cgimage.reset();
+
+ scoped_nsobject<NSImage> image([[NSImage alloc] init]);
+ [image addRepresentation:bitmap];
+
+ // An API to ask the NSImage to write itself to the clipboard comes in 10.6 :(
+ // For now, spit out the image as a TIFF.
+ NSPasteboard* pb = GetPasteboard();
+ [pb addTypes:[NSArray arrayWithObject:NSTIFFPboardType] owner:nil];
+ NSData *tiff_data = [image TIFFRepresentation];
+ LOG_IF(ERROR, tiff_data == NULL) << "Failed to allocate image for clipboard";
+ if (tiff_data) {
+ [pb setData:tiff_data forType:NSTIFFPboardType];
+ }
+}
+
+// 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];
+
+ // Safari only places RTF on the pasteboard, never HTML. We can convert RTF
+ // to HTML, so the presence of either indicates success when looking for HTML.
+ if ([format_ns isEqualToString:NSHTMLPboardType]) {
+ return [types containsObject:NSHTMLPboardType] ||
+ [types containsObject:NSRTFPboardType];
+ }
+ 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,
+ NSRTFPboardType,
+ NSStringPboardType,
+ nil];
+ NSString* bestType = [pb availableTypeFromArray:supportedTypes];
+ NSString* contents = [pb stringForType:bestType];
+ if ([bestType isEqualToString:NSRTFPboardType])
+ contents = [pb htmlFromRtf];
+ 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::GetBitmapFormatType() {
+ static const std::string type = base::SysNSStringToUTF8(NSTIFFPboardType);
+ return type;
+}
+
+// static
+Clipboard::FormatType Clipboard::GetWebKitSmartPasteFormatType() {
+ static const std::string type =
+ base::SysNSStringToUTF8(kWebSmartPastePboardType);
+ return type;
+}
+
+} // namespace ui
diff --git a/ui/base/clipboard/clipboard_unittest.cc b/ui/base/clipboard/clipboard_unittest.cc
new file mode 100644
index 0000000..866db6c
--- /dev/null
+++ b/ui/base/clipboard/clipboard_unittest.cc
@@ -0,0 +1,432 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "build/build_config.h"
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "gfx/size.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+#include "ui/base/clipboard/clipboard.h"
+#include "ui/base/clipboard/scoped_clipboard_writer.h"
+
+#if defined(OS_WIN)
+#include "base/message_loop.h"
+#include "ui/base/clipboard/clipboard_util_win.h"
+#endif
+
+#if defined(OS_WIN) || (defined(OS_POSIX) && !defined(OS_MACOSX))
+#include "base/pickle.h"
+#endif
+
+namespace ui {
+
+#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)
+
+namespace {
+
+bool ClipboardContentsIsExpected(const string16& copied_markup,
+ const string16& pasted_markup) {
+#if defined(OS_POSIX)
+ return pasted_markup.find(copied_markup) != string16::npos;
+#else
+ return copied_markup == pasted_markup;
+#endif
+}
+
+} // namespace
+
+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_TRUE(ClipboardContentsIsExpected(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_TRUE(ClipboardContentsIsExpected(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)
+}
+
+#if defined(OS_LINUX)
+// Regression test for crbug.com/56298 (pasting empty HTML crashes Linux).
+TEST_F(ClipboardTest, EmptyHTMLTest) {
+ Clipboard clipboard;
+ // ScopedClipboardWriter doesn't let us write empty data to the clipboard.
+ clipboard.clipboard_data_ = new Clipboard::TargetMap();
+ // The 1 is so the compiler doesn't warn about allocating an empty array.
+ char* empty = new char[1];
+ clipboard.InsertMapping("text/html", empty, 0U);
+ clipboard.SetGtkClipboard();
+
+ EXPECT_TRUE(clipboard.IsFormatAvailable(Clipboard::GetHtmlFormatType(),
+ Clipboard::BUFFER_STANDARD));
+ string16 markup_result;
+ std::string url_result;
+ clipboard.ReadHTML(Clipboard::BUFFER_STANDARD, &markup_result, &url_result);
+ EXPECT_TRUE(ClipboardContentsIsExpected(string16(), markup_result));
+}
+#endif
+
+// TODO(estade): Port the following test (decide what target we use for urls)
+#if !defined(OS_POSIX) || defined(OS_MACOSX)
+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_TRUE(ClipboardContentsIsExpected(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);
+}
+
+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)
+}
+
+TEST_F(ClipboardTest, SharedBitmapTest) {
+ unsigned int fake_bitmap[] = {
+ 0x46155189, 0xF6A55C8D, 0x79845674, 0xFA57BD89,
+ 0x78FD46AE, 0x87C64F5A, 0x36EDC5AF, 0x4378F568,
+ 0x91E9F63A, 0xC31EA14F, 0x69AB32DF, 0x643A3FD1,
+ };
+ gfx::Size fake_bitmap_size(3, 4);
+ uint32 bytes = sizeof(fake_bitmap);
+
+ // Create shared memory region.
+ base::SharedMemory shared_buf;
+ ASSERT_TRUE(shared_buf.CreateAndMapAnonymous(bytes));
+ memcpy(shared_buf.memory(), fake_bitmap, bytes);
+ base::SharedMemoryHandle handle_to_share;
+ base::ProcessHandle current_process = base::kNullProcessHandle;
+#if defined(OS_WIN)
+ current_process = GetCurrentProcess();
+#endif
+ shared_buf.ShareToProcess(current_process, &handle_to_share);
+ ASSERT_TRUE(shared_buf.Unmap());
+
+ // Setup data for clipboard.
+ Clipboard::ObjectMapParam placeholder_param;
+ Clipboard::ObjectMapParam size_param;
+ const char* size_data = reinterpret_cast<const char*>(&fake_bitmap_size);
+ for (size_t i = 0; i < sizeof(fake_bitmap_size); ++i)
+ size_param.push_back(size_data[i]);
+
+ Clipboard::ObjectMapParams params;
+ params.push_back(placeholder_param);
+ params.push_back(size_param);
+
+ Clipboard::ObjectMap objects;
+ objects[Clipboard::CBF_SMBITMAP] = params;
+ Clipboard::ReplaceSharedMemHandle(&objects, handle_to_share, current_process);
+
+ Clipboard clipboard;
+ clipboard.WriteObjects(objects);
+
+ EXPECT_TRUE(clipboard.IsFormatAvailable(Clipboard::GetBitmapFormatType(),
+ Clipboard::BUFFER_STANDARD));
+}
+
+#if defined(OS_WIN) || (defined(OS_POSIX) && !defined(OS_MACOSX))
+TEST_F(ClipboardTest, DataTest) {
+ Clipboard clipboard;
+ const char* kFormat = "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, kFormat);
+ }
+
+ ASSERT_TRUE(clipboard.IsFormatAvailableByString(
+ kFormat, Clipboard::BUFFER_STANDARD));
+ std::string output;
+ clipboard.ReadData(kFormat, &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;
+
+ const std::string kTitle("The Example Company");
+ const std::string kUrl("http://www.example.com/");
+ const std::string kExpectedHtml("<a href=\"http://www.example.com/\">"
+ "The Example Company</a>");
+ std::string url_result;
+ string16 html_result;
+
+ {
+ ScopedClipboardWriter clipboard_writer(&clipboard);
+ clipboard_writer.WriteHyperlink(ASCIIToUTF16(kTitle), kUrl);
+ }
+
+ EXPECT_TRUE(clipboard.IsFormatAvailable(Clipboard::GetHtmlFormatType(),
+ Clipboard::BUFFER_STANDARD));
+ clipboard.ReadHTML(Clipboard::BUFFER_STANDARD, &html_result, &url_result);
+ EXPECT_EQ(ASCIIToUTF16(kExpectedHtml), 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));
+}
+
+void HtmlTestHelper(const std::string& cf_html,
+ const std::string& expected_html) {
+ std::string html;
+ ClipboardUtil::CFHtmlToHtml(cf_html, &html, NULL);
+ EXPECT_EQ(html, expected_html);
+}
+
+TEST_F(ClipboardTest, HtmlTest) {
+ // Test converting from CF_HTML format data with <!--StartFragment--> and
+ // <!--EndFragment--> comments, like from MS Word.
+ HtmlTestHelper("Version:1.0\r\n"
+ "StartHTML:0000000105\r\n"
+ "EndHTML:0000000199\r\n"
+ "StartFragment:0000000123\r\n"
+ "EndFragment:0000000161\r\n"
+ "\r\n"
+ "<html>\r\n"
+ "<body>\r\n"
+ "<!--StartFragment-->\r\n"
+ "\r\n"
+ "<p>Foo</p>\r\n"
+ "\r\n"
+ "<!--EndFragment-->\r\n"
+ "</body>\r\n"
+ "</html>\r\n\r\n",
+ "<p>Foo</p>");
+
+ // Test converting from CF_HTML format data without <!--StartFragment--> and
+ // <!--EndFragment--> comments, like from OpenOffice Writer.
+ HtmlTestHelper("Version:1.0\r\n"
+ "StartHTML:0000000105\r\n"
+ "EndHTML:0000000151\r\n"
+ "StartFragment:0000000121\r\n"
+ "EndFragment:0000000131\r\n"
+ "<html>\r\n"
+ "<body>\r\n"
+ "<p>Foo</p>\r\n"
+ "</body>\r\n"
+ "</html>\r\n\r\n",
+ "<p>Foo</p>");
+}
+#endif // defined(OS_WIN)
+
+// Test writing all formats we have simultaneously.
+TEST_F(ClipboardTest, WriteEverything) {
+ Clipboard clipboard;
+
+ {
+ ScopedClipboardWriter writer(&clipboard);
+ writer.WriteText(UTF8ToUTF16("foo"));
+ writer.WriteURL(UTF8ToUTF16("foo"));
+ writer.WriteHTML(UTF8ToUTF16("foo"), "bar");
+ writer.WriteBookmark(UTF8ToUTF16("foo"), "bar");
+ writer.WriteHyperlink(ASCIIToUTF16("foo"), "bar");
+ writer.WriteWebSmartPaste();
+ // Left out: WriteFile, WriteFiles, WriteBitmapFromPixels, WritePickledData.
+ }
+
+ // Passes if we don't crash.
+}
+
+} // namespace ui
diff --git a/ui/base/clipboard/clipboard_util_win.cc b/ui/base/clipboard/clipboard_util_win.cc
new file mode 100644
index 0000000..c6dd6a3
--- /dev/null
+++ b/ui/base/clipboard/clipboard_util_win.cc
@@ -0,0 +1,544 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/clipboard/clipboard_util_win.h"
+
+#include <shellapi.h>
+#include <shlwapi.h>
+#include <wininet.h> // For INTERNET_MAX_URL_LENGTH.
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/scoped_handle.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "base/win/scoped_hglobal.h"
+
+namespace ui {
+
+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->assign(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;
+}
+
+void SplitUrlAndTitle(const std::wstring& str,
+ std::wstring* url,
+ std::wstring* title) {
+ DCHECK(url && title);
+ size_t newline_pos = str.find('\n');
+ if (newline_pos != std::wstring::npos) {
+ url->assign(str, 0, newline_pos);
+ title->assign(str, newline_pos + 1, std::wstring::npos);
+ } else {
+ url->assign(str);
+ title->assign(str);
+ }
+}
+
+bool GetFileUrl(IDataObject* data_object, std::wstring* url,
+ std::wstring* title) {
+ STGMEDIUM store;
+ if (SUCCEEDED(data_object->GetData(ClipboardUtil::GetFilenameWFormat(),
+ &store))) {
+ bool success = false;
+ {
+ // filename using unicode
+ base::win::ScopedHGlobal<wchar_t> data(store.hGlobal);
+ if (data.get() && data.get()[0] &&
+ (PathFileExists(data.get()) || PathIsUNC(data.get()))) {
+ wchar_t file_url[INTERNET_MAX_URL_LENGTH];
+ DWORD file_url_len = arraysize(file_url);
+ if (SUCCEEDED(::UrlCreateFromPathW(data.get(), file_url, &file_url_len,
+ 0))) {
+ url->assign(file_url);
+ title->assign(file_url);
+ success = true;
+ }
+ }
+ }
+ ReleaseStgMedium(&store);
+ if (success)
+ return true;
+ }
+
+ if (SUCCEEDED(data_object->GetData(ClipboardUtil::GetFilenameFormat(),
+ &store))) {
+ bool success = false;
+ {
+ // filename using ascii
+ base::win::ScopedHGlobal<char> data(store.hGlobal);
+ if (data.get() && data.get()[0] && (PathFileExistsA(data.get()) ||
+ PathIsUNCA(data.get()))) {
+ char file_url[INTERNET_MAX_URL_LENGTH];
+ DWORD file_url_len = arraysize(file_url);
+ if (SUCCEEDED(::UrlCreateFromPathA(data.get(), file_url, &file_url_len,
+ 0))) {
+ url->assign(UTF8ToWide(file_url));
+ title->assign(*url);
+ success = true;
+ }
+ }
+ }
+ ReleaseStgMedium(&store);
+ if (success)
+ return true;
+ }
+ return false;
+}
+
+} // 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, -1, 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, bool convert_filenames) {
+ 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
+ base::win::ScopedHGlobal<wchar_t> data(store.hGlobal);
+ SplitUrlAndTitle(data.get(), url, title);
+ }
+ ReleaseStgMedium(&store);
+ return true;
+ }
+
+ if (SUCCEEDED(data_object->GetData(GetUrlFormat(), &store))) {
+ {
+ // URL using ascii
+ base::win::ScopedHGlobal<char> data(store.hGlobal);
+ SplitUrlAndTitle(UTF8ToWide(data.get()), url, title);
+ }
+ ReleaseStgMedium(&store);
+ return true;
+ }
+
+ if (convert_filenames) {
+ return GetFileUrl(data_object, url, title);
+ } else {
+ 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;
+ if (SUCCEEDED(data_object->GetData(GetPlainTextWFormat(), &store))) {
+ {
+ // Unicode text
+ base::win::ScopedHGlobal<wchar_t> data(store.hGlobal);
+ plain_text->assign(data.get());
+ }
+ ReleaseStgMedium(&store);
+ return true;
+ }
+
+ if (SUCCEEDED(data_object->GetData(GetPlainTextFormat(), &store))) {
+ {
+ // ascii text
+ base::win::ScopedHGlobal<char> data(store.hGlobal);
+ plain_text->assign(UTF8ToWide(data.get()));
+ }
+ ReleaseStgMedium(&store);
+ return true;
+ }
+
+ // 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;
+ return GetUrl(data_object, plain_text, &title, false);
+}
+
+bool ClipboardUtil::GetHtml(IDataObject* data_object,
+ std::wstring* html, std::string* base_url) {
+ DCHECK(data_object && html && base_url);
+
+ STGMEDIUM store;
+ if (SUCCEEDED(data_object->QueryGetData(GetHtmlFormat())) &&
+ SUCCEEDED(data_object->GetData(GetHtmlFormat(), &store))) {
+ {
+ // MS CF html
+ base::win::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;
+
+ if (FAILED(data_object->GetData(GetTextHtmlFormat(), &store)))
+ return false;
+
+ {
+ // text/html
+ base::win::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);
+ if (!SUCCEEDED(data_object->QueryGetData(GetFileContentFormatZero())) &&
+ !SUCCEEDED(data_object->QueryGetData(GetFileDescriptorFormat())))
+ 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) {
+ base::win::ScopedHGlobal<char> data(content.hGlobal);
+ file_contents->assign(data.get(), data.Size());
+ }
+ ReleaseStgMedium(&content);
+ }
+
+ STGMEDIUM description;
+ if (SUCCEEDED(data_object->GetData(GetFileDescriptorFormat(),
+ &description))) {
+ {
+ base::win::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 the comments cannot be found, like copying from OpenOffice Writer,
+ // we simply fall back to using StartFragment/EndFragment bytecount values
+ // to get the markup.
+ if (html) {
+ size_t fragment_start = std::string::npos;
+ size_t fragment_end = std::string::npos;
+
+ 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);
+ if (tag_start == std::string::npos) {
+ static std::string start_fragment_str("StartFragment:");
+ size_t start_fragment_start = cf_html.find(start_fragment_str);
+ if (start_fragment_start != std::string::npos) {
+ fragment_start = static_cast<size_t>(atoi(cf_html.c_str() +
+ start_fragment_start + start_fragment_str.length()));
+ }
+
+ static std::string end_fragment_str("EndFragment:");
+ size_t end_fragment_start = cf_html.find(end_fragment_str);
+ if (end_fragment_start != std::string::npos) {
+ fragment_end = static_cast<size_t>(atoi(cf_html.c_str() +
+ end_fragment_start + end_fragment_str.length()));
+ }
+ } else {
+ fragment_start = cf_html.find('>', tag_start) + 1;
+ size_t tag_end = cf_html.rfind("<!--EndFragment", std::string::npos);
+ 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);
+ }
+ }
+}
+
+} // namespace ui
diff --git a/ui/base/clipboard/clipboard_util_win.h b/ui/base/clipboard/clipboard_util_win.h
new file mode 100644
index 0000000..6c322ee
--- /dev/null
+++ b/ui/base/clipboard/clipboard_util_win.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Some helper functions for working with the clipboard and IDataObjects.
+
+#ifndef UI_BASE_CLIPBOARD_CLIPBOARD_UTIL_WIN_H_
+#define UI_BASE_CLIPBOARD_CLIPBOARD_UTIL_WIN_H_
+#pragma once
+
+#include <shlobj.h>
+#include <string>
+#include <vector>
+
+namespace ui {
+
+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, bool convert_filenames);
+ 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 // UI_BASE_CLIPBOARD_CLIPBOARD_UTIL_WIN_H_
diff --git a/ui/base/clipboard/clipboard_win.cc b/ui/base/clipboard/clipboard_win.cc
new file mode 100644
index 0000000..7c21dcf
--- /dev/null
+++ b/ui/base/clipboard/clipboard_win.cc
@@ -0,0 +1,606 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Many of these functions are based on those found in
+// webkit/port/platform/PasteboardWin.cpp
+
+#include "ui/base/clipboard/clipboard.h"
+
+#include <shlobj.h>
+#include <shellapi.h>
+
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/shared_memory.h"
+#include "base/string_util.h"
+#include "base/string_number_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "gfx/size.h"
+#include "ui/base/clipboard/clipboard_util_win.h"
+
+namespace ui {
+
+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) {
+ 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(ClipboardUtil::GetHtmlFormat()->cfFormat, 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(ClipboardUtil::GetUrlWFormat()->cfFormat, glob);
+}
+
+void Clipboard::WriteWebSmartPaste() {
+ DCHECK(clipboard_owner_);
+ ::SetClipboardData(ClipboardUtil::GetWebKitSmartPasteFormat()->cfFormat,
+ 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::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);
+}
+
+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);
+ int f;
+ if (!base::StringToInt(format, &f))
+ return false;
+ return ::IsClipboardFormatAvailable(f) != 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(ClipboardUtil::GetHtmlFormat()->cfFormat);
+ 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(ClipboardUtil::GetUrlWFormat()->cfFormat);
+ 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 base::IntToString(ClipboardUtil::GetUrlFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetUrlWFormatType() {
+ return base::IntToString(ClipboardUtil::GetUrlWFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetMozUrlFormatType() {
+ return base::IntToString(ClipboardUtil::GetMozUrlFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetPlainTextFormatType() {
+ return base::IntToString(ClipboardUtil::GetPlainTextFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetPlainTextWFormatType() {
+ return base::IntToString(ClipboardUtil::GetPlainTextWFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetFilenameFormatType() {
+ return base::IntToString(ClipboardUtil::GetFilenameFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetFilenameWFormatType() {
+ return base::IntToString(ClipboardUtil::GetFilenameWFormat()->cfFormat);
+}
+
+// MS HTML Format
+// static
+Clipboard::FormatType Clipboard::GetHtmlFormatType() {
+ return base::IntToString(ClipboardUtil::GetHtmlFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetBitmapFormatType() {
+ return base::IntToString(CF_BITMAP);
+}
+
+// Firefox text/html
+// static
+Clipboard::FormatType Clipboard::GetTextHtmlFormatType() {
+ return base::IntToString(ClipboardUtil::GetTextHtmlFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetCFHDropFormatType() {
+ return base::IntToString(ClipboardUtil::GetCFHDropFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetFileDescriptorFormatType() {
+ return base::IntToString(ClipboardUtil::GetFileDescriptorFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetFileContentFormatZeroType() {
+ return base::IntToString(ClipboardUtil::GetFileContentFormatZero()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetWebKitSmartPasteFormatType() {
+ return base::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_;
+}
+
+} // namespace ui
diff --git a/ui/base/clipboard/scoped_clipboard_writer.cc b/ui/base/clipboard/scoped_clipboard_writer.cc
new file mode 100644
index 0000000..43ffaad
--- /dev/null
+++ b/ui/base/clipboard/scoped_clipboard_writer.cc
@@ -0,0 +1,140 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// 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 "ui/base/clipboard/scoped_clipboard_writer.h"
+
+#include "base/pickle.h"
+#include "base/utf_string_conversions.h"
+#include "gfx/size.h"
+
+namespace ui {
+
+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 string16& 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(UTF16ToUTF8(anchor_text));
+ html.append("</a>");
+ WriteHTML(UTF8ToUTF16(html), std::string());
+}
+
+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();
+ }
+}
+
+} // namespace ui
diff --git a/ui/base/clipboard/scoped_clipboard_writer.h b/ui/base/clipboard/scoped_clipboard_writer.h
new file mode 100644
index 0000000..4bd2da7
--- /dev/null
+++ b/ui/base/clipboard/scoped_clipboard_writer.h
@@ -0,0 +1,85 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// 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 UI_BASE_CLIPBOARD_SCOPED_CLIPBOARD_WRITER_H_
+#define UI_BASE_CLIPBOARD_SCOPED_CLIPBOARD_WRITER_H_
+#pragma once
+
+#include <string>
+
+#include "base/string16.h"
+#include "ui/base/clipboard/clipboard.h"
+
+class Pickle;
+
+namespace ui {
+
+// 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.
+ explicit 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 string16& anchor_text, const std::string& url);
+
+ // 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);
+};
+
+} // namespace ui
+
+#endif // UI_BASE_CLIPBOARD_SCOPED_CLIPBOARD_WRITER_H_
+
diff --git a/ui/base/ui_base.gypi b/ui/base/ui_base.gypi
index f441c6f..4844945 100644
--- a/ui/base/ui_base.gypi
+++ b/ui/base/ui_base.gypi
@@ -69,6 +69,15 @@
'animation/throb_animation.h',
'animation/tween.cc',
'animation/tween.h',
+ 'clipboard/clipboard.cc',
+ 'clipboard/clipboard.h',
+ 'clipboard/clipboard_linux.cc',
+ 'clipboard/clipboard_mac.mm',
+ 'clipboard/clipboard_util_win.cc',
+ 'clipboard/clipboard_util_win.h',
+ 'clipboard/clipboard_win.cc',
+ 'clipboard/scoped_clipboard_writer.cc',
+ 'clipboard/scoped_clipboard_writer.h',
],
},
{
@@ -86,6 +95,7 @@
'animation/animation_unittest.cc',
'animation/multi_animation_unittest.cc',
'animation/slide_animation_unittest.cc',
+ 'clipboard/clipboard_unittest.cc',
'run_all_unittests.cc',
'test_suite.h',
],