Add ICU message format support
[chromium-blink-merge.git] / ui / base / clipboard / clipboard_win.cc
blobe818f2240d9e12a661e9567bcc8fc2b589976267
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // Many of these functions are based on those found in
6 // webkit/port/platform/PasteboardWin.cpp
8 #include "ui/base/clipboard/clipboard_win.h"
10 #include <shellapi.h>
11 #include <shlobj.h>
13 #include "base/basictypes.h"
14 #include "base/bind.h"
15 #include "base/files/file_path.h"
16 #include "base/lazy_instance.h"
17 #include "base/logging.h"
18 #include "base/memory/shared_memory.h"
19 #include "base/message_loop/message_loop.h"
20 #include "base/numerics/safe_conversions.h"
21 #include "base/stl_util.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/string_util.h"
24 #include "base/strings/utf_offset_string_conversions.h"
25 #include "base/strings/utf_string_conversions.h"
26 #include "base/win/message_window.h"
27 #include "base/win/scoped_gdi_object.h"
28 #include "base/win/scoped_hdc.h"
29 #include "third_party/skia/include/core/SkBitmap.h"
30 #include "ui/base/clipboard/clipboard_util_win.h"
31 #include "ui/base/clipboard/custom_data_helper.h"
32 #include "ui/gfx/canvas.h"
33 #include "ui/gfx/geometry/size.h"
35 namespace ui {
37 namespace {
39 // A scoper to impersonate the anonymous token and revert when leaving scope
40 class AnonymousImpersonator {
41 public:
42 AnonymousImpersonator() {
43 must_revert_ = ::ImpersonateAnonymousToken(::GetCurrentThread());
46 ~AnonymousImpersonator() {
47 if (must_revert_)
48 ::RevertToSelf();
51 private:
52 BOOL must_revert_;
53 DISALLOW_COPY_AND_ASSIGN(AnonymousImpersonator);
56 // A scoper to manage acquiring and automatically releasing the clipboard.
57 class ScopedClipboard {
58 public:
59 ScopedClipboard() : opened_(false) { }
61 ~ScopedClipboard() {
62 if (opened_)
63 Release();
66 bool Acquire(HWND owner) {
67 const int kMaxAttemptsToOpenClipboard = 5;
69 if (opened_) {
70 NOTREACHED();
71 return false;
74 // Attempt to open the clipboard, which will acquire the Windows clipboard
75 // lock. This may fail if another process currently holds this lock.
76 // We're willing to try a few times in the hopes of acquiring it.
78 // This turns out to be an issue when using remote desktop because the
79 // rdpclip.exe process likes to read what we've written to the clipboard and
80 // send it to the RDP client. If we open and close the clipboard in quick
81 // succession, we might be trying to open it while rdpclip.exe has it open,
82 // See Bug 815425.
84 // In fact, we believe we'll only spin this loop over remote desktop. In
85 // normal situations, the user is initiating clipboard operations and there
86 // shouldn't be contention.
88 for (int attempts = 0; attempts < kMaxAttemptsToOpenClipboard; ++attempts) {
89 // If we didn't manage to open the clipboard, sleep a bit and be hopeful.
90 if (attempts != 0)
91 ::Sleep(5);
93 if (::OpenClipboard(owner)) {
94 opened_ = true;
95 return true;
99 // We failed to acquire the clipboard.
100 return false;
103 void Release() {
104 if (opened_) {
105 // Impersonate the anonymous token during the call to CloseClipboard
106 // This prevents Windows 8+ capturing the broker's access token which
107 // could be accessed by lower-privileges chrome processes leading to
108 // a risk of EoP
109 AnonymousImpersonator impersonator;
110 ::CloseClipboard();
111 opened_ = false;
112 } else {
113 NOTREACHED();
117 private:
118 bool opened_;
121 bool ClipboardOwnerWndProc(UINT message,
122 WPARAM wparam,
123 LPARAM lparam,
124 LRESULT* result) {
125 switch (message) {
126 case WM_RENDERFORMAT:
127 // This message comes when SetClipboardData was sent a null data handle
128 // and now it's come time to put the data on the clipboard.
129 // We always set data, so there isn't a need to actually do anything here.
130 break;
131 case WM_RENDERALLFORMATS:
132 // This message comes when SetClipboardData was sent a null data handle
133 // and now this application is about to quit, so it must put data on
134 // the clipboard before it exits.
135 // We always set data, so there isn't a need to actually do anything here.
136 break;
137 case WM_DRAWCLIPBOARD:
138 break;
139 case WM_DESTROY:
140 break;
141 case WM_CHANGECBCHAIN:
142 break;
143 default:
144 return false;
147 *result = 0;
148 return true;
151 template <typename charT>
152 HGLOBAL CreateGlobalData(const std::basic_string<charT>& str) {
153 HGLOBAL data =
154 ::GlobalAlloc(GMEM_MOVEABLE, ((str.size() + 1) * sizeof(charT)));
155 if (data) {
156 charT* raw_data = static_cast<charT*>(::GlobalLock(data));
157 memcpy(raw_data, str.data(), str.size() * sizeof(charT));
158 raw_data[str.size()] = '\0';
159 ::GlobalUnlock(data);
161 return data;
164 bool BitmapHasInvalidPremultipliedColors(const SkBitmap& bitmap) {
165 for (int x = 0; x < bitmap.width(); ++x) {
166 for (int y = 0; y < bitmap.height(); ++y) {
167 uint32_t pixel = *bitmap.getAddr32(x, y);
168 if (SkColorGetR(pixel) > SkColorGetA(pixel) ||
169 SkColorGetG(pixel) > SkColorGetA(pixel) ||
170 SkColorGetB(pixel) > SkColorGetA(pixel))
171 return true;
174 return false;
177 void MakeBitmapOpaque(const SkBitmap& bitmap) {
178 for (int x = 0; x < bitmap.width(); ++x) {
179 for (int y = 0; y < bitmap.height(); ++y) {
180 *bitmap.getAddr32(x, y) = SkColorSetA(*bitmap.getAddr32(x, y), 0xFF);
185 void ParseBookmarkClipboardFormat(const base::string16& bookmark,
186 base::string16* title,
187 std::string* url) {
188 const base::string16 kDelim = base::ASCIIToUTF16("\r\n");
190 const size_t title_end = bookmark.find_first_of(kDelim);
191 if (title)
192 title->assign(bookmark.substr(0, title_end));
194 if (url) {
195 const size_t url_start = bookmark.find_first_not_of(kDelim, title_end);
196 if (url_start != base::string16::npos) {
197 *url =
198 base::UTF16ToUTF8(bookmark.substr(url_start, base::string16::npos));
203 void FreeData(unsigned int format, HANDLE data) {
204 if (format == CF_BITMAP)
205 ::DeleteObject(static_cast<HBITMAP>(data));
206 else
207 ::GlobalFree(data);
210 } // namespace
212 // Clipboard::FormatType implementation.
213 Clipboard::FormatType::FormatType() : data_() {}
215 Clipboard::FormatType::FormatType(UINT native_format) : data_() {
216 // There's no good way to actually initialize this in the constructor in
217 // C++03.
218 data_.cfFormat = static_cast<CLIPFORMAT>(native_format);
219 data_.dwAspect = DVASPECT_CONTENT;
220 data_.lindex = -1;
221 data_.tymed = TYMED_HGLOBAL;
224 Clipboard::FormatType::FormatType(UINT native_format, LONG index) : data_() {
225 // There's no good way to actually initialize this in the constructor in
226 // C++03.
227 data_.cfFormat = static_cast<CLIPFORMAT>(native_format);
228 data_.dwAspect = DVASPECT_CONTENT;
229 data_.lindex = index;
230 data_.tymed = TYMED_HGLOBAL;
233 Clipboard::FormatType::~FormatType() {
236 std::string Clipboard::FormatType::Serialize() const {
237 return base::IntToString(data_.cfFormat);
240 // static
241 Clipboard::FormatType Clipboard::FormatType::Deserialize(
242 const std::string& serialization) {
243 int clipboard_format = -1;
244 if (!base::StringToInt(serialization, &clipboard_format)) {
245 NOTREACHED();
246 return FormatType();
248 return FormatType(clipboard_format);
251 bool Clipboard::FormatType::operator<(const FormatType& other) const {
252 return data_.cfFormat < other.data_.cfFormat;
255 bool Clipboard::FormatType::Equals(const FormatType& other) const {
256 return data_.cfFormat == other.data_.cfFormat;
259 // Various predefined FormatTypes.
260 // static
261 Clipboard::FormatType Clipboard::GetFormatType(
262 const std::string& format_string) {
263 return FormatType(
264 ::RegisterClipboardFormat(base::ASCIIToUTF16(format_string).c_str()));
267 // The following formats can be referenced by ClipboardUtilWin::GetPlainText.
268 // For reasons (COM), they must be initialized in a thread-safe manner.
269 // TODO(dcheng): We probably need to make static initialization of "known"
270 // FormatTypes thread-safe on all platforms.
271 #define CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(name, ...) \
272 struct FormatTypeArgumentForwarder : public FormatType { \
273 FormatTypeArgumentForwarder() : FormatType(__VA_ARGS__) { } \
274 }; \
275 static base::LazyInstance<FormatTypeArgumentForwarder>::Leaky name = \
276 LAZY_INSTANCE_INITIALIZER
277 // static
278 const Clipboard::FormatType& Clipboard::GetUrlFormatType() {
279 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
280 type, ::RegisterClipboardFormat(CFSTR_INETURLA));
281 return type.Get();
284 // static
285 const Clipboard::FormatType& Clipboard::GetUrlWFormatType() {
286 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
287 type, ::RegisterClipboardFormat(CFSTR_INETURLW));
288 return type.Get();
291 // static
292 const Clipboard::FormatType& Clipboard::GetMozUrlFormatType() {
293 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
294 type, ::RegisterClipboardFormat(L"text/x-moz-url"));
295 return type.Get();
298 // static
299 const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() {
300 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(type, CF_TEXT);
301 return type.Get();
304 // static
305 const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() {
306 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(type, CF_UNICODETEXT);
307 return type.Get();
310 // static
311 const Clipboard::FormatType& Clipboard::GetFilenameFormatType() {
312 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
313 type, ::RegisterClipboardFormat(CFSTR_FILENAMEA));
314 return type.Get();
317 // static
318 const Clipboard::FormatType& Clipboard::GetFilenameWFormatType() {
319 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
320 type, ::RegisterClipboardFormat(CFSTR_FILENAMEW));
321 return type.Get();
324 // MS HTML Format
325 // static
326 const Clipboard::FormatType& Clipboard::GetHtmlFormatType() {
327 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
328 type, ::RegisterClipboardFormat(L"HTML Format"));
329 return type.Get();
332 // MS RTF Format
333 // static
334 const Clipboard::FormatType& Clipboard::GetRtfFormatType() {
335 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
336 type, ::RegisterClipboardFormat(L"Rich Text Format"));
337 return type.Get();
340 // static
341 const Clipboard::FormatType& Clipboard::GetBitmapFormatType() {
342 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(type, CF_BITMAP);
343 return type.Get();
346 // Firefox text/html
347 // static
348 const Clipboard::FormatType& Clipboard::GetTextHtmlFormatType() {
349 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
350 type, ::RegisterClipboardFormat(L"text/html"));
351 return type.Get();
354 // static
355 const Clipboard::FormatType& Clipboard::GetCFHDropFormatType() {
356 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(type, CF_HDROP);
357 return type.Get();
360 // static
361 const Clipboard::FormatType& Clipboard::GetFileDescriptorFormatType() {
362 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
363 type, ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR));
364 return type.Get();
367 // static
368 const Clipboard::FormatType& Clipboard::GetFileContentZeroFormatType() {
369 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
370 type, ::RegisterClipboardFormat(CFSTR_FILECONTENTS), 0);
371 return type.Get();
374 // static
375 const Clipboard::FormatType& Clipboard::GetIDListFormatType() {
376 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
377 type, ::RegisterClipboardFormat(CFSTR_SHELLIDLIST));
378 return type.Get();
381 // static
382 const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() {
383 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
384 type,
385 ::RegisterClipboardFormat(L"WebKit Smart Paste Format"));
386 return type.Get();
389 // static
390 const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() {
391 // TODO(dcheng): This name is temporary. See http://crbug.com/106449.
392 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
393 type,
394 ::RegisterClipboardFormat(L"Chromium Web Custom MIME Data Format"));
395 return type.Get();
398 // static
399 const Clipboard::FormatType& Clipboard::GetPepperCustomDataFormatType() {
400 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
401 type,
402 ::RegisterClipboardFormat(L"Chromium Pepper MIME Data Format"));
403 return type.Get();
405 #undef CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE
407 // Clipboard factory method.
408 // static
409 Clipboard* Clipboard::Create() {
410 return new ClipboardWin;
413 // ClipboardWin implementation.
414 ClipboardWin::ClipboardWin() {
415 if (base::MessageLoopForUI::IsCurrent())
416 clipboard_owner_.reset(new base::win::MessageWindow());
419 ClipboardWin::~ClipboardWin() {
422 uint64 ClipboardWin::GetSequenceNumber(ClipboardType type) const {
423 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
424 return ::GetClipboardSequenceNumber();
427 bool ClipboardWin::IsFormatAvailable(const Clipboard::FormatType& format,
428 ClipboardType type) const {
429 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
430 return ::IsClipboardFormatAvailable(format.ToFormatEtc().cfFormat) != FALSE;
433 void ClipboardWin::Clear(ClipboardType type) {
434 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
435 ScopedClipboard clipboard;
436 if (!clipboard.Acquire(GetClipboardWindow()))
437 return;
439 ::EmptyClipboard();
442 void ClipboardWin::ReadAvailableTypes(ClipboardType type,
443 std::vector<base::string16>* types,
444 bool* contains_filenames) const {
445 if (!types || !contains_filenames) {
446 NOTREACHED();
447 return;
450 types->clear();
451 if (::IsClipboardFormatAvailable(
452 GetPlainTextFormatType().ToFormatEtc().cfFormat))
453 types->push_back(base::UTF8ToUTF16(kMimeTypeText));
454 if (::IsClipboardFormatAvailable(GetHtmlFormatType().ToFormatEtc().cfFormat))
455 types->push_back(base::UTF8ToUTF16(kMimeTypeHTML));
456 if (::IsClipboardFormatAvailable(GetRtfFormatType().ToFormatEtc().cfFormat))
457 types->push_back(base::UTF8ToUTF16(kMimeTypeRTF));
458 if (::IsClipboardFormatAvailable(CF_DIB))
459 types->push_back(base::UTF8ToUTF16(kMimeTypePNG));
460 *contains_filenames = false;
462 // Acquire the clipboard.
463 ScopedClipboard clipboard;
464 if (!clipboard.Acquire(GetClipboardWindow()))
465 return;
467 HANDLE hdata =
468 ::GetClipboardData(GetWebCustomDataFormatType().ToFormatEtc().cfFormat);
469 if (!hdata)
470 return;
472 ReadCustomDataTypes(::GlobalLock(hdata), ::GlobalSize(hdata), types);
473 ::GlobalUnlock(hdata);
476 void ClipboardWin::ReadText(ClipboardType type, base::string16* result) const {
477 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
478 if (!result) {
479 NOTREACHED();
480 return;
483 result->clear();
485 // Acquire the clipboard.
486 ScopedClipboard clipboard;
487 if (!clipboard.Acquire(GetClipboardWindow()))
488 return;
490 HANDLE data = ::GetClipboardData(CF_UNICODETEXT);
491 if (!data)
492 return;
494 result->assign(static_cast<const base::char16*>(::GlobalLock(data)));
495 ::GlobalUnlock(data);
498 void ClipboardWin::ReadAsciiText(ClipboardType type,
499 std::string* result) const {
500 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
501 if (!result) {
502 NOTREACHED();
503 return;
506 result->clear();
508 // Acquire the clipboard.
509 ScopedClipboard clipboard;
510 if (!clipboard.Acquire(GetClipboardWindow()))
511 return;
513 HANDLE data = ::GetClipboardData(CF_TEXT);
514 if (!data)
515 return;
517 result->assign(static_cast<const char*>(::GlobalLock(data)));
518 ::GlobalUnlock(data);
521 void ClipboardWin::ReadHTML(ClipboardType type,
522 base::string16* markup,
523 std::string* src_url,
524 uint32* fragment_start,
525 uint32* fragment_end) const {
526 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
528 markup->clear();
529 // TODO(dcheng): Remove these checks, I don't think they should be optional.
530 DCHECK(src_url);
531 if (src_url)
532 src_url->clear();
533 *fragment_start = 0;
534 *fragment_end = 0;
536 // Acquire the clipboard.
537 ScopedClipboard clipboard;
538 if (!clipboard.Acquire(GetClipboardWindow()))
539 return;
541 HANDLE data = ::GetClipboardData(GetHtmlFormatType().ToFormatEtc().cfFormat);
542 if (!data)
543 return;
545 std::string cf_html(static_cast<const char*>(::GlobalLock(data)));
546 ::GlobalUnlock(data);
548 size_t html_start = std::string::npos;
549 size_t start_index = std::string::npos;
550 size_t end_index = std::string::npos;
551 ClipboardUtil::CFHtmlExtractMetadata(cf_html, src_url, &html_start,
552 &start_index, &end_index);
554 // This might happen if the contents of the clipboard changed and CF_HTML is
555 // no longer available.
556 if (start_index == std::string::npos ||
557 end_index == std::string::npos ||
558 html_start == std::string::npos)
559 return;
561 if (start_index < html_start || end_index < start_index)
562 return;
564 std::vector<size_t> offsets;
565 offsets.push_back(start_index - html_start);
566 offsets.push_back(end_index - html_start);
567 markup->assign(base::UTF8ToUTF16AndAdjustOffsets(cf_html.data() + html_start,
568 &offsets));
569 *fragment_start = base::checked_cast<uint32>(offsets[0]);
570 *fragment_end = base::checked_cast<uint32>(offsets[1]);
573 void ClipboardWin::ReadRTF(ClipboardType type, std::string* result) const {
574 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
576 ReadData(GetRtfFormatType(), result);
579 SkBitmap ClipboardWin::ReadImage(ClipboardType type) const {
580 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
582 // Acquire the clipboard.
583 ScopedClipboard clipboard;
584 if (!clipboard.Acquire(GetClipboardWindow()))
585 return SkBitmap();
587 // We use a DIB rather than a DDB here since ::GetObject() with the
588 // HBITMAP returned from ::GetClipboardData(CF_BITMAP) always reports a color
589 // depth of 32bpp.
590 BITMAPINFO* bitmap = static_cast<BITMAPINFO*>(::GetClipboardData(CF_DIB));
591 if (!bitmap)
592 return SkBitmap();
593 int color_table_length = 0;
594 switch (bitmap->bmiHeader.biBitCount) {
595 case 1:
596 case 4:
597 case 8:
598 color_table_length = bitmap->bmiHeader.biClrUsed
599 ? bitmap->bmiHeader.biClrUsed
600 : 1 << bitmap->bmiHeader.biBitCount;
601 break;
602 case 16:
603 case 32:
604 if (bitmap->bmiHeader.biCompression == BI_BITFIELDS)
605 color_table_length = 3;
606 break;
607 case 24:
608 break;
609 default:
610 NOTREACHED();
612 const void* bitmap_bits = reinterpret_cast<const char*>(bitmap)
613 + bitmap->bmiHeader.biSize + color_table_length * sizeof(RGBQUAD);
615 gfx::Canvas canvas(gfx::Size(bitmap->bmiHeader.biWidth,
616 bitmap->bmiHeader.biHeight),
617 1.0f,
618 false);
620 skia::ScopedPlatformPaint scoped_platform_paint(canvas.sk_canvas());
621 HDC dc = scoped_platform_paint.GetPlatformSurface();
622 ::SetDIBitsToDevice(dc, 0, 0, bitmap->bmiHeader.biWidth,
623 bitmap->bmiHeader.biHeight, 0, 0, 0,
624 bitmap->bmiHeader.biHeight, bitmap_bits, bitmap,
625 DIB_RGB_COLORS);
627 // Windows doesn't really handle alpha channels well in many situations. When
628 // the source image is < 32 bpp, we force the bitmap to be opaque. When the
629 // source image is 32 bpp, the alpha channel might still contain garbage data.
630 // Since Windows uses premultiplied alpha, we scan for instances where
631 // (R, G, B) > A. If there are any invalid premultiplied colors in the image,
632 // we assume the alpha channel contains garbage and force the bitmap to be
633 // opaque as well. Note that this heuristic will fail on a transparent bitmap
634 // containing only black pixels...
635 const SkBitmap& device_bitmap =
636 canvas.sk_canvas()->getDevice()->accessBitmap(true);
638 SkAutoLockPixels lock(device_bitmap);
639 bool has_invalid_alpha_channel = bitmap->bmiHeader.biBitCount < 32 ||
640 BitmapHasInvalidPremultipliedColors(device_bitmap);
641 if (has_invalid_alpha_channel) {
642 MakeBitmapOpaque(device_bitmap);
646 return canvas.ExtractImageRep().sk_bitmap();
649 void ClipboardWin::ReadCustomData(ClipboardType clipboard_type,
650 const base::string16& type,
651 base::string16* result) const {
652 DCHECK_EQ(clipboard_type, CLIPBOARD_TYPE_COPY_PASTE);
654 // Acquire the clipboard.
655 ScopedClipboard clipboard;
656 if (!clipboard.Acquire(GetClipboardWindow()))
657 return;
659 HANDLE hdata =
660 ::GetClipboardData(GetWebCustomDataFormatType().ToFormatEtc().cfFormat);
661 if (!hdata)
662 return;
664 ReadCustomDataForType(::GlobalLock(hdata), ::GlobalSize(hdata), type, result);
665 ::GlobalUnlock(hdata);
668 void ClipboardWin::ReadBookmark(base::string16* title, std::string* url) const {
669 if (title)
670 title->clear();
672 if (url)
673 url->clear();
675 // Acquire the clipboard.
676 ScopedClipboard clipboard;
677 if (!clipboard.Acquire(GetClipboardWindow()))
678 return;
680 HANDLE data = ::GetClipboardData(GetUrlWFormatType().ToFormatEtc().cfFormat);
681 if (!data)
682 return;
684 base::string16 bookmark(static_cast<const base::char16*>(::GlobalLock(data)));
685 ::GlobalUnlock(data);
687 ParseBookmarkClipboardFormat(bookmark, title, url);
690 void ClipboardWin::ReadData(const FormatType& format,
691 std::string* result) const {
692 if (!result) {
693 NOTREACHED();
694 return;
697 ScopedClipboard clipboard;
698 if (!clipboard.Acquire(GetClipboardWindow()))
699 return;
701 HANDLE data = ::GetClipboardData(format.ToFormatEtc().cfFormat);
702 if (!data)
703 return;
705 result->assign(static_cast<const char*>(::GlobalLock(data)),
706 ::GlobalSize(data));
707 ::GlobalUnlock(data);
710 void ClipboardWin::WriteObjects(ClipboardType type, const ObjectMap& objects) {
711 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
713 ScopedClipboard clipboard;
714 if (!clipboard.Acquire(GetClipboardWindow()))
715 return;
717 ::EmptyClipboard();
719 for (ObjectMap::const_iterator iter = objects.begin(); iter != objects.end();
720 ++iter) {
721 DispatchObject(static_cast<ObjectType>(iter->first), iter->second);
725 void ClipboardWin::WriteText(const char* text_data, size_t text_len) {
726 base::string16 text;
727 base::UTF8ToUTF16(text_data, text_len, &text);
728 HGLOBAL glob = CreateGlobalData(text);
730 WriteToClipboard(CF_UNICODETEXT, glob);
733 void ClipboardWin::WriteHTML(const char* markup_data,
734 size_t markup_len,
735 const char* url_data,
736 size_t url_len) {
737 std::string markup(markup_data, markup_len);
738 std::string url;
740 if (url_len > 0)
741 url.assign(url_data, url_len);
743 std::string html_fragment = ClipboardUtil::HtmlToCFHtml(markup, url);
744 HGLOBAL glob = CreateGlobalData(html_fragment);
746 WriteToClipboard(Clipboard::GetHtmlFormatType().ToFormatEtc().cfFormat, glob);
749 void ClipboardWin::WriteRTF(const char* rtf_data, size_t data_len) {
750 WriteData(GetRtfFormatType(), rtf_data, data_len);
753 void ClipboardWin::WriteBookmark(const char* title_data,
754 size_t title_len,
755 const char* url_data,
756 size_t url_len) {
757 std::string bookmark(title_data, title_len);
758 bookmark.append(1, L'\n');
759 bookmark.append(url_data, url_len);
761 base::string16 wide_bookmark = base::UTF8ToUTF16(bookmark);
762 HGLOBAL glob = CreateGlobalData(wide_bookmark);
764 WriteToClipboard(GetUrlWFormatType().ToFormatEtc().cfFormat, glob);
767 void ClipboardWin::WriteWebSmartPaste() {
768 DCHECK(clipboard_owner_->hwnd() != NULL);
769 ::SetClipboardData(GetWebKitSmartPasteFormatType().ToFormatEtc().cfFormat,
770 NULL);
773 void ClipboardWin::WriteBitmap(const SkBitmap& bitmap) {
774 HDC dc = ::GetDC(NULL);
776 // This doesn't actually cost us a memcpy when the bitmap comes from the
777 // renderer as we load it into the bitmap using setPixels which just sets a
778 // pointer. Someone has to memcpy it into GDI, it might as well be us here.
780 // TODO(darin): share data in gfx/bitmap_header.cc somehow
781 BITMAPINFO bm_info = {};
782 bm_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
783 bm_info.bmiHeader.biWidth = bitmap.width();
784 bm_info.bmiHeader.biHeight = -bitmap.height(); // sets vertical orientation
785 bm_info.bmiHeader.biPlanes = 1;
786 bm_info.bmiHeader.biBitCount = 32;
787 bm_info.bmiHeader.biCompression = BI_RGB;
789 // ::CreateDIBSection allocates memory for us to copy our bitmap into.
790 // Unfortunately, we can't write the created bitmap to the clipboard,
791 // (see http://msdn2.microsoft.com/en-us/library/ms532292.aspx)
792 void* bits;
793 HBITMAP source_hbitmap =
794 ::CreateDIBSection(dc, &bm_info, DIB_RGB_COLORS, &bits, NULL, 0);
796 if (bits && source_hbitmap) {
798 SkAutoLockPixels bitmap_lock(bitmap);
799 // Copy the bitmap out of shared memory and into GDI
800 memcpy(bits, bitmap.getPixels(), bitmap.getSize());
803 // Now we have an HBITMAP, we can write it to the clipboard
804 WriteBitmapFromHandle(source_hbitmap,
805 gfx::Size(bitmap.width(), bitmap.height()));
808 ::DeleteObject(source_hbitmap);
809 ::ReleaseDC(NULL, dc);
812 void ClipboardWin::WriteData(const FormatType& format,
813 const char* data_data,
814 size_t data_len) {
815 HGLOBAL hdata = ::GlobalAlloc(GMEM_MOVEABLE, data_len);
816 if (!hdata)
817 return;
819 char* data = static_cast<char*>(::GlobalLock(hdata));
820 memcpy(data, data_data, data_len);
821 ::GlobalUnlock(data);
822 WriteToClipboard(format.ToFormatEtc().cfFormat, hdata);
825 void ClipboardWin::WriteBitmapFromHandle(HBITMAP source_hbitmap,
826 const gfx::Size& size) {
827 // We would like to just call ::SetClipboardData on the source_hbitmap,
828 // but that bitmap might not be of a sort we can write to the clipboard.
829 // For this reason, we create a new bitmap, copy the bits over, and then
830 // write that to the clipboard.
832 HDC dc = ::GetDC(NULL);
833 HDC compatible_dc = ::CreateCompatibleDC(NULL);
834 HDC source_dc = ::CreateCompatibleDC(NULL);
836 // This is the HBITMAP we will eventually write to the clipboard
837 HBITMAP hbitmap = ::CreateCompatibleBitmap(dc, size.width(), size.height());
838 if (!hbitmap) {
839 // Failed to create the bitmap
840 ::DeleteDC(compatible_dc);
841 ::DeleteDC(source_dc);
842 ::ReleaseDC(NULL, dc);
843 return;
846 HBITMAP old_hbitmap = (HBITMAP)SelectObject(compatible_dc, hbitmap);
847 HBITMAP old_source = (HBITMAP)SelectObject(source_dc, source_hbitmap);
849 // Now we need to blend it into an HBITMAP we can place on the clipboard
850 BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
851 ::GdiAlphaBlend(compatible_dc,
854 size.width(),
855 size.height(),
856 source_dc,
859 size.width(),
860 size.height(),
861 bf);
863 // Clean up all the handles we just opened
864 ::SelectObject(compatible_dc, old_hbitmap);
865 ::SelectObject(source_dc, old_source);
866 ::DeleteObject(old_hbitmap);
867 ::DeleteObject(old_source);
868 ::DeleteDC(compatible_dc);
869 ::DeleteDC(source_dc);
870 ::ReleaseDC(NULL, dc);
872 WriteToClipboard(CF_BITMAP, hbitmap);
875 void ClipboardWin::WriteToClipboard(unsigned int format, HANDLE handle) {
876 DCHECK(clipboard_owner_->hwnd() != NULL);
877 if (handle && !::SetClipboardData(format, handle)) {
878 DCHECK(ERROR_CLIPBOARD_NOT_OPEN != GetLastError());
879 FreeData(format, handle);
883 HWND ClipboardWin::GetClipboardWindow() const {
884 if (!clipboard_owner_)
885 return NULL;
887 if (clipboard_owner_->hwnd() == NULL)
888 clipboard_owner_->Create(base::Bind(&ClipboardOwnerWndProc));
890 return clipboard_owner_->hwnd();
893 } // namespace ui