Linux: Depend on liberation-fonts package for RPMs.
[chromium-blink-merge.git] / ui / base / clipboard / clipboard_win.cc
blob2743755dfd4d3470db499f111965414e93ab2aa4
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/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 // The following formats can be referenced by ClipboardUtilWin::GetPlainText.
267 // For reasons (COM), they must be initialized in a thread-safe manner.
268 // TODO(dcheng): We probably need to make static initialization of "known"
269 // FormatTypes thread-safe on all platforms.
270 #define CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(name, ...) \
271 struct FormatTypeArgumentForwarder : public FormatType { \
272 FormatTypeArgumentForwarder() : FormatType(__VA_ARGS__) { } \
273 }; \
274 static base::LazyInstance<FormatTypeArgumentForwarder>::Leaky name = \
275 LAZY_INSTANCE_INITIALIZER
276 // static
277 const Clipboard::FormatType& Clipboard::GetUrlFormatType() {
278 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
279 type, ::RegisterClipboardFormat(CFSTR_INETURLA));
280 return type.Get();
283 // static
284 const Clipboard::FormatType& Clipboard::GetUrlWFormatType() {
285 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
286 type, ::RegisterClipboardFormat(CFSTR_INETURLW));
287 return type.Get();
290 // static
291 const Clipboard::FormatType& Clipboard::GetMozUrlFormatType() {
292 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
293 type, ::RegisterClipboardFormat(L"text/x-moz-url"));
294 return type.Get();
297 // static
298 const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() {
299 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(type, CF_TEXT);
300 return type.Get();
303 // static
304 const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() {
305 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(type, CF_UNICODETEXT);
306 return type.Get();
309 // static
310 const Clipboard::FormatType& Clipboard::GetFilenameFormatType() {
311 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
312 type, ::RegisterClipboardFormat(CFSTR_FILENAMEA));
313 return type.Get();
316 // static
317 const Clipboard::FormatType& Clipboard::GetFilenameWFormatType() {
318 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
319 type, ::RegisterClipboardFormat(CFSTR_FILENAMEW));
320 return type.Get();
323 // MS HTML Format
324 // static
325 const Clipboard::FormatType& Clipboard::GetHtmlFormatType() {
326 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
327 type, ::RegisterClipboardFormat(L"HTML Format"));
328 return type.Get();
331 // MS RTF Format
332 // static
333 const Clipboard::FormatType& Clipboard::GetRtfFormatType() {
334 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
335 type, ::RegisterClipboardFormat(L"Rich Text Format"));
336 return type.Get();
339 // static
340 const Clipboard::FormatType& Clipboard::GetBitmapFormatType() {
341 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(type, CF_BITMAP);
342 return type.Get();
345 // Firefox text/html
346 // static
347 const Clipboard::FormatType& Clipboard::GetTextHtmlFormatType() {
348 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
349 type, ::RegisterClipboardFormat(L"text/html"));
350 return type.Get();
353 // static
354 const Clipboard::FormatType& Clipboard::GetCFHDropFormatType() {
355 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(type, CF_HDROP);
356 return type.Get();
359 // static
360 const Clipboard::FormatType& Clipboard::GetFileDescriptorFormatType() {
361 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
362 type, ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR));
363 return type.Get();
366 // static
367 const Clipboard::FormatType& Clipboard::GetFileContentZeroFormatType() {
368 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
369 type, ::RegisterClipboardFormat(CFSTR_FILECONTENTS), 0);
370 return type.Get();
373 // static
374 const Clipboard::FormatType& Clipboard::GetIDListFormatType() {
375 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
376 type, ::RegisterClipboardFormat(CFSTR_SHELLIDLIST));
377 return type.Get();
380 // static
381 const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() {
382 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
383 type,
384 ::RegisterClipboardFormat(L"WebKit Smart Paste Format"));
385 return type.Get();
388 // static
389 const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() {
390 // TODO(dcheng): This name is temporary. See http://crbug.com/106449.
391 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
392 type,
393 ::RegisterClipboardFormat(L"Chromium Web Custom MIME Data Format"));
394 return type.Get();
397 // static
398 const Clipboard::FormatType& Clipboard::GetPepperCustomDataFormatType() {
399 CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE(
400 type,
401 ::RegisterClipboardFormat(L"Chromium Pepper MIME Data Format"));
402 return type.Get();
404 #undef CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE
406 // Clipboard factory method.
407 // static
408 Clipboard* Clipboard::Create() {
409 return new ClipboardWin;
412 // ClipboardWin implementation.
413 ClipboardWin::ClipboardWin() {
414 if (base::MessageLoopForUI::IsCurrent())
415 clipboard_owner_.reset(new base::win::MessageWindow());
418 ClipboardWin::~ClipboardWin() {
421 uint64 ClipboardWin::GetSequenceNumber(ClipboardType type) const {
422 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
423 return ::GetClipboardSequenceNumber();
426 bool ClipboardWin::IsFormatAvailable(const Clipboard::FormatType& format,
427 ClipboardType type) const {
428 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
429 return ::IsClipboardFormatAvailable(format.ToFormatEtc().cfFormat) != FALSE;
432 void ClipboardWin::Clear(ClipboardType type) {
433 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
434 ScopedClipboard clipboard;
435 if (!clipboard.Acquire(GetClipboardWindow()))
436 return;
438 ::EmptyClipboard();
441 void ClipboardWin::ReadAvailableTypes(ClipboardType type,
442 std::vector<base::string16>* types,
443 bool* contains_filenames) const {
444 if (!types || !contains_filenames) {
445 NOTREACHED();
446 return;
449 types->clear();
450 if (::IsClipboardFormatAvailable(
451 GetPlainTextFormatType().ToFormatEtc().cfFormat))
452 types->push_back(base::UTF8ToUTF16(kMimeTypeText));
453 if (::IsClipboardFormatAvailable(GetHtmlFormatType().ToFormatEtc().cfFormat))
454 types->push_back(base::UTF8ToUTF16(kMimeTypeHTML));
455 if (::IsClipboardFormatAvailable(GetRtfFormatType().ToFormatEtc().cfFormat))
456 types->push_back(base::UTF8ToUTF16(kMimeTypeRTF));
457 if (::IsClipboardFormatAvailable(CF_DIB))
458 types->push_back(base::UTF8ToUTF16(kMimeTypePNG));
459 *contains_filenames = false;
461 // Acquire the clipboard.
462 ScopedClipboard clipboard;
463 if (!clipboard.Acquire(GetClipboardWindow()))
464 return;
466 HANDLE hdata =
467 ::GetClipboardData(GetWebCustomDataFormatType().ToFormatEtc().cfFormat);
468 if (!hdata)
469 return;
471 ReadCustomDataTypes(::GlobalLock(hdata), ::GlobalSize(hdata), types);
472 ::GlobalUnlock(hdata);
475 void ClipboardWin::ReadText(ClipboardType type, base::string16* result) const {
476 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
477 if (!result) {
478 NOTREACHED();
479 return;
482 result->clear();
484 // Acquire the clipboard.
485 ScopedClipboard clipboard;
486 if (!clipboard.Acquire(GetClipboardWindow()))
487 return;
489 HANDLE data = ::GetClipboardData(CF_UNICODETEXT);
490 if (!data)
491 return;
493 result->assign(static_cast<const base::char16*>(::GlobalLock(data)));
494 ::GlobalUnlock(data);
497 void ClipboardWin::ReadAsciiText(ClipboardType type,
498 std::string* result) const {
499 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
500 if (!result) {
501 NOTREACHED();
502 return;
505 result->clear();
507 // Acquire the clipboard.
508 ScopedClipboard clipboard;
509 if (!clipboard.Acquire(GetClipboardWindow()))
510 return;
512 HANDLE data = ::GetClipboardData(CF_TEXT);
513 if (!data)
514 return;
516 result->assign(static_cast<const char*>(::GlobalLock(data)));
517 ::GlobalUnlock(data);
520 void ClipboardWin::ReadHTML(ClipboardType type,
521 base::string16* markup,
522 std::string* src_url,
523 uint32* fragment_start,
524 uint32* fragment_end) const {
525 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
527 markup->clear();
528 // TODO(dcheng): Remove these checks, I don't think they should be optional.
529 DCHECK(src_url);
530 if (src_url)
531 src_url->clear();
532 *fragment_start = 0;
533 *fragment_end = 0;
535 // Acquire the clipboard.
536 ScopedClipboard clipboard;
537 if (!clipboard.Acquire(GetClipboardWindow()))
538 return;
540 HANDLE data = ::GetClipboardData(GetHtmlFormatType().ToFormatEtc().cfFormat);
541 if (!data)
542 return;
544 std::string cf_html(static_cast<const char*>(::GlobalLock(data)));
545 ::GlobalUnlock(data);
547 size_t html_start = std::string::npos;
548 size_t start_index = std::string::npos;
549 size_t end_index = std::string::npos;
550 ClipboardUtil::CFHtmlExtractMetadata(cf_html, src_url, &html_start,
551 &start_index, &end_index);
553 // This might happen if the contents of the clipboard changed and CF_HTML is
554 // no longer available.
555 if (start_index == std::string::npos ||
556 end_index == std::string::npos ||
557 html_start == std::string::npos)
558 return;
560 if (start_index < html_start || end_index < start_index)
561 return;
563 std::vector<size_t> offsets;
564 offsets.push_back(start_index - html_start);
565 offsets.push_back(end_index - html_start);
566 markup->assign(base::UTF8ToUTF16AndAdjustOffsets(cf_html.data() + html_start,
567 &offsets));
568 *fragment_start = base::checked_cast<uint32>(offsets[0]);
569 *fragment_end = base::checked_cast<uint32>(offsets[1]);
572 void ClipboardWin::ReadRTF(ClipboardType type, std::string* result) const {
573 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
575 ReadData(GetRtfFormatType(), result);
578 SkBitmap ClipboardWin::ReadImage(ClipboardType type) const {
579 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
581 // Acquire the clipboard.
582 ScopedClipboard clipboard;
583 if (!clipboard.Acquire(GetClipboardWindow()))
584 return SkBitmap();
586 // We use a DIB rather than a DDB here since ::GetObject() with the
587 // HBITMAP returned from ::GetClipboardData(CF_BITMAP) always reports a color
588 // depth of 32bpp.
589 BITMAPINFO* bitmap = static_cast<BITMAPINFO*>(::GetClipboardData(CF_DIB));
590 if (!bitmap)
591 return SkBitmap();
592 int color_table_length = 0;
593 switch (bitmap->bmiHeader.biBitCount) {
594 case 1:
595 case 4:
596 case 8:
597 color_table_length = bitmap->bmiHeader.biClrUsed
598 ? bitmap->bmiHeader.biClrUsed
599 : 1 << bitmap->bmiHeader.biBitCount;
600 break;
601 case 16:
602 case 32:
603 if (bitmap->bmiHeader.biCompression == BI_BITFIELDS)
604 color_table_length = 3;
605 break;
606 case 24:
607 break;
608 default:
609 NOTREACHED();
611 const void* bitmap_bits = reinterpret_cast<const char*>(bitmap)
612 + bitmap->bmiHeader.biSize + color_table_length * sizeof(RGBQUAD);
614 gfx::Canvas canvas(gfx::Size(bitmap->bmiHeader.biWidth,
615 bitmap->bmiHeader.biHeight),
616 1.0f,
617 false);
619 skia::ScopedPlatformPaint scoped_platform_paint(canvas.sk_canvas());
620 HDC dc = scoped_platform_paint.GetPlatformSurface();
621 ::SetDIBitsToDevice(dc, 0, 0, bitmap->bmiHeader.biWidth,
622 bitmap->bmiHeader.biHeight, 0, 0, 0,
623 bitmap->bmiHeader.biHeight, bitmap_bits, bitmap,
624 DIB_RGB_COLORS);
626 // Windows doesn't really handle alpha channels well in many situations. When
627 // the source image is < 32 bpp, we force the bitmap to be opaque. When the
628 // source image is 32 bpp, the alpha channel might still contain garbage data.
629 // Since Windows uses premultiplied alpha, we scan for instances where
630 // (R, G, B) > A. If there are any invalid premultiplied colors in the image,
631 // we assume the alpha channel contains garbage and force the bitmap to be
632 // opaque as well. Note that this heuristic will fail on a transparent bitmap
633 // containing only black pixels...
634 const SkBitmap& device_bitmap =
635 canvas.sk_canvas()->getDevice()->accessBitmap(true);
637 SkAutoLockPixels lock(device_bitmap);
638 bool has_invalid_alpha_channel = bitmap->bmiHeader.biBitCount < 32 ||
639 BitmapHasInvalidPremultipliedColors(device_bitmap);
640 if (has_invalid_alpha_channel) {
641 MakeBitmapOpaque(device_bitmap);
645 return canvas.ExtractImageRep().sk_bitmap();
648 void ClipboardWin::ReadCustomData(ClipboardType clipboard_type,
649 const base::string16& type,
650 base::string16* result) const {
651 DCHECK_EQ(clipboard_type, CLIPBOARD_TYPE_COPY_PASTE);
653 // Acquire the clipboard.
654 ScopedClipboard clipboard;
655 if (!clipboard.Acquire(GetClipboardWindow()))
656 return;
658 HANDLE hdata =
659 ::GetClipboardData(GetWebCustomDataFormatType().ToFormatEtc().cfFormat);
660 if (!hdata)
661 return;
663 ReadCustomDataForType(::GlobalLock(hdata), ::GlobalSize(hdata), type, result);
664 ::GlobalUnlock(hdata);
667 void ClipboardWin::ReadBookmark(base::string16* title, std::string* url) const {
668 if (title)
669 title->clear();
671 if (url)
672 url->clear();
674 // Acquire the clipboard.
675 ScopedClipboard clipboard;
676 if (!clipboard.Acquire(GetClipboardWindow()))
677 return;
679 HANDLE data = ::GetClipboardData(GetUrlWFormatType().ToFormatEtc().cfFormat);
680 if (!data)
681 return;
683 base::string16 bookmark(static_cast<const base::char16*>(::GlobalLock(data)));
684 ::GlobalUnlock(data);
686 ParseBookmarkClipboardFormat(bookmark, title, url);
689 void ClipboardWin::ReadData(const FormatType& format,
690 std::string* result) const {
691 if (!result) {
692 NOTREACHED();
693 return;
696 ScopedClipboard clipboard;
697 if (!clipboard.Acquire(GetClipboardWindow()))
698 return;
700 HANDLE data = ::GetClipboardData(format.ToFormatEtc().cfFormat);
701 if (!data)
702 return;
704 result->assign(static_cast<const char*>(::GlobalLock(data)),
705 ::GlobalSize(data));
706 ::GlobalUnlock(data);
709 void ClipboardWin::WriteObjects(ClipboardType type, const ObjectMap& objects) {
710 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
712 ScopedClipboard clipboard;
713 if (!clipboard.Acquire(GetClipboardWindow()))
714 return;
716 ::EmptyClipboard();
718 for (ObjectMap::const_iterator iter = objects.begin(); iter != objects.end();
719 ++iter) {
720 DispatchObject(static_cast<ObjectType>(iter->first), iter->second);
724 void ClipboardWin::WriteText(const char* text_data, size_t text_len) {
725 base::string16 text;
726 base::UTF8ToUTF16(text_data, text_len, &text);
727 HGLOBAL glob = CreateGlobalData(text);
729 WriteToClipboard(CF_UNICODETEXT, glob);
732 void ClipboardWin::WriteHTML(const char* markup_data,
733 size_t markup_len,
734 const char* url_data,
735 size_t url_len) {
736 std::string markup(markup_data, markup_len);
737 std::string url;
739 if (url_len > 0)
740 url.assign(url_data, url_len);
742 std::string html_fragment = ClipboardUtil::HtmlToCFHtml(markup, url);
743 HGLOBAL glob = CreateGlobalData(html_fragment);
745 WriteToClipboard(Clipboard::GetHtmlFormatType().ToFormatEtc().cfFormat, glob);
748 void ClipboardWin::WriteRTF(const char* rtf_data, size_t data_len) {
749 WriteData(GetRtfFormatType(), rtf_data, data_len);
752 void ClipboardWin::WriteBookmark(const char* title_data,
753 size_t title_len,
754 const char* url_data,
755 size_t url_len) {
756 std::string bookmark(title_data, title_len);
757 bookmark.append(1, L'\n');
758 bookmark.append(url_data, url_len);
760 base::string16 wide_bookmark = base::UTF8ToUTF16(bookmark);
761 HGLOBAL glob = CreateGlobalData(wide_bookmark);
763 WriteToClipboard(GetUrlWFormatType().ToFormatEtc().cfFormat, glob);
766 void ClipboardWin::WriteWebSmartPaste() {
767 DCHECK(clipboard_owner_->hwnd() != NULL);
768 ::SetClipboardData(GetWebKitSmartPasteFormatType().ToFormatEtc().cfFormat,
769 NULL);
772 void ClipboardWin::WriteBitmap(const SkBitmap& bitmap) {
773 HDC dc = ::GetDC(NULL);
775 // This doesn't actually cost us a memcpy when the bitmap comes from the
776 // renderer as we load it into the bitmap using setPixels which just sets a
777 // pointer. Someone has to memcpy it into GDI, it might as well be us here.
779 // TODO(darin): share data in gfx/bitmap_header.cc somehow
780 BITMAPINFO bm_info = {};
781 bm_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
782 bm_info.bmiHeader.biWidth = bitmap.width();
783 bm_info.bmiHeader.biHeight = -bitmap.height(); // sets vertical orientation
784 bm_info.bmiHeader.biPlanes = 1;
785 bm_info.bmiHeader.biBitCount = 32;
786 bm_info.bmiHeader.biCompression = BI_RGB;
788 // ::CreateDIBSection allocates memory for us to copy our bitmap into.
789 // Unfortunately, we can't write the created bitmap to the clipboard,
790 // (see http://msdn2.microsoft.com/en-us/library/ms532292.aspx)
791 void* bits;
792 HBITMAP source_hbitmap =
793 ::CreateDIBSection(dc, &bm_info, DIB_RGB_COLORS, &bits, NULL, 0);
795 if (bits && source_hbitmap) {
797 SkAutoLockPixels bitmap_lock(bitmap);
798 // Copy the bitmap out of shared memory and into GDI
799 memcpy(bits, bitmap.getPixels(), bitmap.getSize());
802 // Now we have an HBITMAP, we can write it to the clipboard
803 WriteBitmapFromHandle(source_hbitmap,
804 gfx::Size(bitmap.width(), bitmap.height()));
807 ::DeleteObject(source_hbitmap);
808 ::ReleaseDC(NULL, dc);
811 void ClipboardWin::WriteData(const FormatType& format,
812 const char* data_data,
813 size_t data_len) {
814 HGLOBAL hdata = ::GlobalAlloc(GMEM_MOVEABLE, data_len);
815 if (!hdata)
816 return;
818 char* data = static_cast<char*>(::GlobalLock(hdata));
819 memcpy(data, data_data, data_len);
820 ::GlobalUnlock(data);
821 WriteToClipboard(format.ToFormatEtc().cfFormat, hdata);
824 void ClipboardWin::WriteBitmapFromHandle(HBITMAP source_hbitmap,
825 const gfx::Size& size) {
826 // We would like to just call ::SetClipboardData on the source_hbitmap,
827 // but that bitmap might not be of a sort we can write to the clipboard.
828 // For this reason, we create a new bitmap, copy the bits over, and then
829 // write that to the clipboard.
831 HDC dc = ::GetDC(NULL);
832 HDC compatible_dc = ::CreateCompatibleDC(NULL);
833 HDC source_dc = ::CreateCompatibleDC(NULL);
835 // This is the HBITMAP we will eventually write to the clipboard
836 HBITMAP hbitmap = ::CreateCompatibleBitmap(dc, size.width(), size.height());
837 if (!hbitmap) {
838 // Failed to create the bitmap
839 ::DeleteDC(compatible_dc);
840 ::DeleteDC(source_dc);
841 ::ReleaseDC(NULL, dc);
842 return;
845 HBITMAP old_hbitmap = (HBITMAP)SelectObject(compatible_dc, hbitmap);
846 HBITMAP old_source = (HBITMAP)SelectObject(source_dc, source_hbitmap);
848 // Now we need to blend it into an HBITMAP we can place on the clipboard
849 BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
850 ::GdiAlphaBlend(compatible_dc,
853 size.width(),
854 size.height(),
855 source_dc,
858 size.width(),
859 size.height(),
860 bf);
862 // Clean up all the handles we just opened
863 ::SelectObject(compatible_dc, old_hbitmap);
864 ::SelectObject(source_dc, old_source);
865 ::DeleteObject(old_hbitmap);
866 ::DeleteObject(old_source);
867 ::DeleteDC(compatible_dc);
868 ::DeleteDC(source_dc);
869 ::ReleaseDC(NULL, dc);
871 WriteToClipboard(CF_BITMAP, hbitmap);
874 void ClipboardWin::WriteToClipboard(unsigned int format, HANDLE handle) {
875 DCHECK(clipboard_owner_->hwnd() != NULL);
876 if (handle && !::SetClipboardData(format, handle)) {
877 DCHECK(ERROR_CLIPBOARD_NOT_OPEN != GetLastError());
878 FreeData(format, handle);
882 HWND ClipboardWin::GetClipboardWindow() const {
883 if (!clipboard_owner_)
884 return NULL;
886 if (clipboard_owner_->hwnd() == NULL)
887 clipboard_owner_->Create(base::Bind(&ClipboardOwnerWndProc));
889 return clipboard_owner_->hwnd();
892 } // namespace ui