Bug 938991 - text/rtf support for clipboard data. r=enndeakin
authorJorg K <mozilla@jorgk.com>
Mon, 23 Nov 2015 11:04:26 -0800
changeset 273801 0c34cc2dc429d2ea06415dc977def64d1a7ac72a
parent 273800 e1b5927361ac2fbcb7075c19061d1b73a70487dc
child 273802 c728b4bd9be62afcfda73eba03edc3bd07aee33f
push id68389
push userkwierso@gmail.com
push dateMon, 23 Nov 2015 19:19:12 +0000
treeherdermozilla-inbound@be19277b423f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersenndeakin
bugs938991
milestone45.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 938991 - text/rtf support for clipboard data. r=enndeakin
dom/events/DataTransfer.cpp
widget/cocoa/nsClipboard.mm
widget/cocoa/nsDragService.mm
widget/nsClipboardProxy.cpp
widget/nsITransferable.idl
widget/nsPrimitiveHelpers.cpp
widget/windows/nsClipboard.cpp
--- a/dom/events/DataTransfer.cpp
+++ b/dom/events/DataTransfer.cpp
@@ -1232,17 +1232,17 @@ DataTransfer::CacheExternalDragFormats()
   // make sure that the system principal is used for external drags
   nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
   nsCOMPtr<nsIPrincipal> sysPrincipal;
   ssm->GetSystemPrincipal(getter_AddRefs(sysPrincipal));
 
   // there isn't a way to get a list of the formats that might be available on
   // all platforms, so just check for the types that can actually be imported
   // XXXndeakin there are some other formats but those are platform specific.
-  const char* formats[] = { kFileMime, kHTMLMime, kURLMime, kURLDataMime, kUnicodeMime };
+  const char* formats[] = { kFileMime, kHTMLMime, kRTFMime, kURLMime, kURLDataMime, kUnicodeMime };
 
   uint32_t count;
   dragSession->GetNumDropItems(&count);
   for (uint32_t c = 0; c < count; c++) {
     for (uint32_t f = 0; f < ArrayLength(formats); f++) {
       // IsDataFlavorSupported doesn't take an index as an argument and just
       // checks if any of the items support a particular flavor, even though
       // the GetData method does take an index. Here, we just assume that
@@ -1274,17 +1274,17 @@ DataTransfer::CacheExternalClipboardForm
   }
 
   nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
   nsCOMPtr<nsIPrincipal> sysPrincipal;
   ssm->GetSystemPrincipal(getter_AddRefs(sysPrincipal));
 
   // there isn't a way to get a list of the formats that might be available on
   // all platforms, so just check for the types that can actually be imported
-  const char* formats[] = { kFileMime, kHTMLMime, kURLMime, kURLDataMime, kUnicodeMime };
+  const char* formats[] = { kFileMime, kHTMLMime, kRTFMime, kURLMime, kURLDataMime, kUnicodeMime };
 
   for (uint32_t f = 0; f < mozilla::ArrayLength(formats); ++f) {
     // check each format one at a time
     bool supported;
     clipboard->HasDataMatchingFlavors(&(formats[f]), 1, mClipboardType, &supported);
     // if the format is supported, add an item to the array with null as
     // the data. When retrieved, GetRealData will read the data.
     if (supported) {
--- a/widget/cocoa/nsClipboard.mm
+++ b/widget/cocoa/nsClipboard.mm
@@ -148,17 +148,22 @@ nsClipboard::TransferableFromPasteboard(
     // printf("looking for clipboard data of type %s\n", flavorStr.get());
 
     NSString *pboardType = nil;
     if (nsClipboard::IsStringType(flavorStr, &pboardType)) {
       NSString* pString = [cocoaPasteboard stringForType:pboardType];
       if (!pString)
         continue;
 
-      NSData* stringData = [pString dataUsingEncoding:NSUnicodeStringEncoding];
+      NSData* stringData;
+      if ([pboardType isEqualToString:NSRTFPboardType]) {
+        stringData = [pString dataUsingEncoding:NSASCIIStringEncoding];
+      } else {
+        stringData = [pString dataUsingEncoding:NSUnicodeStringEncoding];
+      }
       unsigned int dataLength = [stringData length];
       void* clipboardDataPtr = malloc(dataLength);
       if (!clipboardDataPtr)
         return NS_ERROR_OUT_OF_MEMORY;
       [stringData getBytes:clipboardDataPtr];
 
       // The DOM only wants LF, so convert from MacOS line endings to DOM line endings.
       int32_t signedDataLength = dataLength;
@@ -576,22 +581,24 @@ nsClipboard::PasteboardDictFromTransfera
 
   return pasteboardOutputDict;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 bool nsClipboard::IsStringType(const nsCString& aMIMEType, NSString** aPasteboardType)
 {
-  if (aMIMEType.EqualsLiteral(kUnicodeMime) ||
-      aMIMEType.EqualsLiteral(kHTMLMime)) {
-    if (aMIMEType.EqualsLiteral(kUnicodeMime))
-      *aPasteboardType = NSStringPboardType;
-    else
-      *aPasteboardType = NSHTMLPboardType;
+  if (aMIMEType.EqualsLiteral(kUnicodeMime)) {
+    *aPasteboardType = NSStringPboardType;
+    return true;
+  } else if (aMIMEType.EqualsLiteral(kRTFMime)) {
+    *aPasteboardType = NSRTFPboardType;
+    return true;
+  } else if (aMIMEType.EqualsLiteral(kHTMLMime)) {
+    *aPasteboardType = NSHTMLPboardType;
     return true;
   } else {
     return false;
   }
 }
 
 NSString* nsClipboard::WrapHtmlForSystemPasteboard(NSString* aString)
 {
--- a/widget/cocoa/nsDragService.mm
+++ b/widget/cocoa/nsDragService.mm
@@ -488,19 +488,26 @@ nsDragService::GetData(nsITransferable* 
           title = pString;
         }
         pString = [NSString stringWithFormat:@"%@\n%@", pString, title];
       }
     } else if (flavorStr.EqualsLiteral(kURLDataMime)) {
       pString = GetStringForType(item, (const NSString*)kUTTypeURL);
     } else if (flavorStr.EqualsLiteral(kURLDescriptionMime)) {
       pString = GetTitleForURL(item);
+    } else if (flavorStr.EqualsLiteral(kRTFMime)) {
+      pString = GetStringForType(item, (const NSString*)kUTTypeRTF);
     }
     if (pString) {
-      NSData* stringData = [pString dataUsingEncoding:NSUnicodeStringEncoding];
+      NSData* stringData;
+      if (flavorStr.EqualsLiteral(kRTFMime)) {
+        stringData = [pString dataUsingEncoding:NSASCIIStringEncoding];
+      } else {
+        stringData = [pString dataUsingEncoding:NSUnicodeStringEncoding];
+      }
       unsigned int dataLength = [stringData length];
       void* clipboardDataPtr = malloc(dataLength);
       if (!clipboardDataPtr)
         return NS_ERROR_OUT_OF_MEMORY;
       [stringData getBytes:clipboardDataPtr];
 
       // The DOM only wants LF, so convert from MacOS line endings to DOM line endings.
       int32_t signedDataLength = dataLength;
@@ -596,16 +603,18 @@ nsDragService::IsDataFlavorSupported(con
     type = (const NSString*)kUTTypeUTF8PlainText;
   } else if (dataFlavor.EqualsLiteral(kHTMLMime)) {
     type = (const NSString*)kUTTypeHTML;
   } else if (dataFlavor.EqualsLiteral(kURLMime) ||
              dataFlavor.EqualsLiteral(kURLDataMime)) {
     type = (const NSString*)kUTTypeURL;
   } else if (dataFlavor.EqualsLiteral(kURLDescriptionMime)) {
     type = (const NSString*)kUTTypeURLName;
+  } else if (dataFlavor.EqualsLiteral(kRTFMime)) {
+    type = (const NSString*)kUTTypeRTF;
   }
   NSString* availableType = [globalDragPboard availableTypeFromArray:[NSArray arrayWithObjects:(id)type, nil]];
   if (availableType && IsValidType(availableType, allowFileURL)) {
     *_retval = true;
   }
 
   return NS_OK;
 
--- a/widget/nsClipboardProxy.cpp
+++ b/widget/nsClipboardProxy.cpp
@@ -87,17 +87,18 @@ nsClipboardProxy::GetData(nsITransferabl
           flavor.EqualsLiteral(kJPGImageMime) ||
           flavor.EqualsLiteral(kPNGImageMime) ||
           flavor.EqualsLiteral(kGIFImageMime)) {
         nsCOMPtr<nsIInputStream> stream;
         NS_NewCStringInputStream(getter_AddRefs(stream), item.data().get_nsCString());
 
         rv = aTransferable->SetTransferData(flavor.get(), stream, sizeof(nsISupports*));
         NS_ENSURE_SUCCESS(rv, rv);
-      } else if (flavor.EqualsLiteral(kNativeHTMLMime)) {
+      } else if (flavor.EqualsLiteral(kNativeHTMLMime) ||
+                 flavor.EqualsLiteral(kRTFMime)) {
         nsCOMPtr<nsISupportsCString> dataWrapper =
           do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv);
         NS_ENSURE_SUCCESS(rv, rv);
 
         nsCString data = item.data().get_nsCString();
         rv = dataWrapper->SetData(data);
         NS_ENSURE_SUCCESS(rv, rv);
 
--- a/widget/nsITransferable.idl
+++ b/widget/nsITransferable.idl
@@ -10,16 +10,17 @@
 
 interface nsIDOMNode;
 
 %{ C++
 
 // these probably shouldn't live here, but in some central repository shared
 // by the entire app.
 #define kTextMime                   "text/plain"
+#define kRTFMime                    "text/rtf"
 #define kUnicodeMime                "text/unicode"
 #define kMozTextInternal          "text/x-moz-text-internal"  // text data which isn't suppoed to be parsed by other apps.
 #define kHTMLMime                   "text/html"
 #define kAOLMailMime                "AOLMAIL"
 #define kPNGImageMime               "image/png"
 #define kJPEGImageMime              "image/jpeg"
 #define kJPGImageMime               "image/jpg"
 #define kGIFImageMime               "image/gif"
--- a/widget/nsPrimitiveHelpers.cpp
+++ b/widget/nsPrimitiveHelpers.cpp
@@ -40,17 +40,18 @@
 //
 void
 nsPrimitiveHelpers :: CreatePrimitiveForData ( const char* aFlavor, const void* aDataBuff,
                                                  uint32_t aDataLen, nsISupports** aPrimitive )
 {
   if ( !aPrimitive )
     return;
 
-  if ( strcmp(aFlavor,kTextMime) == 0 || strcmp(aFlavor,kNativeHTMLMime) == 0 ) {
+  if ( strcmp(aFlavor,kTextMime) == 0 || strcmp(aFlavor,kNativeHTMLMime) == 0 ||
+       strcmp(aFlavor,kRTFMime) == 0) {
     nsCOMPtr<nsISupportsCString> primitive =
         do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID);
     if ( primitive ) {
       const char * start = reinterpret_cast<const char*>(aDataBuff);
       primitive->SetData(Substring(start, start + aDataLen));
       NS_ADDREF(*aPrimitive = primitive);
     }
   }
@@ -165,17 +166,17 @@ nsLinebreakHelpers :: ConvertPlatformToD
                                                           int32_t* ioLengthInBytes )
 {
   NS_ASSERTION ( ioData && *ioData && ioLengthInBytes, "Bad Params");
   if ( !(ioData && *ioData && ioLengthInBytes) )
     return NS_ERROR_INVALID_ARG;
 
   nsresult retVal = NS_OK;
 
-  if ( strcmp(inFlavor, "text/plain") == 0 ) {
+  if ( strcmp(inFlavor, kTextMime) == 0 || strcmp(inFlavor, kRTFMime) == 0) {
     char* buffAsChars = reinterpret_cast<char*>(*ioData);
     char* oldBuffer = buffAsChars;
     retVal = nsLinebreakConverter::ConvertLineBreaksInSitu ( &buffAsChars, nsLinebreakConverter::eLinebreakAny,
                                                               nsLinebreakConverter::eLinebreakContent,
                                                               *ioLengthInBytes, ioLengthInBytes );
     if ( NS_SUCCEEDED(retVal) ) {
       if ( buffAsChars != oldBuffer )             // check if buffer was reallocated
         free ( oldBuffer );
--- a/widget/windows/nsClipboard.cpp
+++ b/widget/windows/nsClipboard.cpp
@@ -90,16 +90,18 @@ nsClipboard::Observe(nsISupports *aSubje
 //-------------------------------------------------------------------------
 UINT nsClipboard::GetFormat(const char* aMimeStr)
 {
   UINT format;
 
   if (strcmp(aMimeStr, kTextMime) == 0 ||
       strcmp(aMimeStr, kUnicodeMime) == 0)
     format = CF_UNICODETEXT;
+  else if (strcmp(aMimeStr, kRTFMime) == 0)
+    format = ::RegisterClipboardFormat(L"Rich Text Format");
   else if (strcmp(aMimeStr, kJPEGImageMime) == 0 ||
            strcmp(aMimeStr, kJPGImageMime) == 0 ||
            strcmp(aMimeStr, kPNGImageMime) == 0)
     format = CF_DIBV5;
   else if (strcmp(aMimeStr, kFileMime) == 0 ||
            strcmp(aMimeStr, kFilePromiseMime) == 0)
     format = CF_HDROP;
   else if (strcmp(aMimeStr, kNativeHTMLMime) == 0 ||
@@ -665,16 +667,22 @@ nsresult nsClipboard::GetDataFromDataObj
         }
         else {
           // we probably have some form of text. The DOM only wants LF, so convert from Win32 line 
           // endings to DOM line endings.
           int32_t signedLen = static_cast<int32_t>(dataLen);
           nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks ( flavorStr, &data, &signedLen );
           dataLen = signedLen;
 
+          if (strcmp(flavorStr, kRTFMime) == 0) {
+            // RTF on Windows is known to sometimes deliver an extra null byte.
+            if (dataLen > 0 && static_cast<char*>(data)[dataLen - 1] == '\0')
+              dataLen--;
+          }
+
           nsPrimitiveHelpers::CreatePrimitiveForData ( flavorStr, data, dataLen, getter_AddRefs(genericDataWrapper) );
           free(data);
         }
         
         NS_ASSERTION ( genericDataWrapper, "About to put null data into the transferable" );
         aTransferable->SetTransferData(flavorStr, genericDataWrapper, dataLen);
         res = NS_OK;