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