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