forked from mirrors/gecko-dev
		
	 4d956f9599
			
		
	
	
		4d956f9599
		
	
	
	
	
		
			
			MozReview-Commit-ID: CTz0tRr3p57 --HG-- extra : rebase_source : 500687c604957227dba8668a11f7c4f55c7fd175
		
			
				
	
	
		
			778 lines
		
	
	
	
		
			25 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			778 lines
		
	
	
	
		
			25 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 | |
| /* vim:expandtab:shiftwidth=4:tabstop=4:
 | |
|  */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "mozilla/ArrayUtils.h"
 | |
| 
 | |
| #include "nsArrayUtils.h"
 | |
| #include "nsClipboard.h"
 | |
| #include "nsClipboardX11.h"
 | |
| #if defined(MOZ_WAYLAND)
 | |
| #include "nsClipboardWayland.h"
 | |
| #endif
 | |
| #include "HeadlessClipboard.h"
 | |
| #include "nsSupportsPrimitives.h"
 | |
| #include "nsString.h"
 | |
| #include "nsReadableUtils.h"
 | |
| #include "nsPrimitiveHelpers.h"
 | |
| #include "nsIServiceManager.h"
 | |
| #include "nsImageToPixbuf.h"
 | |
| #include "nsStringStream.h"
 | |
| #include "nsIObserverService.h"
 | |
| #include "mozilla/Services.h"
 | |
| #include "mozilla/RefPtr.h"
 | |
| #include "mozilla/TimeStamp.h"
 | |
| 
 | |
| #include "imgIContainer.h"
 | |
| 
 | |
| #include <gtk/gtk.h>
 | |
| #include <gtk/gtkx.h>
 | |
| 
 | |
| #include "mozilla/Encoding.h"
 | |
| 
 | |
| 
 | |
| using namespace mozilla;
 | |
| 
 | |
| // Idle timeout for receiving selection and property notify events (microsec)
 | |
| const int kClipboardTimeout = 500000;
 | |
| 
 | |
| // Callback when someone asks us for the data
 | |
| void
 | |
| clipboard_get_cb(GtkClipboard *aGtkClipboard,
 | |
|                  GtkSelectionData *aSelectionData,
 | |
|                  guint info,
 | |
|                  gpointer user_data);
 | |
| 
 | |
| // Callback when someone asks us to clear a clipboard
 | |
| void
 | |
| clipboard_clear_cb(GtkClipboard *aGtkClipboard,
 | |
|                    gpointer user_data);
 | |
| 
 | |
| static void
 | |
| ConvertHTMLtoUCS2          (const char*         data,
 | |
|                             int32_t             dataLength,
 | |
|                             char16_t         **unicodeData,
 | |
|                             int32_t            &outUnicodeLen);
 | |
| 
 | |
| static void
 | |
| GetHTMLCharset             (const char* data, int32_t dataLength, nsCString& str);
 | |
| 
 | |
| GdkAtom
 | |
| GetSelectionAtom(int32_t aWhichClipboard)
 | |
| {
 | |
|     if (aWhichClipboard == nsIClipboard::kGlobalClipboard)
 | |
|         return GDK_SELECTION_CLIPBOARD;
 | |
| 
 | |
|     return GDK_SELECTION_PRIMARY;
 | |
| }
 | |
| 
 | |
| nsClipboard::nsClipboard()
 | |
| {
 | |
| }
 | |
| 
 | |
| nsClipboard::~nsClipboard()
 | |
| {
 | |
|     // We have to clear clipboard before gdk_display_close() call.
 | |
|     // See bug 531580 for details.
 | |
|     if (mGlobalTransferable) {
 | |
|         gtk_clipboard_clear(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
 | |
|     }
 | |
|     if (mSelectionTransferable) {
 | |
|         gtk_clipboard_clear(gtk_clipboard_get(GDK_SELECTION_PRIMARY));
 | |
|     }
 | |
| }
 | |
| 
 | |
| NS_IMPL_ISUPPORTS(nsClipboard, nsIClipboard)
 | |
| 
 | |
| nsresult
 | |
| nsClipboard::Init(void)
 | |
| {
 | |
|     GdkDisplay *display = gdk_display_get_default();
 | |
| 
 | |
|     // Create a nsRetrievalContext. If there's no default display
 | |
|     // create the X11 one as a fallback.
 | |
|     if (!display || GDK_IS_X11_DISPLAY(display)) {
 | |
|         mContext = new nsRetrievalContextX11();
 | |
| #if defined(MOZ_WAYLAND)
 | |
|     } else {
 | |
|         mContext = new nsRetrievalContextWayland();
 | |
| #endif
 | |
|     }
 | |
|     NS_ASSERTION(mContext, "Missing nsRetrievalContext for nsClipboard!");
 | |
| 
 | |
|     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
 | |
|     if (os) {
 | |
|         os->AddObserver(this, "quit-application", false);
 | |
|         os->AddObserver(this, "xpcom-shutdown", false);
 | |
|     }
 | |
| 
 | |
|     return NS_OK;
 | |
| }
 | |
| 
 | |
| 
 | |
| nsresult
 | |
| nsClipboard::Store(void)
 | |
| {
 | |
|     if (mGlobalTransferable) {
 | |
|         GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
 | |
|         gtk_clipboard_store(clipboard);
 | |
|     }
 | |
|     return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsClipboard::Observe(nsISupports *aSubject, const char *aTopic,
 | |
|                      const char16_t *aData)
 | |
| {
 | |
|     Store();
 | |
|     return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsClipboard::SetData(nsITransferable *aTransferable,
 | |
|                      nsIClipboardOwner *aOwner, int32_t aWhichClipboard)
 | |
| {
 | |
|     // See if we can short cut
 | |
|     if ((aWhichClipboard == kGlobalClipboard &&
 | |
|          aTransferable == mGlobalTransferable.get() &&
 | |
|          aOwner == mGlobalOwner.get()) ||
 | |
|         (aWhichClipboard == kSelectionClipboard &&
 | |
|          aTransferable == mSelectionTransferable.get() &&
 | |
|          aOwner == mSelectionOwner.get())) {
 | |
|         return NS_OK;
 | |
|     }
 | |
| 
 | |
|     // Clear out the clipboard in order to set the new data
 | |
|     EmptyClipboard(aWhichClipboard);
 | |
| 
 | |
|     // List of suported targets
 | |
|     GtkTargetList *list = gtk_target_list_new(nullptr, 0);
 | |
| 
 | |
|     // Get the types of supported flavors
 | |
|     nsCOMPtr<nsIArray> flavors;
 | |
| 
 | |
|     nsresult rv =
 | |
|         aTransferable->FlavorsTransferableCanExport(getter_AddRefs(flavors));
 | |
|     if (!flavors || NS_FAILED(rv))
 | |
|         return NS_ERROR_FAILURE;
 | |
| 
 | |
|     // Add all the flavors to this widget's supported type.
 | |
|     bool imagesAdded = false;
 | |
|     uint32_t count;
 | |
|     flavors->GetLength(&count);
 | |
|     for (uint32_t i=0; i < count; i++) {
 | |
|         nsCOMPtr<nsISupportsCString> flavor = do_QueryElementAt(flavors, i);
 | |
| 
 | |
|         if (flavor) {
 | |
|             nsCString flavorStr;
 | |
|             flavor->ToString(getter_Copies(flavorStr));
 | |
| 
 | |
|             // special case text/unicode since we can handle all of
 | |
|             // the string types
 | |
|             if (flavorStr.EqualsLiteral(kUnicodeMime)) {
 | |
|                 gtk_target_list_add(list, gdk_atom_intern("UTF8_STRING", FALSE), 0, 0);
 | |
|                 gtk_target_list_add(list, gdk_atom_intern("COMPOUND_TEXT", FALSE), 0, 0);
 | |
|                 gtk_target_list_add(list, gdk_atom_intern("TEXT", FALSE), 0, 0);
 | |
|                 gtk_target_list_add(list, GDK_SELECTION_TYPE_STRING, 0, 0);
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             if (flavorStr.EqualsLiteral(kNativeImageMime) ||
 | |
|                 flavorStr.EqualsLiteral(kPNGImageMime) ||
 | |
|                 flavorStr.EqualsLiteral(kJPEGImageMime) ||
 | |
|                 flavorStr.EqualsLiteral(kJPGImageMime) ||
 | |
|                 flavorStr.EqualsLiteral(kGIFImageMime)) {
 | |
|                 // don't bother adding image targets twice
 | |
|                 if (!imagesAdded) {
 | |
|                     // accept any writable image type
 | |
|                     gtk_target_list_add_image_targets(list, 0, TRUE);
 | |
|                     imagesAdded = true;
 | |
|                 }
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             // Add this to our list of valid targets
 | |
|             GdkAtom atom = gdk_atom_intern(flavorStr.get(), FALSE);
 | |
|             gtk_target_list_add(list, atom, 0, 0);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Get GTK clipboard (CLIPBOARD or PRIMARY)
 | |
|     GtkClipboard *gtkClipboard = gtk_clipboard_get(GetSelectionAtom(aWhichClipboard));
 | |
| 
 | |
|     gint numTargets;
 | |
|     GtkTargetEntry *gtkTargets = gtk_target_table_new_from_list(list, &numTargets);
 | |
| 
 | |
|     // Set getcallback and request to store data after an application exit
 | |
|     if (gtkTargets &&
 | |
|         gtk_clipboard_set_with_data(gtkClipboard, gtkTargets, numTargets,
 | |
|                                     clipboard_get_cb, clipboard_clear_cb, this))
 | |
|     {
 | |
|         // We managed to set-up the clipboard so update internal state
 | |
|         // We have to set it now because gtk_clipboard_set_with_data() calls clipboard_clear_cb()
 | |
|         // which reset our internal state
 | |
|         if (aWhichClipboard == kSelectionClipboard) {
 | |
|             mSelectionOwner = aOwner;
 | |
|             mSelectionTransferable = aTransferable;
 | |
|         }
 | |
|         else {
 | |
|             mGlobalOwner = aOwner;
 | |
|             mGlobalTransferable = aTransferable;
 | |
|             gtk_clipboard_set_can_store(gtkClipboard, gtkTargets, numTargets);
 | |
|         }
 | |
| 
 | |
|         rv = NS_OK;
 | |
|     }
 | |
|     else {
 | |
|         rv = NS_ERROR_FAILURE;
 | |
|     }
 | |
| 
 | |
|     gtk_target_table_free(gtkTargets, numTargets);
 | |
|     gtk_target_list_unref(list);
 | |
| 
 | |
|     return rv;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsClipboard::SetTransferableData(nsITransferable* aTransferable,
 | |
|                                  nsCString&       aFlavor,
 | |
|                                  const char*      aClipboardData,
 | |
|                                  uint32_t         aClipboardDataLength)
 | |
| {
 | |
|   nsCOMPtr<nsISupports> wrapper;
 | |
|   nsPrimitiveHelpers::CreatePrimitiveForData(aFlavor,
 | |
|                                              aClipboardData,
 | |
|                                              aClipboardDataLength,
 | |
|                                              getter_AddRefs(wrapper));
 | |
|   aTransferable->SetTransferData(aFlavor.get(),
 | |
|                                  wrapper, aClipboardDataLength);
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsClipboard::GetData(nsITransferable *aTransferable, int32_t aWhichClipboard)
 | |
| {
 | |
|     if (!aTransferable)
 | |
|         return NS_ERROR_FAILURE;
 | |
| 
 | |
|     // Get a list of flavors this transferable can import
 | |
|     nsCOMPtr<nsIArray> flavors;
 | |
|     nsresult rv;
 | |
|     rv = aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavors));
 | |
|     if (!flavors || NS_FAILED(rv))
 | |
|         return NS_ERROR_FAILURE;
 | |
| 
 | |
|     uint32_t count;
 | |
|     flavors->GetLength(&count);
 | |
|     for (uint32_t i=0; i < count; i++) {
 | |
|         nsCOMPtr<nsISupportsCString> currentFlavor;
 | |
|         currentFlavor = do_QueryElementAt(flavors, i);
 | |
|         if (!currentFlavor)
 | |
|             continue;
 | |
| 
 | |
|         nsCString flavorStr;
 | |
|         currentFlavor->ToString(getter_Copies(flavorStr));
 | |
| 
 | |
|         if (flavorStr.EqualsLiteral(kJPEGImageMime) ||
 | |
|             flavorStr.EqualsLiteral(kJPGImageMime) ||
 | |
|             flavorStr.EqualsLiteral(kPNGImageMime) ||
 | |
|             flavorStr.EqualsLiteral(kGIFImageMime)) {
 | |
|             // Emulate support for image/jpg
 | |
|             if (flavorStr.EqualsLiteral(kJPGImageMime)) {
 | |
|                 flavorStr.Assign(kJPEGImageMime);
 | |
|             }
 | |
| 
 | |
|             uint32_t    clipboardDataLength;
 | |
|             const char* clipboardData =
 | |
|                 mContext->GetClipboardData(flavorStr.get(),
 | |
|                                            aWhichClipboard,
 | |
|                                            &clipboardDataLength);
 | |
|             if (!clipboardData)
 | |
|                 continue;
 | |
| 
 | |
|             nsCOMPtr<nsIInputStream> byteStream;
 | |
|             NS_NewByteInputStream(getter_AddRefs(byteStream),
 | |
|                                   clipboardData,
 | |
|                                   clipboardDataLength,
 | |
|                                   NS_ASSIGNMENT_COPY);
 | |
|             aTransferable->SetTransferData(flavorStr.get(), byteStream,
 | |
|                                            sizeof(nsIInputStream*));
 | |
| 
 | |
|             mContext->ReleaseClipboardData(clipboardData);
 | |
|             return NS_OK;
 | |
|         }
 | |
| 
 | |
|         // Special case text/unicode since we can convert any
 | |
|         // string into text/unicode
 | |
|         if (flavorStr.EqualsLiteral(kUnicodeMime)) {
 | |
|             const char* clipboardData =
 | |
|                 mContext->GetClipboardText(aWhichClipboard);
 | |
|             if (!clipboardData) {
 | |
|                 // If the type was text/unicode and we couldn't get
 | |
|                 // text off the clipboard, run the next loop
 | |
|                 // iteration.
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             // Convert utf-8 into our unicode format.
 | |
|             NS_ConvertUTF8toUTF16 ucs2string(clipboardData);
 | |
|             const char* unicodeData = (const char *)ToNewUnicode(ucs2string);
 | |
|             uint32_t unicodeDataLength = ucs2string.Length() * 2;
 | |
|             SetTransferableData(aTransferable, flavorStr,
 | |
|                                 unicodeData, unicodeDataLength);
 | |
|             free((void *)unicodeData);
 | |
| 
 | |
|             mContext->ReleaseClipboardData(clipboardData);
 | |
|             return NS_OK;
 | |
|         }
 | |
| 
 | |
| 
 | |
|         uint32_t clipboardDataLength;
 | |
|         const char* clipboardData = mContext->GetClipboardData(
 | |
|             flavorStr.get(), aWhichClipboard, &clipboardDataLength);
 | |
| 
 | |
|         if (clipboardData) {
 | |
|             // Special case text/html since we can convert into UCS2
 | |
|             if (flavorStr.EqualsLiteral(kHTMLMime)) {
 | |
|                 char16_t* htmlBody = nullptr;
 | |
|                 int32_t htmlBodyLen = 0;
 | |
|                 // Convert text/html into our unicode format
 | |
|                 ConvertHTMLtoUCS2(clipboardData, clipboardDataLength,
 | |
|                                   &htmlBody, htmlBodyLen);
 | |
| 
 | |
|                 // Try next data format?
 | |
|                 if (!htmlBodyLen) {
 | |
|                     mContext->ReleaseClipboardData(clipboardData);
 | |
|                     continue;
 | |
|                 }
 | |
| 
 | |
|                 SetTransferableData(aTransferable, flavorStr,
 | |
|                                     (const char*)htmlBody, htmlBodyLen * 2);
 | |
|                 free(htmlBody);
 | |
|             } else {
 | |
|                 SetTransferableData(aTransferable, flavorStr,
 | |
|                                     clipboardData, clipboardDataLength);
 | |
|             }
 | |
| 
 | |
|             mContext->ReleaseClipboardData(clipboardData);
 | |
|             return NS_OK;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsClipboard::EmptyClipboard(int32_t aWhichClipboard)
 | |
| {
 | |
|     if (aWhichClipboard == kSelectionClipboard) {
 | |
|         if (mSelectionOwner) {
 | |
|             mSelectionOwner->LosingOwnership(mSelectionTransferable);
 | |
|             mSelectionOwner = nullptr;
 | |
|         }
 | |
|         mSelectionTransferable = nullptr;
 | |
|     }
 | |
|     else {
 | |
|         if (mGlobalOwner) {
 | |
|             mGlobalOwner->LosingOwnership(mGlobalTransferable);
 | |
|             mGlobalOwner = nullptr;
 | |
|         }
 | |
|         mGlobalTransferable = nullptr;
 | |
|     }
 | |
| 
 | |
|     return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsClipboard::HasDataMatchingFlavors(const char** aFlavorList, uint32_t aLength,
 | |
|                                     int32_t aWhichClipboard, bool *_retval)
 | |
| {
 | |
|   if (!aFlavorList || !_retval)
 | |
|       return NS_ERROR_NULL_POINTER;
 | |
| 
 | |
|   *_retval = false;
 | |
| 
 | |
|   int targetNums;
 | |
|   GdkAtom* targets = mContext->GetTargets(aWhichClipboard, &targetNums);
 | |
|   if (!targets)
 | |
|       return NS_OK;
 | |
| 
 | |
|   // Walk through the provided types and try to match it to a
 | |
|   // provided type.
 | |
|   for (uint32_t i = 0; i < aLength && !*_retval; i++) {
 | |
|       // We special case text/unicode here.
 | |
|       if (!strcmp(aFlavorList[i], kUnicodeMime) &&
 | |
|           gtk_targets_include_text(targets, targetNums)) {
 | |
|           *_retval = true;
 | |
|           break;
 | |
|       }
 | |
| 
 | |
|       for (int32_t j = 0; j < targetNums; j++) {
 | |
|           gchar *atom_name = gdk_atom_name(targets[j]);
 | |
|           if (!atom_name)
 | |
|               continue;
 | |
| 
 | |
|           if (!strcmp(atom_name, aFlavorList[i]))
 | |
|               *_retval = true;
 | |
| 
 | |
|           // X clipboard supports image/jpeg, but we want to emulate support
 | |
|           // for image/jpg as well
 | |
|           if (!strcmp(aFlavorList[i], kJPGImageMime) &&
 | |
|               !strcmp(atom_name, kJPEGImageMime)) {
 | |
|               *_retval = true;
 | |
|           }
 | |
| 
 | |
|           g_free(atom_name);
 | |
| 
 | |
|           if (*_retval)
 | |
|               break;
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   g_free(targets);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsClipboard::SupportsSelectionClipboard(bool *_retval)
 | |
| {
 | |
|     *_retval = mContext->HasSelectionSupport();
 | |
|     return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsClipboard::SupportsFindClipboard(bool* _retval)
 | |
| {
 | |
|   *_retval = false;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsITransferable *
 | |
| nsClipboard::GetTransferable(int32_t aWhichClipboard)
 | |
| {
 | |
|     nsITransferable *retval;
 | |
| 
 | |
|     if (aWhichClipboard == kSelectionClipboard)
 | |
|         retval = mSelectionTransferable.get();
 | |
|     else
 | |
|         retval = mGlobalTransferable.get();
 | |
| 
 | |
|     return retval;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsClipboard::SelectionGetEvent(GtkClipboard     *aClipboard,
 | |
|                                GtkSelectionData *aSelectionData)
 | |
| {
 | |
|     // Someone has asked us to hand them something.  The first thing
 | |
|     // that we want to do is see if that something includes text.  If
 | |
|     // it does, try to give it text/unicode after converting it to
 | |
|     // utf-8.
 | |
| 
 | |
|     int32_t whichClipboard;
 | |
| 
 | |
|     // which clipboard?
 | |
|     GdkAtom selection = gtk_selection_data_get_selection(aSelectionData);
 | |
|     if (selection == GDK_SELECTION_PRIMARY)
 | |
|         whichClipboard = kSelectionClipboard;
 | |
|     else if (selection == GDK_SELECTION_CLIPBOARD)
 | |
|         whichClipboard = kGlobalClipboard;
 | |
|     else
 | |
|         return; // THAT AIN'T NO CLIPBOARD I EVER HEARD OF
 | |
| 
 | |
|     nsCOMPtr<nsITransferable> trans = GetTransferable(whichClipboard);
 | |
|     if (!trans) {
 | |
|       // We have nothing to serve
 | |
| #ifdef DEBUG_CLIPBOARD
 | |
|       printf("nsClipboard::SelectionGetEvent() - %s clipboard is empty!\n",
 | |
|              whichClipboard == kSelectionClipboard ? "Selection" : "Global");
 | |
| #endif
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     nsresult rv;
 | |
|     nsCOMPtr<nsISupports> item;
 | |
|     uint32_t len;
 | |
| 
 | |
|     GdkAtom selectionTarget = gtk_selection_data_get_target(aSelectionData);
 | |
| 
 | |
|     // Check to see if the selection data includes any of the string
 | |
|     // types that we support.
 | |
|     if (selectionTarget == gdk_atom_intern ("STRING", FALSE) ||
 | |
|         selectionTarget == gdk_atom_intern ("TEXT", FALSE) ||
 | |
|         selectionTarget == gdk_atom_intern ("COMPOUND_TEXT", FALSE) ||
 | |
|         selectionTarget == gdk_atom_intern ("UTF8_STRING", FALSE)) {
 | |
|         // Try to convert our internal type into a text string.  Get
 | |
|         // the transferable for this clipboard and try to get the
 | |
|         // text/unicode type for it.
 | |
|         rv = trans->GetTransferData("text/unicode", getter_AddRefs(item),
 | |
|                                     &len);
 | |
|         if (!item || NS_FAILED(rv))
 | |
|             return;
 | |
| 
 | |
|         nsCOMPtr<nsISupportsString> wideString;
 | |
|         wideString = do_QueryInterface(item);
 | |
|         if (!wideString)
 | |
|             return;
 | |
| 
 | |
|         nsAutoString ucs2string;
 | |
|         wideString->GetData(ucs2string);
 | |
|         char *utf8string = ToNewUTF8String(ucs2string);
 | |
|         if (!utf8string)
 | |
|             return;
 | |
| 
 | |
|         gtk_selection_data_set_text (aSelectionData, utf8string,
 | |
|                                      strlen(utf8string));
 | |
| 
 | |
|         free(utf8string);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     // Check to see if the selection data is an image type
 | |
|     if (gtk_targets_include_image(&selectionTarget, 1, TRUE)) {
 | |
|         // Look through our transfer data for the image
 | |
|         static const char* const imageMimeTypes[] = {
 | |
|             kNativeImageMime, kPNGImageMime, kJPEGImageMime, kJPGImageMime, kGIFImageMime };
 | |
|         nsCOMPtr<nsISupports> imageItem;
 | |
|         nsCOMPtr<nsISupportsInterfacePointer> ptrPrimitive;
 | |
|         for (uint32_t i = 0; !ptrPrimitive && i < ArrayLength(imageMimeTypes); i++) {
 | |
|             rv = trans->GetTransferData(imageMimeTypes[i], getter_AddRefs(imageItem), &len);
 | |
|             ptrPrimitive = do_QueryInterface(imageItem);
 | |
|         }
 | |
|         if (!ptrPrimitive)
 | |
|             return;
 | |
| 
 | |
|         nsCOMPtr<nsISupports> primitiveData;
 | |
|         ptrPrimitive->GetData(getter_AddRefs(primitiveData));
 | |
|         nsCOMPtr<imgIContainer> image(do_QueryInterface(primitiveData));
 | |
|         if (!image) // Not getting an image for an image mime type!?
 | |
|             return;
 | |
| 
 | |
|         GdkPixbuf* pixbuf = nsImageToPixbuf::ImageToPixbuf(image);
 | |
|         if (!pixbuf)
 | |
|             return;
 | |
| 
 | |
|         gtk_selection_data_set_pixbuf(aSelectionData, pixbuf);
 | |
|         g_object_unref(pixbuf);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     // Try to match up the selection data target to something our
 | |
|     // transferable provides.
 | |
|     gchar *target_name = gdk_atom_name(selectionTarget);
 | |
|     if (!target_name)
 | |
|         return;
 | |
| 
 | |
|     rv = trans->GetTransferData(target_name, getter_AddRefs(item), &len);
 | |
|     // nothing found?
 | |
|     if (!item || NS_FAILED(rv)) {
 | |
|         g_free(target_name);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     void *primitive_data = nullptr;
 | |
|     nsPrimitiveHelpers::CreateDataFromPrimitive(nsDependentCString(target_name),
 | |
|                                                 item, &primitive_data, len);
 | |
| 
 | |
|     if (primitive_data) {
 | |
|         // Check to see if the selection data is text/html
 | |
|         if (selectionTarget == gdk_atom_intern (kHTMLMime, FALSE)) {
 | |
|             /*
 | |
|              * "text/html" can be encoded UCS2. It is recommended that
 | |
|              * documents transmitted as UCS2 always begin with a ZERO-WIDTH
 | |
|              * NON-BREAKING SPACE character (hexadecimal FEFF, also called
 | |
|              * Byte Order Mark (BOM)). Adding BOM can help other app to
 | |
|              * detect mozilla use UCS2 encoding when copy-paste.
 | |
|              */
 | |
|             guchar *buffer = (guchar *)
 | |
|                     g_malloc((len * sizeof(guchar)) + sizeof(char16_t));
 | |
|             if (!buffer)
 | |
|                 return;
 | |
|             char16_t prefix = 0xFEFF;
 | |
|             memcpy(buffer, &prefix, sizeof(prefix));
 | |
|             memcpy(buffer + sizeof(prefix), primitive_data, len);
 | |
|             g_free((guchar *)primitive_data);
 | |
|             primitive_data = (guchar *)buffer;
 | |
|             len += sizeof(prefix);
 | |
|         }
 | |
| 
 | |
|         gtk_selection_data_set(aSelectionData, selectionTarget,
 | |
|                                8, /* 8 bits in a unit */
 | |
|                                (const guchar *)primitive_data, len);
 | |
|         g_free(primitive_data);
 | |
|     }
 | |
| 
 | |
|     g_free(target_name);
 | |
| 
 | |
| }
 | |
| 
 | |
| void
 | |
| nsClipboard::SelectionClearEvent(GtkClipboard *aGtkClipboard)
 | |
| {
 | |
|     int32_t whichClipboard;
 | |
| 
 | |
|     // which clipboard?
 | |
|     if (aGtkClipboard == gtk_clipboard_get(GDK_SELECTION_PRIMARY))
 | |
|         whichClipboard = kSelectionClipboard;
 | |
|     else if (aGtkClipboard == gtk_clipboard_get(GDK_SELECTION_CLIPBOARD))
 | |
|         whichClipboard = kGlobalClipboard;
 | |
|     else
 | |
|         return; // THAT AIN'T NO CLIPBOARD I EVER HEARD OF
 | |
| 
 | |
|     EmptyClipboard(whichClipboard);
 | |
| }
 | |
| 
 | |
| void
 | |
| clipboard_get_cb(GtkClipboard *aGtkClipboard,
 | |
|                  GtkSelectionData *aSelectionData,
 | |
|                  guint info,
 | |
|                  gpointer user_data)
 | |
| {
 | |
|     nsClipboard *aClipboard = static_cast<nsClipboard *>(user_data);
 | |
|     aClipboard->SelectionGetEvent(aGtkClipboard, aSelectionData);
 | |
| }
 | |
| 
 | |
| void
 | |
| clipboard_clear_cb(GtkClipboard *aGtkClipboard,
 | |
|                    gpointer user_data)
 | |
| {
 | |
|     nsClipboard *aClipboard = static_cast<nsClipboard *>(user_data);
 | |
|     aClipboard->SelectionClearEvent(aGtkClipboard);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * when copy-paste, mozilla wants data encoded using UCS2,
 | |
|  * other app such as StarOffice use "text/html"(RFC2854).
 | |
|  * This function convert data(got from GTK clipboard)
 | |
|  * to data mozilla wanted.
 | |
|  *
 | |
|  * data from GTK clipboard can be 3 forms:
 | |
|  *  1. From current mozilla
 | |
|  *     "text/html", charset = utf-16
 | |
|  *  2. From old version mozilla or mozilla-based app
 | |
|  *     content("body" only), charset = utf-16
 | |
|  *  3. From other app who use "text/html" when copy-paste
 | |
|  *     "text/html", has "charset" info
 | |
|  *
 | |
|  * data      : got from GTK clipboard
 | |
|  * dataLength: got from GTK clipboard
 | |
|  * body      : pass to Mozilla
 | |
|  * bodyLength: pass to Mozilla
 | |
|  */
 | |
| void ConvertHTMLtoUCS2(const char* data, int32_t dataLength,
 | |
|                        char16_t** unicodeData, int32_t& outUnicodeLen)
 | |
| {
 | |
|     nsAutoCString charset;
 | |
|     GetHTMLCharset(data, dataLength, charset);// get charset of HTML
 | |
|     if (charset.EqualsLiteral("UTF-16")) {//current mozilla
 | |
|         outUnicodeLen = (dataLength / 2) - 1;
 | |
|         *unicodeData =
 | |
|             reinterpret_cast<char16_t*>
 | |
|             (moz_xmalloc((outUnicodeLen + sizeof('\0')) * sizeof(char16_t)));
 | |
|         if (*unicodeData) {
 | |
|             memcpy(*unicodeData, data + sizeof(char16_t),
 | |
|                    outUnicodeLen * sizeof(char16_t));
 | |
|             (*unicodeData)[outUnicodeLen] = '\0';
 | |
|         }
 | |
|     } else if (charset.EqualsLiteral("UNKNOWN")) {
 | |
|         outUnicodeLen = 0;
 | |
|         return;
 | |
|     } else {
 | |
|         // app which use "text/html" to copy&paste
 | |
|         // get the decoder
 | |
|         auto encoding = Encoding::ForLabelNoReplacement(charset);
 | |
|         if (!encoding) {
 | |
| #ifdef DEBUG_CLIPBOARD
 | |
|             g_print("        get unicode decoder error\n");
 | |
| #endif
 | |
|             outUnicodeLen = 0;
 | |
|             return;
 | |
|         }
 | |
|         auto decoder = encoding->NewDecoder();
 | |
|         CheckedInt<size_t> needed = decoder->MaxUTF16BufferLength(dataLength);
 | |
|         if (!needed.isValid() || needed.value() > INT32_MAX) {
 | |
|           outUnicodeLen = 0;
 | |
|           return;
 | |
|         }
 | |
| 
 | |
|         outUnicodeLen = 0;
 | |
|         if (needed.value()) {
 | |
|           *unicodeData = reinterpret_cast<char16_t*>(
 | |
|             moz_xmalloc((needed.value() + 1) * sizeof(char16_t)));
 | |
|           if (*unicodeData) {
 | |
|             uint32_t result;
 | |
|             size_t read;
 | |
|             size_t written;
 | |
|             bool hadErrors;
 | |
|             Tie(result, read, written, hadErrors) =
 | |
|               decoder->DecodeToUTF16(AsBytes(MakeSpan(data, dataLength)),
 | |
|                                      MakeSpan(*unicodeData, needed.value()),
 | |
|                                      true);
 | |
|             MOZ_ASSERT(result == kInputEmpty);
 | |
|             MOZ_ASSERT(read == size_t(dataLength));
 | |
|             MOZ_ASSERT(written <= needed.value());
 | |
|             Unused << hadErrors;
 | |
| #ifdef DEBUG_CLIPBOARD
 | |
|             if (read != dataLength)
 | |
|               printf("didn't consume all the bytes\n");
 | |
| #endif
 | |
|             outUnicodeLen = written;
 | |
|             // null terminate.
 | |
|             (*unicodeData)[outUnicodeLen] = '\0';
 | |
|             }
 | |
|         } // if valid length
 | |
|     }
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * get "charset" information from clipboard data
 | |
|  * return value can be:
 | |
|  *  1. "UTF-16":      mozilla or "text/html" with "charset=utf-16"
 | |
|  *  2. "UNKNOWN":     mozilla can't detect what encode it use
 | |
|  *  3. other:         "text/html" with other charset than utf-16
 | |
|  */
 | |
| void GetHTMLCharset(const char* data, int32_t dataLength, nsCString& str)
 | |
| {
 | |
|     // if detect "FFFE" or "FEFF", assume UTF-16
 | |
|     char16_t* beginChar =  (char16_t*)data;
 | |
|     if ((beginChar[0] == 0xFFFE) || (beginChar[0] == 0xFEFF)) {
 | |
|         str.AssignLiteral("UTF-16");
 | |
|         return;
 | |
|     }
 | |
|     // no "FFFE" and "FEFF", assume ASCII first to find "charset" info
 | |
|     const nsDependentCString htmlStr(data, dataLength);
 | |
|     nsACString::const_iterator start, end;
 | |
|     htmlStr.BeginReading(start);
 | |
|     htmlStr.EndReading(end);
 | |
|     nsACString::const_iterator valueStart(start), valueEnd(start);
 | |
| 
 | |
|     if (CaseInsensitiveFindInReadable(
 | |
|         NS_LITERAL_CSTRING("CONTENT=\"text/html;"),
 | |
|         start, end)) {
 | |
|         start = end;
 | |
|         htmlStr.EndReading(end);
 | |
| 
 | |
|         if (CaseInsensitiveFindInReadable(
 | |
|             NS_LITERAL_CSTRING("charset="),
 | |
|             start, end)) {
 | |
|             valueStart = end;
 | |
|             start = end;
 | |
|             htmlStr.EndReading(end);
 | |
| 
 | |
|             if (FindCharInReadable('"', start, end))
 | |
|                 valueEnd = start;
 | |
|         }
 | |
|     }
 | |
|     // find "charset" in HTML
 | |
|     if (valueStart != valueEnd) {
 | |
|         str = Substring(valueStart, valueEnd);
 | |
|         ToUpperCase(str);
 | |
| #ifdef DEBUG_CLIPBOARD
 | |
|         printf("Charset of HTML = %s\n", charsetUpperStr.get());
 | |
| #endif
 | |
|         return;
 | |
|     }
 | |
|     str.AssignLiteral("UNKNOWN");
 | |
| }
 |