Drive: Add BatchableRequest subclass.
[chromium-blink-merge.git] / ui / base / clipboard / clipboard_win.cc
blob79984311809d4b09324cca04af17e7e9e259020c
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/logging.h"
17 #include "base/memory/shared_memory.h"
18 #include "base/message_loop/message_loop.h"
19 #include "base/numerics/safe_conversions.h"
20 #include "base/stl_util.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/string_util.h"
23 #include "base/strings/utf_offset_string_conversions.h"
24 #include "base/strings/utf_string_conversions.h"
25 #include "base/win/message_window.h"
26 #include "base/win/scoped_gdi_object.h"
27 #include "base/win/scoped_hdc.h"
28 #include "third_party/skia/include/core/SkBitmap.h"
29 #include "ui/base/clipboard/clipboard_util_win.h"
30 #include "ui/base/clipboard/custom_data_helper.h"
31 #include "ui/gfx/canvas.h"
32 #include "ui/gfx/geometry/size.h"
34 namespace ui {
36 namespace {
38 // A scoper to impersonate the anonymous token and revert when leaving scope
39 class AnonymousImpersonator {
40 public:
41 AnonymousImpersonator() {
42 must_revert_ = ::ImpersonateAnonymousToken(::GetCurrentThread());
45 ~AnonymousImpersonator() {
46 if (must_revert_)
47 ::RevertToSelf();
50 private:
51 BOOL must_revert_;
52 DISALLOW_COPY_AND_ASSIGN(AnonymousImpersonator);
55 // A scoper to manage acquiring and automatically releasing the clipboard.
56 class ScopedClipboard {
57 public:
58 ScopedClipboard() : opened_(false) { }
60 ~ScopedClipboard() {
61 if (opened_)
62 Release();
65 bool Acquire(HWND owner) {
66 const int kMaxAttemptsToOpenClipboard = 5;
68 if (opened_) {
69 NOTREACHED();
70 return false;
73 // Attempt to open the clipboard, which will acquire the Windows clipboard
74 // lock. This may fail if another process currently holds this lock.
75 // We're willing to try a few times in the hopes of acquiring it.
77 // This turns out to be an issue when using remote desktop because the
78 // rdpclip.exe process likes to read what we've written to the clipboard and
79 // send it to the RDP client. If we open and close the clipboard in quick
80 // succession, we might be trying to open it while rdpclip.exe has it open,
81 // See Bug 815425.
83 // In fact, we believe we'll only spin this loop over remote desktop. In
84 // normal situations, the user is initiating clipboard operations and there
85 // shouldn't be contention.
87 for (int attempts = 0; attempts < kMaxAttemptsToOpenClipboard; ++attempts) {
88 // If we didn't manage to open the clipboard, sleep a bit and be hopeful.
89 if (attempts != 0)
90 ::Sleep(5);
92 if (::OpenClipboard(owner)) {
93 opened_ = true;
94 return true;
98 // We failed to acquire the clipboard.
99 return false;
102 void Release() {
103 if (opened_) {
104 // Impersonate the anonymous token during the call to CloseClipboard
105 // This prevents Windows 8+ capturing the broker's access token which
106 // could be accessed by lower-privileges chrome processes leading to
107 // a risk of EoP
108 AnonymousImpersonator impersonator;
109 ::CloseClipboard();
110 opened_ = false;
111 } else {
112 NOTREACHED();
116 private:
117 bool opened_;
120 bool ClipboardOwnerWndProc(UINT message,
121 WPARAM wparam,
122 LPARAM lparam,
123 LRESULT* result) {
124 switch (message) {
125 case WM_RENDERFORMAT:
126 // This message comes when SetClipboardData was sent a null data handle
127 // and now it's come time to put the data on the clipboard.
128 // We always set data, so there isn't a need to actually do anything here.
129 break;
130 case WM_RENDERALLFORMATS:
131 // This message comes when SetClipboardData was sent a null data handle
132 // and now this application is about to quit, so it must put data on
133 // the clipboard before it exits.
134 // We always set data, so there isn't a need to actually do anything here.
135 break;
136 case WM_DRAWCLIPBOARD:
137 break;
138 case WM_DESTROY:
139 break;
140 case WM_CHANGECBCHAIN:
141 break;
142 default:
143 return false;
146 *result = 0;
147 return true;
150 template <typename charT>
151 HGLOBAL CreateGlobalData(const std::basic_string<charT>& str) {
152 HGLOBAL data =
153 ::GlobalAlloc(GMEM_MOVEABLE, ((str.size() + 1) * sizeof(charT)));
154 if (data) {
155 charT* raw_data = static_cast<charT*>(::GlobalLock(data));
156 memcpy(raw_data, str.data(), str.size() * sizeof(charT));
157 raw_data[str.size()] = '\0';
158 ::GlobalUnlock(data);
160 return data;
163 bool BitmapHasInvalidPremultipliedColors(const SkBitmap& bitmap) {
164 for (int x = 0; x < bitmap.width(); ++x) {
165 for (int y = 0; y < bitmap.height(); ++y) {
166 uint32_t pixel = *bitmap.getAddr32(x, y);
167 if (SkColorGetR(pixel) > SkColorGetA(pixel) ||
168 SkColorGetG(pixel) > SkColorGetA(pixel) ||
169 SkColorGetB(pixel) > SkColorGetA(pixel))
170 return true;
173 return false;
176 void MakeBitmapOpaque(const SkBitmap& bitmap) {
177 for (int x = 0; x < bitmap.width(); ++x) {
178 for (int y = 0; y < bitmap.height(); ++y) {
179 *bitmap.getAddr32(x, y) = SkColorSetA(*bitmap.getAddr32(x, y), 0xFF);
184 void ParseBookmarkClipboardFormat(const base::string16& bookmark,
185 base::string16* title,
186 std::string* url) {
187 const base::string16 kDelim = base::ASCIIToUTF16("\r\n");
189 const size_t title_end = bookmark.find_first_of(kDelim);
190 if (title)
191 title->assign(bookmark.substr(0, title_end));
193 if (url) {
194 const size_t url_start = bookmark.find_first_not_of(kDelim, title_end);
195 if (url_start != base::string16::npos) {
196 *url =
197 base::UTF16ToUTF8(bookmark.substr(url_start, base::string16::npos));
202 void FreeData(unsigned int format, HANDLE data) {
203 if (format == CF_BITMAP)
204 ::DeleteObject(static_cast<HBITMAP>(data));
205 else
206 ::GlobalFree(data);
209 } // namespace
211 // Clipboard::FormatType implementation.
212 Clipboard::FormatType::FormatType() : data_() {}
214 Clipboard::FormatType::FormatType(UINT native_format) : data_() {
215 // There's no good way to actually initialize this in the constructor in
216 // C++03.
217 data_.cfFormat = static_cast<CLIPFORMAT>(native_format);
218 data_.dwAspect = DVASPECT_CONTENT;
219 data_.lindex = -1;
220 data_.tymed = TYMED_HGLOBAL;
223 Clipboard::FormatType::FormatType(UINT native_format, LONG index) : data_() {
224 // There's no good way to actually initialize this in the constructor in
225 // C++03.
226 data_.cfFormat = static_cast<CLIPFORMAT>(native_format);
227 data_.dwAspect = DVASPECT_CONTENT;
228 data_.lindex = index;
229 data_.tymed = TYMED_HGLOBAL;
232 Clipboard::FormatType::~FormatType() {
235 std::string Clipboard::FormatType::Serialize() const {
236 return base::IntToString(data_.cfFormat);
239 // static
240 Clipboard::FormatType Clipboard::FormatType::Deserialize(
241 const std::string& serialization) {
242 int clipboard_format = -1;
243 if (!base::StringToInt(serialization, &clipboard_format)) {
244 NOTREACHED();
245 return FormatType();
247 return FormatType(clipboard_format);
250 bool Clipboard::FormatType::operator<(const FormatType& other) const {
251 return data_.cfFormat < other.data_.cfFormat;
254 bool Clipboard::FormatType::Equals(const FormatType& other) const {
255 return data_.cfFormat == other.data_.cfFormat;
258 // Various predefined FormatTypes.
259 // static
260 Clipboard::FormatType Clipboard::GetFormatType(
261 const std::string& format_string) {
262 return FormatType(
263 ::RegisterClipboardFormat(base::ASCIIToUTF16(format_string).c_str()));
266 // static
267 const Clipboard::FormatType& Clipboard::GetUrlFormatType() {
268 CR_DEFINE_STATIC_LOCAL(
269 FormatType, type, (::RegisterClipboardFormat(CFSTR_INETURLA)));
270 return type;
273 // static
274 const Clipboard::FormatType& Clipboard::GetUrlWFormatType() {
275 CR_DEFINE_STATIC_LOCAL(
276 FormatType, type, (::RegisterClipboardFormat(CFSTR_INETURLW)));
277 return type;
280 // static
281 const Clipboard::FormatType& Clipboard::GetMozUrlFormatType() {
282 CR_DEFINE_STATIC_LOCAL(
283 FormatType, type, (::RegisterClipboardFormat(L"text/x-moz-url")));
284 return type;
287 // static
288 const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() {
289 CR_DEFINE_STATIC_LOCAL(FormatType, type, (CF_TEXT));
290 return type;
293 // static
294 const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() {
295 CR_DEFINE_STATIC_LOCAL(FormatType, type, (CF_UNICODETEXT));
296 return type;
299 // static
300 const Clipboard::FormatType& Clipboard::GetFilenameFormatType() {
301 CR_DEFINE_STATIC_LOCAL(
302 FormatType, type, (::RegisterClipboardFormat(CFSTR_FILENAMEA)));
303 return type;
306 // static
307 const Clipboard::FormatType& Clipboard::GetFilenameWFormatType() {
308 CR_DEFINE_STATIC_LOCAL(
309 FormatType, type, (::RegisterClipboardFormat(CFSTR_FILENAMEW)));
310 return type;
313 // MS HTML Format
314 // static
315 const Clipboard::FormatType& Clipboard::GetHtmlFormatType() {
316 CR_DEFINE_STATIC_LOCAL(
317 FormatType, type, (::RegisterClipboardFormat(L"HTML Format")));
318 return type;
321 // MS RTF Format
322 // static
323 const Clipboard::FormatType& Clipboard::GetRtfFormatType() {
324 CR_DEFINE_STATIC_LOCAL(
325 FormatType, type, (::RegisterClipboardFormat(L"Rich Text Format")));
326 return type;
329 // static
330 const Clipboard::FormatType& Clipboard::GetBitmapFormatType() {
331 CR_DEFINE_STATIC_LOCAL(FormatType, type, (CF_BITMAP));
332 return type;
335 // Firefox text/html
336 // static
337 const Clipboard::FormatType& Clipboard::GetTextHtmlFormatType() {
338 CR_DEFINE_STATIC_LOCAL(
339 FormatType, type, (::RegisterClipboardFormat(L"text/html")));
340 return type;
343 // static
344 const Clipboard::FormatType& Clipboard::GetCFHDropFormatType() {
345 CR_DEFINE_STATIC_LOCAL(FormatType, type, (CF_HDROP));
346 return type;
349 // static
350 const Clipboard::FormatType& Clipboard::GetFileDescriptorFormatType() {
351 CR_DEFINE_STATIC_LOCAL(
352 FormatType, type, (::RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR)));
353 return type;
356 // static
357 const Clipboard::FormatType& Clipboard::GetFileContentZeroFormatType() {
358 CR_DEFINE_STATIC_LOCAL(
359 FormatType, type, (::RegisterClipboardFormat(CFSTR_FILECONTENTS), 0));
360 return type;
363 // static
364 const Clipboard::FormatType& Clipboard::GetIDListFormatType() {
365 CR_DEFINE_STATIC_LOCAL(
366 FormatType, type, (::RegisterClipboardFormat(CFSTR_SHELLIDLIST)));
367 return type;
370 // static
371 const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() {
372 CR_DEFINE_STATIC_LOCAL(
373 FormatType,
374 type,
375 (::RegisterClipboardFormat(L"WebKit Smart Paste Format")));
376 return type;
379 // static
380 const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() {
381 // TODO(dcheng): This name is temporary. See http://crbug.com/106449.
382 CR_DEFINE_STATIC_LOCAL(
383 FormatType,
384 type,
385 (::RegisterClipboardFormat(L"Chromium Web Custom MIME Data Format")));
386 return type;
389 // static
390 const Clipboard::FormatType& Clipboard::GetPepperCustomDataFormatType() {
391 CR_DEFINE_STATIC_LOCAL(
392 FormatType,
393 type,
394 (::RegisterClipboardFormat(L"Chromium Pepper MIME Data Format")));
395 return type;
398 // Clipboard factory method.
399 // static
400 Clipboard* Clipboard::Create() {
401 return new ClipboardWin;
404 // ClipboardWin implementation.
405 ClipboardWin::ClipboardWin() {
406 if (base::MessageLoopForUI::IsCurrent())
407 clipboard_owner_.reset(new base::win::MessageWindow());
410 ClipboardWin::~ClipboardWin() {
413 uint64 ClipboardWin::GetSequenceNumber(ClipboardType type) const {
414 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
415 return ::GetClipboardSequenceNumber();
418 bool ClipboardWin::IsFormatAvailable(const Clipboard::FormatType& format,
419 ClipboardType type) const {
420 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
421 return ::IsClipboardFormatAvailable(format.ToFormatEtc().cfFormat) != FALSE;
424 void ClipboardWin::Clear(ClipboardType type) {
425 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
426 ScopedClipboard clipboard;
427 if (!clipboard.Acquire(GetClipboardWindow()))
428 return;
430 ::EmptyClipboard();
433 void ClipboardWin::ReadAvailableTypes(ClipboardType type,
434 std::vector<base::string16>* types,
435 bool* contains_filenames) const {
436 if (!types || !contains_filenames) {
437 NOTREACHED();
438 return;
441 types->clear();
442 if (::IsClipboardFormatAvailable(
443 GetPlainTextFormatType().ToFormatEtc().cfFormat))
444 types->push_back(base::UTF8ToUTF16(kMimeTypeText));
445 if (::IsClipboardFormatAvailable(GetHtmlFormatType().ToFormatEtc().cfFormat))
446 types->push_back(base::UTF8ToUTF16(kMimeTypeHTML));
447 if (::IsClipboardFormatAvailable(GetRtfFormatType().ToFormatEtc().cfFormat))
448 types->push_back(base::UTF8ToUTF16(kMimeTypeRTF));
449 if (::IsClipboardFormatAvailable(CF_DIB))
450 types->push_back(base::UTF8ToUTF16(kMimeTypePNG));
451 *contains_filenames = false;
453 // Acquire the clipboard.
454 ScopedClipboard clipboard;
455 if (!clipboard.Acquire(GetClipboardWindow()))
456 return;
458 HANDLE hdata =
459 ::GetClipboardData(GetWebCustomDataFormatType().ToFormatEtc().cfFormat);
460 if (!hdata)
461 return;
463 ReadCustomDataTypes(::GlobalLock(hdata), ::GlobalSize(hdata), types);
464 ::GlobalUnlock(hdata);
467 void ClipboardWin::ReadText(ClipboardType type, base::string16* result) const {
468 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
469 if (!result) {
470 NOTREACHED();
471 return;
474 result->clear();
476 // Acquire the clipboard.
477 ScopedClipboard clipboard;
478 if (!clipboard.Acquire(GetClipboardWindow()))
479 return;
481 HANDLE data = ::GetClipboardData(CF_UNICODETEXT);
482 if (!data)
483 return;
485 result->assign(static_cast<const base::char16*>(::GlobalLock(data)));
486 ::GlobalUnlock(data);
489 void ClipboardWin::ReadAsciiText(ClipboardType type,
490 std::string* result) const {
491 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
492 if (!result) {
493 NOTREACHED();
494 return;
497 result->clear();
499 // Acquire the clipboard.
500 ScopedClipboard clipboard;
501 if (!clipboard.Acquire(GetClipboardWindow()))
502 return;
504 HANDLE data = ::GetClipboardData(CF_TEXT);
505 if (!data)
506 return;
508 result->assign(static_cast<const char*>(::GlobalLock(data)));
509 ::GlobalUnlock(data);
512 void ClipboardWin::ReadHTML(ClipboardType type,
513 base::string16* markup,
514 std::string* src_url,
515 uint32* fragment_start,
516 uint32* fragment_end) const {
517 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
519 markup->clear();
520 // TODO(dcheng): Remove these checks, I don't think they should be optional.
521 DCHECK(src_url);
522 if (src_url)
523 src_url->clear();
524 *fragment_start = 0;
525 *fragment_end = 0;
527 // Acquire the clipboard.
528 ScopedClipboard clipboard;
529 if (!clipboard.Acquire(GetClipboardWindow()))
530 return;
532 HANDLE data = ::GetClipboardData(GetHtmlFormatType().ToFormatEtc().cfFormat);
533 if (!data)
534 return;
536 std::string cf_html(static_cast<const char*>(::GlobalLock(data)));
537 ::GlobalUnlock(data);
539 size_t html_start = std::string::npos;
540 size_t start_index = std::string::npos;
541 size_t end_index = std::string::npos;
542 ClipboardUtil::CFHtmlExtractMetadata(cf_html, src_url, &html_start,
543 &start_index, &end_index);
545 // This might happen if the contents of the clipboard changed and CF_HTML is
546 // no longer available.
547 if (start_index == std::string::npos ||
548 end_index == std::string::npos ||
549 html_start == std::string::npos)
550 return;
552 if (start_index < html_start || end_index < start_index)
553 return;
555 std::vector<size_t> offsets;
556 offsets.push_back(start_index - html_start);
557 offsets.push_back(end_index - html_start);
558 markup->assign(base::UTF8ToUTF16AndAdjustOffsets(cf_html.data() + html_start,
559 &offsets));
560 *fragment_start = base::checked_cast<uint32>(offsets[0]);
561 *fragment_end = base::checked_cast<uint32>(offsets[1]);
564 void ClipboardWin::ReadRTF(ClipboardType type, std::string* result) const {
565 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
567 ReadData(GetRtfFormatType(), result);
570 SkBitmap ClipboardWin::ReadImage(ClipboardType type) const {
571 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
573 // Acquire the clipboard.
574 ScopedClipboard clipboard;
575 if (!clipboard.Acquire(GetClipboardWindow()))
576 return SkBitmap();
578 // We use a DIB rather than a DDB here since ::GetObject() with the
579 // HBITMAP returned from ::GetClipboardData(CF_BITMAP) always reports a color
580 // depth of 32bpp.
581 BITMAPINFO* bitmap = static_cast<BITMAPINFO*>(::GetClipboardData(CF_DIB));
582 if (!bitmap)
583 return SkBitmap();
584 int color_table_length = 0;
585 switch (bitmap->bmiHeader.biBitCount) {
586 case 1:
587 case 4:
588 case 8:
589 color_table_length = bitmap->bmiHeader.biClrUsed
590 ? bitmap->bmiHeader.biClrUsed
591 : 1 << bitmap->bmiHeader.biBitCount;
592 break;
593 case 16:
594 case 32:
595 if (bitmap->bmiHeader.biCompression == BI_BITFIELDS)
596 color_table_length = 3;
597 break;
598 case 24:
599 break;
600 default:
601 NOTREACHED();
603 const void* bitmap_bits = reinterpret_cast<const char*>(bitmap)
604 + bitmap->bmiHeader.biSize + color_table_length * sizeof(RGBQUAD);
606 gfx::Canvas canvas(gfx::Size(bitmap->bmiHeader.biWidth,
607 bitmap->bmiHeader.biHeight),
608 1.0f,
609 false);
611 skia::ScopedPlatformPaint scoped_platform_paint(canvas.sk_canvas());
612 HDC dc = scoped_platform_paint.GetPlatformSurface();
613 ::SetDIBitsToDevice(dc, 0, 0, bitmap->bmiHeader.biWidth,
614 bitmap->bmiHeader.biHeight, 0, 0, 0,
615 bitmap->bmiHeader.biHeight, bitmap_bits, bitmap,
616 DIB_RGB_COLORS);
618 // Windows doesn't really handle alpha channels well in many situations. When
619 // the source image is < 32 bpp, we force the bitmap to be opaque. When the
620 // source image is 32 bpp, the alpha channel might still contain garbage data.
621 // Since Windows uses premultiplied alpha, we scan for instances where
622 // (R, G, B) > A. If there are any invalid premultiplied colors in the image,
623 // we assume the alpha channel contains garbage and force the bitmap to be
624 // opaque as well. Note that this heuristic will fail on a transparent bitmap
625 // containing only black pixels...
626 const SkBitmap& device_bitmap =
627 canvas.sk_canvas()->getDevice()->accessBitmap(true);
629 SkAutoLockPixels lock(device_bitmap);
630 bool has_invalid_alpha_channel = bitmap->bmiHeader.biBitCount < 32 ||
631 BitmapHasInvalidPremultipliedColors(device_bitmap);
632 if (has_invalid_alpha_channel) {
633 MakeBitmapOpaque(device_bitmap);
637 return canvas.ExtractImageRep().sk_bitmap();
640 void ClipboardWin::ReadCustomData(ClipboardType clipboard_type,
641 const base::string16& type,
642 base::string16* result) const {
643 DCHECK_EQ(clipboard_type, CLIPBOARD_TYPE_COPY_PASTE);
645 // Acquire the clipboard.
646 ScopedClipboard clipboard;
647 if (!clipboard.Acquire(GetClipboardWindow()))
648 return;
650 HANDLE hdata =
651 ::GetClipboardData(GetWebCustomDataFormatType().ToFormatEtc().cfFormat);
652 if (!hdata)
653 return;
655 ReadCustomDataForType(::GlobalLock(hdata), ::GlobalSize(hdata), type, result);
656 ::GlobalUnlock(hdata);
659 void ClipboardWin::ReadBookmark(base::string16* title, std::string* url) const {
660 if (title)
661 title->clear();
663 if (url)
664 url->clear();
666 // Acquire the clipboard.
667 ScopedClipboard clipboard;
668 if (!clipboard.Acquire(GetClipboardWindow()))
669 return;
671 HANDLE data = ::GetClipboardData(GetUrlWFormatType().ToFormatEtc().cfFormat);
672 if (!data)
673 return;
675 base::string16 bookmark(static_cast<const base::char16*>(::GlobalLock(data)));
676 ::GlobalUnlock(data);
678 ParseBookmarkClipboardFormat(bookmark, title, url);
681 void ClipboardWin::ReadData(const FormatType& format,
682 std::string* result) const {
683 if (!result) {
684 NOTREACHED();
685 return;
688 ScopedClipboard clipboard;
689 if (!clipboard.Acquire(GetClipboardWindow()))
690 return;
692 HANDLE data = ::GetClipboardData(format.ToFormatEtc().cfFormat);
693 if (!data)
694 return;
696 result->assign(static_cast<const char*>(::GlobalLock(data)),
697 ::GlobalSize(data));
698 ::GlobalUnlock(data);
701 void ClipboardWin::WriteObjects(ClipboardType type, const ObjectMap& objects) {
702 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
704 ScopedClipboard clipboard;
705 if (!clipboard.Acquire(GetClipboardWindow()))
706 return;
708 ::EmptyClipboard();
710 for (ObjectMap::const_iterator iter = objects.begin(); iter != objects.end();
711 ++iter) {
712 DispatchObject(static_cast<ObjectType>(iter->first), iter->second);
716 void ClipboardWin::WriteText(const char* text_data, size_t text_len) {
717 base::string16 text;
718 base::UTF8ToUTF16(text_data, text_len, &text);
719 HGLOBAL glob = CreateGlobalData(text);
721 WriteToClipboard(CF_UNICODETEXT, glob);
724 void ClipboardWin::WriteHTML(const char* markup_data,
725 size_t markup_len,
726 const char* url_data,
727 size_t url_len) {
728 std::string markup(markup_data, markup_len);
729 std::string url;
731 if (url_len > 0)
732 url.assign(url_data, url_len);
734 std::string html_fragment = ClipboardUtil::HtmlToCFHtml(markup, url);
735 HGLOBAL glob = CreateGlobalData(html_fragment);
737 WriteToClipboard(Clipboard::GetHtmlFormatType().ToFormatEtc().cfFormat, glob);
740 void ClipboardWin::WriteRTF(const char* rtf_data, size_t data_len) {
741 WriteData(GetRtfFormatType(), rtf_data, data_len);
744 void ClipboardWin::WriteBookmark(const char* title_data,
745 size_t title_len,
746 const char* url_data,
747 size_t url_len) {
748 std::string bookmark(title_data, title_len);
749 bookmark.append(1, L'\n');
750 bookmark.append(url_data, url_len);
752 base::string16 wide_bookmark = base::UTF8ToUTF16(bookmark);
753 HGLOBAL glob = CreateGlobalData(wide_bookmark);
755 WriteToClipboard(GetUrlWFormatType().ToFormatEtc().cfFormat, glob);
758 void ClipboardWin::WriteWebSmartPaste() {
759 DCHECK(clipboard_owner_->hwnd() != NULL);
760 ::SetClipboardData(GetWebKitSmartPasteFormatType().ToFormatEtc().cfFormat,
761 NULL);
764 void ClipboardWin::WriteBitmap(const SkBitmap& bitmap) {
765 HDC dc = ::GetDC(NULL);
767 // This doesn't actually cost us a memcpy when the bitmap comes from the
768 // renderer as we load it into the bitmap using setPixels which just sets a
769 // pointer. Someone has to memcpy it into GDI, it might as well be us here.
771 // TODO(darin): share data in gfx/bitmap_header.cc somehow
772 BITMAPINFO bm_info = {0};
773 bm_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
774 bm_info.bmiHeader.biWidth = bitmap.width();
775 bm_info.bmiHeader.biHeight = -bitmap.height(); // sets vertical orientation
776 bm_info.bmiHeader.biPlanes = 1;
777 bm_info.bmiHeader.biBitCount = 32;
778 bm_info.bmiHeader.biCompression = BI_RGB;
780 // ::CreateDIBSection allocates memory for us to copy our bitmap into.
781 // Unfortunately, we can't write the created bitmap to the clipboard,
782 // (see http://msdn2.microsoft.com/en-us/library/ms532292.aspx)
783 void* bits;
784 HBITMAP source_hbitmap =
785 ::CreateDIBSection(dc, &bm_info, DIB_RGB_COLORS, &bits, NULL, 0);
787 if (bits && source_hbitmap) {
789 SkAutoLockPixels bitmap_lock(bitmap);
790 // Copy the bitmap out of shared memory and into GDI
791 memcpy(bits, bitmap.getPixels(), bitmap.getSize());
794 // Now we have an HBITMAP, we can write it to the clipboard
795 WriteBitmapFromHandle(source_hbitmap,
796 gfx::Size(bitmap.width(), bitmap.height()));
799 ::DeleteObject(source_hbitmap);
800 ::ReleaseDC(NULL, dc);
803 void ClipboardWin::WriteData(const FormatType& format,
804 const char* data_data,
805 size_t data_len) {
806 HGLOBAL hdata = ::GlobalAlloc(GMEM_MOVEABLE, data_len);
807 if (!hdata)
808 return;
810 char* data = static_cast<char*>(::GlobalLock(hdata));
811 memcpy(data, data_data, data_len);
812 ::GlobalUnlock(data);
813 WriteToClipboard(format.ToFormatEtc().cfFormat, hdata);
816 void ClipboardWin::WriteBitmapFromHandle(HBITMAP source_hbitmap,
817 const gfx::Size& size) {
818 // We would like to just call ::SetClipboardData on the source_hbitmap,
819 // but that bitmap might not be of a sort we can write to the clipboard.
820 // For this reason, we create a new bitmap, copy the bits over, and then
821 // write that to the clipboard.
823 HDC dc = ::GetDC(NULL);
824 HDC compatible_dc = ::CreateCompatibleDC(NULL);
825 HDC source_dc = ::CreateCompatibleDC(NULL);
827 // This is the HBITMAP we will eventually write to the clipboard
828 HBITMAP hbitmap = ::CreateCompatibleBitmap(dc, size.width(), size.height());
829 if (!hbitmap) {
830 // Failed to create the bitmap
831 ::DeleteDC(compatible_dc);
832 ::DeleteDC(source_dc);
833 ::ReleaseDC(NULL, dc);
834 return;
837 HBITMAP old_hbitmap = (HBITMAP)SelectObject(compatible_dc, hbitmap);
838 HBITMAP old_source = (HBITMAP)SelectObject(source_dc, source_hbitmap);
840 // Now we need to blend it into an HBITMAP we can place on the clipboard
841 BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
842 ::GdiAlphaBlend(compatible_dc,
845 size.width(),
846 size.height(),
847 source_dc,
850 size.width(),
851 size.height(),
852 bf);
854 // Clean up all the handles we just opened
855 ::SelectObject(compatible_dc, old_hbitmap);
856 ::SelectObject(source_dc, old_source);
857 ::DeleteObject(old_hbitmap);
858 ::DeleteObject(old_source);
859 ::DeleteDC(compatible_dc);
860 ::DeleteDC(source_dc);
861 ::ReleaseDC(NULL, dc);
863 WriteToClipboard(CF_BITMAP, hbitmap);
866 void ClipboardWin::WriteToClipboard(unsigned int format, HANDLE handle) {
867 DCHECK(clipboard_owner_->hwnd() != NULL);
868 if (handle && !::SetClipboardData(format, handle)) {
869 DCHECK(ERROR_CLIPBOARD_NOT_OPEN != GetLastError());
870 FreeData(format, handle);
874 HWND ClipboardWin::GetClipboardWindow() const {
875 if (!clipboard_owner_)
876 return NULL;
878 if (clipboard_owner_->hwnd() == NULL)
879 clipboard_owner_->Create(base::Bind(&ClipboardOwnerWndProc));
881 return clipboard_owner_->hwnd();
884 } // namespace ui