Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / ui / base / clipboard / clipboard_mac.mm
blob62331947ddf0d4f1118cb3c19b960faad8d3fecb
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 #include "ui/base/clipboard/clipboard_mac.h"
7 #import <Cocoa/Cocoa.h>
9 #include "base/basictypes.h"
10 #include "base/files/file_path.h"
11 #include "base/logging.h"
12 #include "base/mac/mac_util.h"
13 #include "base/mac/scoped_cftyperef.h"
14 #include "base/mac/scoped_nsobject.h"
15 #include "base/stl_util.h"
16 #include "base/strings/sys_string_conversions.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "skia/ext/skia_utils_mac.h"
19 #import "third_party/mozilla/NSPasteboard+Utils.h"
20 #include "third_party/skia/include/core/SkBitmap.h"
21 #include "ui/base/clipboard/custom_data_helper.h"
22 #include "ui/gfx/canvas.h"
23 #include "ui/gfx/geometry/size.h"
24 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
26 namespace ui {
28 namespace {
30 // Would be nice if this were in UTCoreTypes.h, but it isn't
31 NSString* const kUTTypeURLName = @"public.url-name";
33 // Tells us if WebKit was the last to write to the pasteboard. There's no
34 // actual data associated with this type.
35 NSString* const kWebSmartPastePboardType = @"NeXT smart paste pasteboard type";
37 // Pepper custom data format type.
38 NSString* const kPepperCustomDataPboardType =
39     @"org.chromium.pepper-custom-data";
41 NSPasteboard* GetPasteboard() {
42   // The pasteboard should not be nil in a UI session, but this handy DCHECK
43   // can help track down problems if someone tries using clipboard code outside
44   // of a UI session.
45   NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
46   DCHECK(pasteboard);
47   return pasteboard;
50 }  // namespace
52 // Clipboard::FormatType implementation.
53 Clipboard::FormatType::FormatType() : data_(nil) {
56 Clipboard::FormatType::FormatType(NSString* native_format)
57     : data_([native_format retain]) {
60 Clipboard::FormatType::FormatType(const FormatType& other)
61     : data_([other.data_ retain]) {
64 Clipboard::FormatType& Clipboard::FormatType::operator=(
65     const FormatType& other) {
66   if (this != &other) {
67     [data_ release];
68     data_ = [other.data_ retain];
69   }
70   return *this;
73 bool Clipboard::FormatType::Equals(const FormatType& other) const {
74   return [data_ isEqualToString:other.data_];
77 Clipboard::FormatType::~FormatType() {
78   [data_ release];
81 std::string Clipboard::FormatType::Serialize() const {
82   return base::SysNSStringToUTF8(data_);
85 // static
86 Clipboard::FormatType Clipboard::FormatType::Deserialize(
87     const std::string& serialization) {
88   return FormatType(base::SysUTF8ToNSString(serialization));
91 bool Clipboard::FormatType::operator<(const FormatType& other) const {
92   return [data_ compare:other.data_] == NSOrderedAscending;
95 // Various predefined FormatTypes.
96 // static
97 Clipboard::FormatType Clipboard::GetFormatType(
98     const std::string& format_string) {
99   return FormatType::Deserialize(format_string);
102 // static
103 const Clipboard::FormatType& Clipboard::GetUrlFormatType() {
104   CR_DEFINE_STATIC_LOCAL(FormatType, type, (NSURLPboardType));
105   return type;
108 // static
109 const Clipboard::FormatType& Clipboard::GetUrlWFormatType() {
110   return GetUrlFormatType();
113 // static
114 const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() {
115   CR_DEFINE_STATIC_LOCAL(FormatType, type, (NSStringPboardType));
116   return type;
119 // static
120 const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() {
121   return GetPlainTextFormatType();
124 // static
125 const Clipboard::FormatType& Clipboard::GetFilenameFormatType() {
126   CR_DEFINE_STATIC_LOCAL(FormatType, type, (NSFilenamesPboardType));
127   return type;
130 // static
131 const Clipboard::FormatType& Clipboard::GetFilenameWFormatType() {
132   return GetFilenameFormatType();
135 // static
136 const Clipboard::FormatType& Clipboard::GetHtmlFormatType() {
137   CR_DEFINE_STATIC_LOCAL(FormatType, type, (NSHTMLPboardType));
138   return type;
141 // static
142 const Clipboard::FormatType& Clipboard::GetRtfFormatType() {
143   CR_DEFINE_STATIC_LOCAL(FormatType, type, (NSRTFPboardType));
144   return type;
147 // static
148 const Clipboard::FormatType& Clipboard::GetBitmapFormatType() {
149   CR_DEFINE_STATIC_LOCAL(FormatType, type, (NSTIFFPboardType));
150   return type;
153 // static
154 const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() {
155   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kWebSmartPastePboardType));
156   return type;
159 // static
160 const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() {
161   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kWebCustomDataPboardType));
162   return type;
165 // static
166 const Clipboard::FormatType& Clipboard::GetPepperCustomDataFormatType() {
167   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kPepperCustomDataPboardType));
168   return type;
171 // Clipboard factory method.
172 // static
173 Clipboard* Clipboard::Create() {
174   return new ClipboardMac;
177 // ClipboardMac implementation.
178 ClipboardMac::ClipboardMac() {
179   DCHECK(CalledOnValidThread());
182 ClipboardMac::~ClipboardMac() {
183   DCHECK(CalledOnValidThread());
186 uint64 ClipboardMac::GetSequenceNumber(ClipboardType type) const {
187   DCHECK(CalledOnValidThread());
188   DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
190   NSPasteboard* pb = GetPasteboard();
191   return [pb changeCount];
194 bool ClipboardMac::IsFormatAvailable(const FormatType& format,
195                                      ClipboardType type) const {
196   DCHECK(CalledOnValidThread());
197   DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
199   NSPasteboard* pb = GetPasteboard();
200   NSArray* types = [pb types];
202   // Safari only places RTF on the pasteboard, never HTML. We can convert RTF
203   // to HTML, so the presence of either indicates success when looking for HTML.
204   if ([format.ToNSString() isEqualToString:NSHTMLPboardType]) {
205     return [types containsObject:NSHTMLPboardType] ||
206            [types containsObject:NSRTFPboardType];
207   }
208   return [types containsObject:format.ToNSString()];
211 void ClipboardMac::Clear(ClipboardType type) {
212   DCHECK(CalledOnValidThread());
213   DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
215   NSPasteboard* pb = GetPasteboard();
216   [pb declareTypes:[NSArray array] owner:nil];
219 void ClipboardMac::ReadAvailableTypes(ClipboardType type,
220                                       std::vector<base::string16>* types,
221                                       bool* contains_filenames) const {
222   DCHECK(CalledOnValidThread());
223   types->clear();
224   if (IsFormatAvailable(Clipboard::GetPlainTextFormatType(), type))
225     types->push_back(base::UTF8ToUTF16(kMimeTypeText));
226   if (IsFormatAvailable(Clipboard::GetHtmlFormatType(), type))
227     types->push_back(base::UTF8ToUTF16(kMimeTypeHTML));
228   if (IsFormatAvailable(Clipboard::GetRtfFormatType(), type))
229     types->push_back(base::UTF8ToUTF16(kMimeTypeRTF));
230   if ([NSImage canInitWithPasteboard:GetPasteboard()])
231     types->push_back(base::UTF8ToUTF16(kMimeTypePNG));
232   *contains_filenames = false;
234   NSPasteboard* pb = GetPasteboard();
235   if ([[pb types] containsObject:kWebCustomDataPboardType]) {
236     NSData* data = [pb dataForType:kWebCustomDataPboardType];
237     if ([data length])
238       ReadCustomDataTypes([data bytes], [data length], types);
239   }
242 void ClipboardMac::ReadText(ClipboardType type, base::string16* result) const {
243   DCHECK(CalledOnValidThread());
244   DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
245   NSPasteboard* pb = GetPasteboard();
246   NSString* contents = [pb stringForType:NSStringPboardType];
248   *result = base::SysNSStringToUTF16(contents);
251 void ClipboardMac::ReadAsciiText(ClipboardType type,
252                                  std::string* result) const {
253   DCHECK(CalledOnValidThread());
254   DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
255   NSPasteboard* pb = GetPasteboard();
256   NSString* contents = [pb stringForType:NSStringPboardType];
258   if (!contents)
259     result->clear();
260   else
261     result->assign([contents UTF8String]);
264 void ClipboardMac::ReadHTML(ClipboardType type,
265                             base::string16* markup,
266                             std::string* src_url,
267                             uint32* fragment_start,
268                             uint32* fragment_end) const {
269   DCHECK(CalledOnValidThread());
270   DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
272   // TODO(avi): src_url?
273   markup->clear();
274   if (src_url)
275     src_url->clear();
277   NSPasteboard* pb = GetPasteboard();
278   NSArray* supportedTypes = [NSArray arrayWithObjects:NSHTMLPboardType,
279                                                       NSRTFPboardType,
280                                                       NSStringPboardType,
281                                                       nil];
282   NSString* bestType = [pb availableTypeFromArray:supportedTypes];
283   if (bestType) {
284     NSString* contents = [pb stringForType:bestType];
285     if ([bestType isEqualToString:NSRTFPboardType])
286       contents = [pb htmlFromRtf];
287     *markup = base::SysNSStringToUTF16(contents);
288   }
290   *fragment_start = 0;
291   DCHECK(markup->length() <= kuint32max);
292   *fragment_end = static_cast<uint32>(markup->length());
295 void ClipboardMac::ReadRTF(ClipboardType type, std::string* result) const {
296   DCHECK(CalledOnValidThread());
297   DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
299   return ReadData(GetRtfFormatType(), result);
302 SkBitmap ClipboardMac::ReadImage(ClipboardType type) const {
303   DCHECK(CalledOnValidThread());
304   DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
306   // If the pasteboard's image data is not to its liking, the guts of NSImage
307   // may throw, and that exception will leak. Prevent a crash in that case;
308   // a blank image is better.
309   base::scoped_nsobject<NSImage> image;
310   @try {
311     image.reset([[NSImage alloc] initWithPasteboard:GetPasteboard()]);
312   } @catch (id exception) {
313   }
315   SkBitmap bitmap;
316   if (image.get()) {
317     bitmap = gfx::NSImageToSkBitmapWithColorSpace(
318         image.get(), /*is_opaque=*/ false, base::mac::GetSystemColorSpace());
319   }
320   return bitmap;
323 void ClipboardMac::ReadCustomData(ClipboardType clipboard_type,
324                                   const base::string16& type,
325                                   base::string16* result) const {
326   DCHECK(CalledOnValidThread());
327   DCHECK_EQ(clipboard_type, CLIPBOARD_TYPE_COPY_PASTE);
329   NSPasteboard* pb = GetPasteboard();
330   if ([[pb types] containsObject:kWebCustomDataPboardType]) {
331     NSData* data = [pb dataForType:kWebCustomDataPboardType];
332     if ([data length])
333       ReadCustomDataForType([data bytes], [data length], type, result);
334   }
337 void ClipboardMac::ReadBookmark(base::string16* title, std::string* url) const {
338   DCHECK(CalledOnValidThread());
339   NSPasteboard* pb = GetPasteboard();
341   if (title) {
342     NSString* contents = [pb stringForType:kUTTypeURLName];
343     *title = base::SysNSStringToUTF16(contents);
344   }
346   if (url) {
347     NSString* url_string = [[NSURL URLFromPasteboard:pb] absoluteString];
348     if (!url_string)
349       url->clear();
350     else
351       url->assign([url_string UTF8String]);
352   }
355 void ClipboardMac::ReadData(const FormatType& format,
356                             std::string* result) const {
357   DCHECK(CalledOnValidThread());
358   NSPasteboard* pb = GetPasteboard();
359   NSData* data = [pb dataForType:format.ToNSString()];
360   if ([data length])
361     result->assign(static_cast<const char*>([data bytes]), [data length]);
364 void ClipboardMac::WriteObjects(ClipboardType type, const ObjectMap& objects) {
365   DCHECK(CalledOnValidThread());
366   DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
368   NSPasteboard* pb = GetPasteboard();
369   [pb declareTypes:[NSArray array] owner:nil];
371   for (ObjectMap::const_iterator iter = objects.begin(); iter != objects.end();
372        ++iter) {
373     DispatchObject(static_cast<ObjectType>(iter->first), iter->second);
374   }
377 void ClipboardMac::WriteText(const char* text_data, size_t text_len) {
378   std::string text_str(text_data, text_len);
379   NSString* text = base::SysUTF8ToNSString(text_str);
380   NSPasteboard* pb = GetPasteboard();
381   [pb addTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
382   [pb setString:text forType:NSStringPboardType];
385 void ClipboardMac::WriteHTML(const char* markup_data,
386                              size_t markup_len,
387                              const char* url_data,
388                              size_t url_len) {
389   // We need to mark it as utf-8. (see crbug.com/11957)
390   std::string html_fragment_str("<meta charset='utf-8'>");
391   html_fragment_str.append(markup_data, markup_len);
392   NSString* html_fragment = base::SysUTF8ToNSString(html_fragment_str);
394   // TODO(avi): url_data?
395   NSPasteboard* pb = GetPasteboard();
396   [pb addTypes:[NSArray arrayWithObject:NSHTMLPboardType] owner:nil];
397   [pb setString:html_fragment forType:NSHTMLPboardType];
400 void ClipboardMac::WriteRTF(const char* rtf_data, size_t data_len) {
401   WriteData(GetRtfFormatType(), rtf_data, data_len);
404 void ClipboardMac::WriteBookmark(const char* title_data,
405                                  size_t title_len,
406                                  const char* url_data,
407                                  size_t url_len) {
408   std::string title_str(title_data, title_len);
409   NSString* title = base::SysUTF8ToNSString(title_str);
410   std::string url_str(url_data, url_len);
411   NSString* url = base::SysUTF8ToNSString(url_str);
413   // TODO(playmobil): In the Windows version of this function, an HTML
414   // representation of the bookmark is also added to the clipboard, to support
415   // drag and drop of web shortcuts.  I don't think we need to do this on the
416   // Mac, but we should double check later on.
417   NSURL* nsurl = [NSURL URLWithString:url];
419   NSPasteboard* pb = GetPasteboard();
420   // passing UTIs into the pasteboard methods is valid >= 10.5
421   [pb addTypes:[NSArray arrayWithObjects:NSURLPboardType, kUTTypeURLName, nil]
422          owner:nil];
423   [nsurl writeToPasteboard:pb];
424   [pb setString:title forType:kUTTypeURLName];
427 void ClipboardMac::WriteBitmap(const SkBitmap& bitmap) {
428   NSImage* image = gfx::SkBitmapToNSImageWithColorSpace(
429       bitmap, base::mac::GetSystemColorSpace());
430   // An API to ask the NSImage to write itself to the clipboard comes in 10.6 :(
431   // For now, spit out the image as a TIFF.
432   NSPasteboard* pb = GetPasteboard();
433   [pb addTypes:[NSArray arrayWithObject:NSTIFFPboardType] owner:nil];
434   NSData* tiff_data = [image TIFFRepresentation];
435   LOG_IF(ERROR, tiff_data == NULL) << "Failed to allocate image for clipboard";
436   if (tiff_data) {
437     [pb setData:tiff_data forType:NSTIFFPboardType];
438   }
441 void ClipboardMac::WriteData(const FormatType& format,
442                              const char* data_data,
443                              size_t data_len) {
444   NSPasteboard* pb = GetPasteboard();
445   [pb addTypes:[NSArray arrayWithObject:format.ToNSString()] owner:nil];
446   [pb setData:[NSData dataWithBytes:data_data length:data_len]
447       forType:format.ToNSString()];
450 // Write an extra flavor that signifies WebKit was the last to modify the
451 // pasteboard. This flavor has no data.
452 void ClipboardMac::WriteWebSmartPaste() {
453   NSPasteboard* pb = GetPasteboard();
454   NSString* format = GetWebKitSmartPasteFormatType().ToNSString();
455   [pb addTypes:[NSArray arrayWithObject:format] owner:nil];
456   [pb setData:nil forType:format];
459 }  // namespace ui