1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/widget/gtk/nsClipboard.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1019 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* vim:expandtab:shiftwidth=4:tabstop=4: 1.6 + */ 1.7 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.8 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.9 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.10 + 1.11 +#include "mozilla/ArrayUtils.h" 1.12 + 1.13 +#include "nsClipboard.h" 1.14 +#include "nsSupportsPrimitives.h" 1.15 +#include "nsString.h" 1.16 +#include "nsReadableUtils.h" 1.17 +#include "nsXPIDLString.h" 1.18 +#include "nsPrimitiveHelpers.h" 1.19 +#include "nsICharsetConverterManager.h" 1.20 +#include "nsIServiceManager.h" 1.21 +#include "nsImageToPixbuf.h" 1.22 +#include "nsStringStream.h" 1.23 +#include "nsIObserverService.h" 1.24 +#include "mozilla/Services.h" 1.25 +#include "mozilla/RefPtr.h" 1.26 +#include "mozilla/TimeStamp.h" 1.27 + 1.28 +#include "imgIContainer.h" 1.29 + 1.30 +#include <gtk/gtk.h> 1.31 + 1.32 +// For manipulation of the X event queue 1.33 +#include <X11/Xlib.h> 1.34 +#include <gdk/gdkx.h> 1.35 +#include <sys/time.h> 1.36 +#include <sys/types.h> 1.37 +#include <errno.h> 1.38 +#include <unistd.h> 1.39 + 1.40 +using namespace mozilla; 1.41 + 1.42 +// Callback when someone asks us for the data 1.43 +void 1.44 +clipboard_get_cb(GtkClipboard *aGtkClipboard, 1.45 + GtkSelectionData *aSelectionData, 1.46 + guint info, 1.47 + gpointer user_data); 1.48 + 1.49 +// Callback when someone asks us to clear a clipboard 1.50 +void 1.51 +clipboard_clear_cb(GtkClipboard *aGtkClipboard, 1.52 + gpointer user_data); 1.53 + 1.54 +static void 1.55 +ConvertHTMLtoUCS2 (guchar *data, 1.56 + int32_t dataLength, 1.57 + char16_t **unicodeData, 1.58 + int32_t &outUnicodeLen); 1.59 + 1.60 +static void 1.61 +GetHTMLCharset (guchar * data, int32_t dataLength, nsCString& str); 1.62 + 1.63 + 1.64 +// Our own versions of gtk_clipboard_wait_for_contents and 1.65 +// gtk_clipboard_wait_for_text, which don't run the event loop while 1.66 +// waiting for the data. This prevents a lot of problems related to 1.67 +// dispatching events at unexpected times. 1.68 + 1.69 +static GtkSelectionData * 1.70 +wait_for_contents (GtkClipboard *clipboard, GdkAtom target); 1.71 + 1.72 +static gchar * 1.73 +wait_for_text (GtkClipboard *clipboard); 1.74 + 1.75 +nsClipboard::nsClipboard() 1.76 +{ 1.77 +} 1.78 + 1.79 +nsClipboard::~nsClipboard() 1.80 +{ 1.81 + // We have to clear clipboard before gdk_display_close() call. 1.82 + // See bug 531580 for details. 1.83 + if (mGlobalTransferable) { 1.84 + gtk_clipboard_clear(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)); 1.85 + } 1.86 + if (mSelectionTransferable) { 1.87 + gtk_clipboard_clear(gtk_clipboard_get(GDK_SELECTION_PRIMARY)); 1.88 + } 1.89 +} 1.90 + 1.91 +NS_IMPL_ISUPPORTS(nsClipboard, nsIClipboard) 1.92 + 1.93 +nsresult 1.94 +nsClipboard::Init(void) 1.95 +{ 1.96 + nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); 1.97 + if (!os) 1.98 + return NS_ERROR_FAILURE; 1.99 + 1.100 + os->AddObserver(this, "quit-application", false); 1.101 + 1.102 + return NS_OK; 1.103 +} 1.104 + 1.105 +NS_IMETHODIMP 1.106 +nsClipboard::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData) 1.107 +{ 1.108 + if (strcmp(aTopic, "quit-application") == 0) { 1.109 + // application is going to quit, save clipboard content 1.110 + Store(); 1.111 + } 1.112 + return NS_OK; 1.113 +} 1.114 + 1.115 +nsresult 1.116 +nsClipboard::Store(void) 1.117 +{ 1.118 + // Ask the clipboard manager to store the current clipboard content 1.119 + if (mGlobalTransferable) { 1.120 + GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); 1.121 + gtk_clipboard_store(clipboard); 1.122 + } 1.123 + return NS_OK; 1.124 +} 1.125 + 1.126 +NS_IMETHODIMP 1.127 +nsClipboard::SetData(nsITransferable *aTransferable, 1.128 + nsIClipboardOwner *aOwner, int32_t aWhichClipboard) 1.129 +{ 1.130 + // See if we can short cut 1.131 + if ((aWhichClipboard == kGlobalClipboard && 1.132 + aTransferable == mGlobalTransferable.get() && 1.133 + aOwner == mGlobalOwner.get()) || 1.134 + (aWhichClipboard == kSelectionClipboard && 1.135 + aTransferable == mSelectionTransferable.get() && 1.136 + aOwner == mSelectionOwner.get())) { 1.137 + return NS_OK; 1.138 + } 1.139 + 1.140 + nsresult rv; 1.141 + if (!mPrivacyHandler) { 1.142 + rv = NS_NewClipboardPrivacyHandler(getter_AddRefs(mPrivacyHandler)); 1.143 + NS_ENSURE_SUCCESS(rv, rv); 1.144 + } 1.145 + rv = mPrivacyHandler->PrepareDataForClipboard(aTransferable); 1.146 + NS_ENSURE_SUCCESS(rv, rv); 1.147 + 1.148 + // Clear out the clipboard in order to set the new data 1.149 + EmptyClipboard(aWhichClipboard); 1.150 + 1.151 + // List of suported targets 1.152 + GtkTargetList *list = gtk_target_list_new(nullptr, 0); 1.153 + 1.154 + // Get the types of supported flavors 1.155 + nsCOMPtr<nsISupportsArray> flavors; 1.156 + 1.157 + rv = aTransferable->FlavorsTransferableCanExport(getter_AddRefs(flavors)); 1.158 + if (!flavors || NS_FAILED(rv)) 1.159 + return NS_ERROR_FAILURE; 1.160 + 1.161 + // Add all the flavors to this widget's supported type. 1.162 + bool imagesAdded = false; 1.163 + uint32_t count; 1.164 + flavors->Count(&count); 1.165 + for (uint32_t i=0; i < count; i++) { 1.166 + nsCOMPtr<nsISupports> tastesLike; 1.167 + flavors->GetElementAt(i, getter_AddRefs(tastesLike)); 1.168 + nsCOMPtr<nsISupportsCString> flavor = do_QueryInterface(tastesLike); 1.169 + 1.170 + if (flavor) { 1.171 + nsXPIDLCString flavorStr; 1.172 + flavor->ToString(getter_Copies(flavorStr)); 1.173 + 1.174 + // special case text/unicode since we can handle all of 1.175 + // the string types 1.176 + if (!strcmp(flavorStr, kUnicodeMime)) { 1.177 + gtk_target_list_add(list, gdk_atom_intern("UTF8_STRING", FALSE), 0, 0); 1.178 + gtk_target_list_add(list, gdk_atom_intern("COMPOUND_TEXT", FALSE), 0, 0); 1.179 + gtk_target_list_add(list, gdk_atom_intern("TEXT", FALSE), 0, 0); 1.180 + gtk_target_list_add(list, GDK_SELECTION_TYPE_STRING, 0, 0); 1.181 + continue; 1.182 + } 1.183 + 1.184 + if (flavorStr.EqualsLiteral(kNativeImageMime) || 1.185 + flavorStr.EqualsLiteral(kPNGImageMime) || 1.186 + flavorStr.EqualsLiteral(kJPEGImageMime) || 1.187 + flavorStr.EqualsLiteral(kJPGImageMime) || 1.188 + flavorStr.EqualsLiteral(kGIFImageMime)) { 1.189 + // don't bother adding image targets twice 1.190 + if (!imagesAdded) { 1.191 + // accept any writable image type 1.192 + gtk_target_list_add_image_targets(list, 0, TRUE); 1.193 + imagesAdded = true; 1.194 + } 1.195 + continue; 1.196 + } 1.197 + 1.198 + // Add this to our list of valid targets 1.199 + GdkAtom atom = gdk_atom_intern(flavorStr, FALSE); 1.200 + gtk_target_list_add(list, atom, 0, 0); 1.201 + } 1.202 + } 1.203 + 1.204 + // Get GTK clipboard (CLIPBOARD or PRIMARY) 1.205 + GtkClipboard *gtkClipboard = gtk_clipboard_get(GetSelectionAtom(aWhichClipboard)); 1.206 + 1.207 + gint numTargets; 1.208 + GtkTargetEntry *gtkTargets = gtk_target_table_new_from_list(list, &numTargets); 1.209 + 1.210 + // Set getcallback and request to store data after an application exit 1.211 + if (gtk_clipboard_set_with_data(gtkClipboard, gtkTargets, numTargets, 1.212 + clipboard_get_cb, clipboard_clear_cb, this)) 1.213 + { 1.214 + // We managed to set-up the clipboard so update internal state 1.215 + // We have to set it now because gtk_clipboard_set_with_data() calls clipboard_clear_cb() 1.216 + // which reset our internal state 1.217 + if (aWhichClipboard == kSelectionClipboard) { 1.218 + mSelectionOwner = aOwner; 1.219 + mSelectionTransferable = aTransferable; 1.220 + } 1.221 + else { 1.222 + mGlobalOwner = aOwner; 1.223 + mGlobalTransferable = aTransferable; 1.224 + gtk_clipboard_set_can_store(gtkClipboard, gtkTargets, numTargets); 1.225 + } 1.226 + 1.227 + rv = NS_OK; 1.228 + } 1.229 + else { 1.230 + rv = NS_ERROR_FAILURE; 1.231 + } 1.232 + 1.233 + gtk_target_table_free(gtkTargets, numTargets); 1.234 + gtk_target_list_unref(list); 1.235 + 1.236 + return rv; 1.237 +} 1.238 + 1.239 +NS_IMETHODIMP 1.240 +nsClipboard::GetData(nsITransferable *aTransferable, int32_t aWhichClipboard) 1.241 +{ 1.242 + if (!aTransferable) 1.243 + return NS_ERROR_FAILURE; 1.244 + 1.245 + GtkClipboard *clipboard; 1.246 + clipboard = gtk_clipboard_get(GetSelectionAtom(aWhichClipboard)); 1.247 + 1.248 + guchar *data = nullptr; 1.249 + gint length = 0; 1.250 + bool foundData = false; 1.251 + nsAutoCString foundFlavor; 1.252 + 1.253 + // Get a list of flavors this transferable can import 1.254 + nsCOMPtr<nsISupportsArray> flavors; 1.255 + nsresult rv; 1.256 + rv = aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavors)); 1.257 + if (!flavors || NS_FAILED(rv)) 1.258 + return NS_ERROR_FAILURE; 1.259 + 1.260 + uint32_t count; 1.261 + flavors->Count(&count); 1.262 + for (uint32_t i=0; i < count; i++) { 1.263 + nsCOMPtr<nsISupports> genericFlavor; 1.264 + flavors->GetElementAt(i, getter_AddRefs(genericFlavor)); 1.265 + 1.266 + nsCOMPtr<nsISupportsCString> currentFlavor; 1.267 + currentFlavor = do_QueryInterface(genericFlavor); 1.268 + 1.269 + if (currentFlavor) { 1.270 + nsXPIDLCString flavorStr; 1.271 + currentFlavor->ToString(getter_Copies(flavorStr)); 1.272 + 1.273 + // Special case text/unicode since we can convert any 1.274 + // string into text/unicode 1.275 + if (!strcmp(flavorStr, kUnicodeMime)) { 1.276 + gchar* new_text = wait_for_text(clipboard); 1.277 + if (new_text) { 1.278 + // Convert utf-8 into our unicode format. 1.279 + NS_ConvertUTF8toUTF16 ucs2string(new_text); 1.280 + data = (guchar *)ToNewUnicode(ucs2string); 1.281 + length = ucs2string.Length() * 2; 1.282 + g_free(new_text); 1.283 + foundData = true; 1.284 + foundFlavor = kUnicodeMime; 1.285 + break; 1.286 + } 1.287 + // If the type was text/unicode and we couldn't get 1.288 + // text off the clipboard, run the next loop 1.289 + // iteration. 1.290 + continue; 1.291 + } 1.292 + 1.293 + // For images, we must wrap the data in an nsIInputStream then return instead of break, 1.294 + // because that code below won't help us. 1.295 + if (!strcmp(flavorStr, kJPEGImageMime) || 1.296 + !strcmp(flavorStr, kJPGImageMime) || 1.297 + !strcmp(flavorStr, kPNGImageMime) || 1.298 + !strcmp(flavorStr, kGIFImageMime)) { 1.299 + // Emulate support for image/jpg 1.300 + if (!strcmp(flavorStr, kJPGImageMime)) { 1.301 + flavorStr.Assign(kJPEGImageMime); 1.302 + } 1.303 + 1.304 + GdkAtom atom = gdk_atom_intern(flavorStr, FALSE); 1.305 + 1.306 + GtkSelectionData *selectionData = wait_for_contents(clipboard, atom); 1.307 + if (!selectionData) 1.308 + continue; 1.309 + 1.310 + nsCOMPtr<nsIInputStream> byteStream; 1.311 + NS_NewByteInputStream(getter_AddRefs(byteStream), 1.312 + (const char*)gtk_selection_data_get_data(selectionData), 1.313 + gtk_selection_data_get_length(selectionData), 1.314 + NS_ASSIGNMENT_COPY); 1.315 + aTransferable->SetTransferData(flavorStr, byteStream, sizeof(nsIInputStream*)); 1.316 + gtk_selection_data_free(selectionData); 1.317 + return NS_OK; 1.318 + } 1.319 + 1.320 + // Get the atom for this type and try to request it off 1.321 + // the clipboard. 1.322 + GdkAtom atom = gdk_atom_intern(flavorStr, FALSE); 1.323 + GtkSelectionData *selectionData; 1.324 + selectionData = wait_for_contents(clipboard, atom); 1.325 + if (selectionData) { 1.326 + const guchar *clipboardData = gtk_selection_data_get_data(selectionData); 1.327 + length = gtk_selection_data_get_length(selectionData); 1.328 + // Special case text/html since we can convert into UCS2 1.329 + if (!strcmp(flavorStr, kHTMLMime)) { 1.330 + char16_t* htmlBody= nullptr; 1.331 + int32_t htmlBodyLen = 0; 1.332 + // Convert text/html into our unicode format 1.333 + ConvertHTMLtoUCS2(const_cast<guchar*>(clipboardData), length, 1.334 + &htmlBody, htmlBodyLen); 1.335 + // Try next data format? 1.336 + if (!htmlBodyLen) 1.337 + continue; 1.338 + data = (guchar *)htmlBody; 1.339 + length = htmlBodyLen * 2; 1.340 + } else { 1.341 + data = (guchar *)nsMemory::Alloc(length); 1.342 + if (!data) 1.343 + break; 1.344 + memcpy(data, clipboardData, length); 1.345 + } 1.346 + gtk_selection_data_free(selectionData); 1.347 + foundData = true; 1.348 + foundFlavor = flavorStr; 1.349 + break; 1.350 + } 1.351 + } 1.352 + } 1.353 + 1.354 + if (foundData) { 1.355 + nsCOMPtr<nsISupports> wrapper; 1.356 + nsPrimitiveHelpers::CreatePrimitiveForData(foundFlavor.get(), 1.357 + data, length, 1.358 + getter_AddRefs(wrapper)); 1.359 + aTransferable->SetTransferData(foundFlavor.get(), 1.360 + wrapper, length); 1.361 + } 1.362 + 1.363 + if (data) 1.364 + nsMemory::Free(data); 1.365 + 1.366 + return NS_OK; 1.367 +} 1.368 + 1.369 +NS_IMETHODIMP 1.370 +nsClipboard::EmptyClipboard(int32_t aWhichClipboard) 1.371 +{ 1.372 + if (aWhichClipboard == kSelectionClipboard) { 1.373 + if (mSelectionOwner) { 1.374 + mSelectionOwner->LosingOwnership(mSelectionTransferable); 1.375 + mSelectionOwner = nullptr; 1.376 + } 1.377 + mSelectionTransferable = nullptr; 1.378 + } 1.379 + else { 1.380 + if (mGlobalOwner) { 1.381 + mGlobalOwner->LosingOwnership(mGlobalTransferable); 1.382 + mGlobalOwner = nullptr; 1.383 + } 1.384 + mGlobalTransferable = nullptr; 1.385 + } 1.386 + 1.387 + return NS_OK; 1.388 +} 1.389 + 1.390 +NS_IMETHODIMP 1.391 +nsClipboard::HasDataMatchingFlavors(const char** aFlavorList, uint32_t aLength, 1.392 + int32_t aWhichClipboard, bool *_retval) 1.393 +{ 1.394 + if (!aFlavorList || !_retval) 1.395 + return NS_ERROR_NULL_POINTER; 1.396 + 1.397 + *_retval = false; 1.398 + 1.399 + GtkSelectionData *selection_data = 1.400 + GetTargets(GetSelectionAtom(aWhichClipboard)); 1.401 + if (!selection_data) 1.402 + return NS_OK; 1.403 + 1.404 + gint n_targets = 0; 1.405 + GdkAtom *targets = nullptr; 1.406 + 1.407 + if (!gtk_selection_data_get_targets(selection_data, 1.408 + &targets, &n_targets) || 1.409 + !n_targets) 1.410 + return NS_OK; 1.411 + 1.412 + // Walk through the provided types and try to match it to a 1.413 + // provided type. 1.414 + for (uint32_t i = 0; i < aLength && !*_retval; i++) { 1.415 + // We special case text/unicode here. 1.416 + if (!strcmp(aFlavorList[i], kUnicodeMime) && 1.417 + gtk_selection_data_targets_include_text(selection_data)) { 1.418 + *_retval = true; 1.419 + break; 1.420 + } 1.421 + 1.422 + for (int32_t j = 0; j < n_targets; j++) { 1.423 + gchar *atom_name = gdk_atom_name(targets[j]); 1.424 + if (!atom_name) 1.425 + continue; 1.426 + 1.427 + if (!strcmp(atom_name, aFlavorList[i])) 1.428 + *_retval = true; 1.429 + 1.430 + // X clipboard supports image/jpeg, but we want to emulate support 1.431 + // for image/jpg as well 1.432 + if (!strcmp(aFlavorList[i], kJPGImageMime) && !strcmp(atom_name, kJPEGImageMime)) 1.433 + *_retval = true; 1.434 + 1.435 + g_free(atom_name); 1.436 + 1.437 + if (*_retval) 1.438 + break; 1.439 + } 1.440 + } 1.441 + gtk_selection_data_free(selection_data); 1.442 + g_free(targets); 1.443 + 1.444 + return NS_OK; 1.445 +} 1.446 + 1.447 +NS_IMETHODIMP 1.448 +nsClipboard::SupportsSelectionClipboard(bool *_retval) 1.449 +{ 1.450 + *_retval = true; // yeah, unix supports the selection clipboard 1.451 + return NS_OK; 1.452 +} 1.453 + 1.454 +NS_IMETHODIMP 1.455 +nsClipboard::SupportsFindClipboard(bool* _retval) 1.456 +{ 1.457 + *_retval = false; 1.458 + return NS_OK; 1.459 +} 1.460 + 1.461 +/* static */ 1.462 +GdkAtom 1.463 +nsClipboard::GetSelectionAtom(int32_t aWhichClipboard) 1.464 +{ 1.465 + if (aWhichClipboard == kGlobalClipboard) 1.466 + return GDK_SELECTION_CLIPBOARD; 1.467 + 1.468 + return GDK_SELECTION_PRIMARY; 1.469 +} 1.470 + 1.471 +/* static */ 1.472 +GtkSelectionData * 1.473 +nsClipboard::GetTargets(GdkAtom aWhichClipboard) 1.474 +{ 1.475 + GtkClipboard *clipboard = gtk_clipboard_get(aWhichClipboard); 1.476 + return wait_for_contents(clipboard, gdk_atom_intern("TARGETS", FALSE)); 1.477 +} 1.478 + 1.479 +nsITransferable * 1.480 +nsClipboard::GetTransferable(int32_t aWhichClipboard) 1.481 +{ 1.482 + nsITransferable *retval; 1.483 + 1.484 + if (aWhichClipboard == kSelectionClipboard) 1.485 + retval = mSelectionTransferable.get(); 1.486 + else 1.487 + retval = mGlobalTransferable.get(); 1.488 + 1.489 + return retval; 1.490 +} 1.491 + 1.492 +void 1.493 +nsClipboard::SelectionGetEvent(GtkClipboard *aClipboard, 1.494 + GtkSelectionData *aSelectionData) 1.495 +{ 1.496 + // Someone has asked us to hand them something. The first thing 1.497 + // that we want to do is see if that something includes text. If 1.498 + // it does, try to give it text/unicode after converting it to 1.499 + // utf-8. 1.500 + 1.501 + int32_t whichClipboard; 1.502 + 1.503 + // which clipboard? 1.504 + GdkAtom selection = gtk_selection_data_get_selection(aSelectionData); 1.505 + if (selection == GDK_SELECTION_PRIMARY) 1.506 + whichClipboard = kSelectionClipboard; 1.507 + else if (selection == GDK_SELECTION_CLIPBOARD) 1.508 + whichClipboard = kGlobalClipboard; 1.509 + else 1.510 + return; // THAT AIN'T NO CLIPBOARD I EVER HEARD OF 1.511 + 1.512 + nsCOMPtr<nsITransferable> trans = GetTransferable(whichClipboard); 1.513 + if (!trans) { 1.514 + // We have nothing to serve 1.515 +#ifdef DEBUG_CLIPBOARD 1.516 + printf("nsClipboard::SelectionGetEvent() - %s clipboard is empty!\n", 1.517 + whichClipboard == kSelectionClipboard ? "Selection" : "Global"); 1.518 +#endif 1.519 + return; 1.520 + } 1.521 + 1.522 + nsresult rv; 1.523 + nsCOMPtr<nsISupports> item; 1.524 + uint32_t len; 1.525 + 1.526 + 1.527 + GdkAtom selectionTarget = gtk_selection_data_get_target(aSelectionData); 1.528 + 1.529 + // Check to see if the selection data includes any of the string 1.530 + // types that we support. 1.531 + if (selectionTarget == gdk_atom_intern ("STRING", FALSE) || 1.532 + selectionTarget == gdk_atom_intern ("TEXT", FALSE) || 1.533 + selectionTarget == gdk_atom_intern ("COMPOUND_TEXT", FALSE) || 1.534 + selectionTarget == gdk_atom_intern ("UTF8_STRING", FALSE)) { 1.535 + // Try to convert our internal type into a text string. Get 1.536 + // the transferable for this clipboard and try to get the 1.537 + // text/unicode type for it. 1.538 + rv = trans->GetTransferData("text/unicode", getter_AddRefs(item), 1.539 + &len); 1.540 + if (!item || NS_FAILED(rv)) 1.541 + return; 1.542 + 1.543 + nsCOMPtr<nsISupportsString> wideString; 1.544 + wideString = do_QueryInterface(item); 1.545 + if (!wideString) 1.546 + return; 1.547 + 1.548 + nsAutoString ucs2string; 1.549 + wideString->GetData(ucs2string); 1.550 + char *utf8string = ToNewUTF8String(ucs2string); 1.551 + if (!utf8string) 1.552 + return; 1.553 + 1.554 + gtk_selection_data_set_text (aSelectionData, utf8string, 1.555 + strlen(utf8string)); 1.556 + 1.557 + nsMemory::Free(utf8string); 1.558 + return; 1.559 + } 1.560 + 1.561 + // Check to see if the selection data is an image type 1.562 + if (gtk_targets_include_image(&selectionTarget, 1, TRUE)) { 1.563 + // Look through our transfer data for the image 1.564 + static const char* const imageMimeTypes[] = { 1.565 + kNativeImageMime, kPNGImageMime, kJPEGImageMime, kJPGImageMime, kGIFImageMime }; 1.566 + nsCOMPtr<nsISupports> item; 1.567 + uint32_t len; 1.568 + nsCOMPtr<nsISupportsInterfacePointer> ptrPrimitive; 1.569 + for (uint32_t i = 0; !ptrPrimitive && i < ArrayLength(imageMimeTypes); i++) { 1.570 + rv = trans->GetTransferData(imageMimeTypes[i], getter_AddRefs(item), &len); 1.571 + ptrPrimitive = do_QueryInterface(item); 1.572 + } 1.573 + if (!ptrPrimitive) 1.574 + return; 1.575 + 1.576 + nsCOMPtr<nsISupports> primitiveData; 1.577 + ptrPrimitive->GetData(getter_AddRefs(primitiveData)); 1.578 + nsCOMPtr<imgIContainer> image(do_QueryInterface(primitiveData)); 1.579 + if (!image) // Not getting an image for an image mime type!? 1.580 + return; 1.581 + 1.582 + GdkPixbuf* pixbuf = nsImageToPixbuf::ImageToPixbuf(image); 1.583 + if (!pixbuf) 1.584 + return; 1.585 + 1.586 + gtk_selection_data_set_pixbuf(aSelectionData, pixbuf); 1.587 + g_object_unref(pixbuf); 1.588 + return; 1.589 + } 1.590 + 1.591 + // Try to match up the selection data target to something our 1.592 + // transferable provides. 1.593 + gchar *target_name = gdk_atom_name(selectionTarget); 1.594 + if (!target_name) 1.595 + return; 1.596 + 1.597 + rv = trans->GetTransferData(target_name, getter_AddRefs(item), &len); 1.598 + // nothing found? 1.599 + if (!item || NS_FAILED(rv)) { 1.600 + g_free(target_name); 1.601 + return; 1.602 + } 1.603 + 1.604 + void *primitive_data = nullptr; 1.605 + nsPrimitiveHelpers::CreateDataFromPrimitive(target_name, item, 1.606 + &primitive_data, len); 1.607 + 1.608 + if (primitive_data) { 1.609 + // Check to see if the selection data is text/html 1.610 + if (selectionTarget == gdk_atom_intern (kHTMLMime, FALSE)) { 1.611 + /* 1.612 + * "text/html" can be encoded UCS2. It is recommended that 1.613 + * documents transmitted as UCS2 always begin with a ZERO-WIDTH 1.614 + * NON-BREAKING SPACE character (hexadecimal FEFF, also called 1.615 + * Byte Order Mark (BOM)). Adding BOM can help other app to 1.616 + * detect mozilla use UCS2 encoding when copy-paste. 1.617 + */ 1.618 + guchar *buffer = (guchar *) 1.619 + nsMemory::Alloc((len * sizeof(guchar)) + sizeof(char16_t)); 1.620 + if (!buffer) 1.621 + return; 1.622 + char16_t prefix = 0xFEFF; 1.623 + memcpy(buffer, &prefix, sizeof(prefix)); 1.624 + memcpy(buffer + sizeof(prefix), primitive_data, len); 1.625 + nsMemory::Free((guchar *)primitive_data); 1.626 + primitive_data = (guchar *)buffer; 1.627 + len += sizeof(prefix); 1.628 + } 1.629 + 1.630 + gtk_selection_data_set(aSelectionData, selectionTarget, 1.631 + 8, /* 8 bits in a unit */ 1.632 + (const guchar *)primitive_data, len); 1.633 + nsMemory::Free(primitive_data); 1.634 + } 1.635 + 1.636 + g_free(target_name); 1.637 + 1.638 +} 1.639 + 1.640 +void 1.641 +nsClipboard::SelectionClearEvent(GtkClipboard *aGtkClipboard) 1.642 +{ 1.643 + int32_t whichClipboard; 1.644 + 1.645 + // which clipboard? 1.646 + if (aGtkClipboard == gtk_clipboard_get(GDK_SELECTION_PRIMARY)) 1.647 + whichClipboard = kSelectionClipboard; 1.648 + else if (aGtkClipboard == gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)) 1.649 + whichClipboard = kGlobalClipboard; 1.650 + else 1.651 + return; // THAT AIN'T NO CLIPBOARD I EVER HEARD OF 1.652 + 1.653 + EmptyClipboard(whichClipboard); 1.654 +} 1.655 + 1.656 +void 1.657 +clipboard_get_cb(GtkClipboard *aGtkClipboard, 1.658 + GtkSelectionData *aSelectionData, 1.659 + guint info, 1.660 + gpointer user_data) 1.661 +{ 1.662 + nsClipboard *aClipboard = static_cast<nsClipboard *>(user_data); 1.663 + aClipboard->SelectionGetEvent(aGtkClipboard, aSelectionData); 1.664 +} 1.665 + 1.666 +void 1.667 +clipboard_clear_cb(GtkClipboard *aGtkClipboard, 1.668 + gpointer user_data) 1.669 +{ 1.670 + nsClipboard *aClipboard = static_cast<nsClipboard *>(user_data); 1.671 + aClipboard->SelectionClearEvent(aGtkClipboard); 1.672 +} 1.673 + 1.674 +/* 1.675 + * when copy-paste, mozilla wants data encoded using UCS2, 1.676 + * other app such as StarOffice use "text/html"(RFC2854). 1.677 + * This function convert data(got from GTK clipboard) 1.678 + * to data mozilla wanted. 1.679 + * 1.680 + * data from GTK clipboard can be 3 forms: 1.681 + * 1. From current mozilla 1.682 + * "text/html", charset = utf-16 1.683 + * 2. From old version mozilla or mozilla-based app 1.684 + * content("body" only), charset = utf-16 1.685 + * 3. From other app who use "text/html" when copy-paste 1.686 + * "text/html", has "charset" info 1.687 + * 1.688 + * data : got from GTK clipboard 1.689 + * dataLength: got from GTK clipboard 1.690 + * body : pass to Mozilla 1.691 + * bodyLength: pass to Mozilla 1.692 + */ 1.693 +void ConvertHTMLtoUCS2(guchar * data, int32_t dataLength, 1.694 + char16_t** unicodeData, int32_t& outUnicodeLen) 1.695 +{ 1.696 + nsAutoCString charset; 1.697 + GetHTMLCharset(data, dataLength, charset);// get charset of HTML 1.698 + if (charset.EqualsLiteral("UTF-16")) {//current mozilla 1.699 + outUnicodeLen = (dataLength / 2) - 1; 1.700 + *unicodeData = reinterpret_cast<char16_t*> 1.701 + (nsMemory::Alloc((outUnicodeLen + sizeof('\0')) * 1.702 + sizeof(char16_t))); 1.703 + if (*unicodeData) { 1.704 + memcpy(*unicodeData, data + sizeof(char16_t), 1.705 + outUnicodeLen * sizeof(char16_t)); 1.706 + (*unicodeData)[outUnicodeLen] = '\0'; 1.707 + } 1.708 + } else if (charset.EqualsLiteral("UNKNOWN")) { 1.709 + outUnicodeLen = 0; 1.710 + return; 1.711 + } else { 1.712 + // app which use "text/html" to copy&paste 1.713 + nsCOMPtr<nsIUnicodeDecoder> decoder; 1.714 + nsresult rv; 1.715 + // get the decoder 1.716 + nsCOMPtr<nsICharsetConverterManager> ccm = 1.717 + do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv); 1.718 + if (NS_FAILED(rv)) { 1.719 +#ifdef DEBUG_CLIPBOARD 1.720 + g_print(" can't get CHARSET CONVERTER MANAGER service\n"); 1.721 +#endif 1.722 + outUnicodeLen = 0; 1.723 + return; 1.724 + } 1.725 + rv = ccm->GetUnicodeDecoder(charset.get(), getter_AddRefs(decoder)); 1.726 + if (NS_FAILED(rv)) { 1.727 +#ifdef DEBUG_CLIPBOARD 1.728 + g_print(" get unicode decoder error\n"); 1.729 +#endif 1.730 + outUnicodeLen = 0; 1.731 + return; 1.732 + } 1.733 + // converting 1.734 + decoder->GetMaxLength((const char *)data, dataLength, &outUnicodeLen); 1.735 + // |outUnicodeLen| is number of chars 1.736 + if (outUnicodeLen) { 1.737 + *unicodeData = reinterpret_cast<char16_t*> 1.738 + (nsMemory::Alloc((outUnicodeLen + sizeof('\0')) * 1.739 + sizeof(char16_t))); 1.740 + if (*unicodeData) { 1.741 + int32_t numberTmp = dataLength; 1.742 + decoder->Convert((const char *)data, &numberTmp, 1.743 + *unicodeData, &outUnicodeLen); 1.744 +#ifdef DEBUG_CLIPBOARD 1.745 + if (numberTmp != dataLength) 1.746 + printf("didn't consume all the bytes\n"); 1.747 +#endif 1.748 + // null terminate. Convert() doesn't do it for us 1.749 + (*unicodeData)[outUnicodeLen] = '\0'; 1.750 + } 1.751 + } // if valid length 1.752 + } 1.753 +} 1.754 + 1.755 +/* 1.756 + * get "charset" information from clipboard data 1.757 + * return value can be: 1.758 + * 1. "UTF-16": mozilla or "text/html" with "charset=utf-16" 1.759 + * 2. "UNKNOWN": mozilla can't detect what encode it use 1.760 + * 3. other: "text/html" with other charset than utf-16 1.761 + */ 1.762 +void GetHTMLCharset(guchar * data, int32_t dataLength, nsCString& str) 1.763 +{ 1.764 + // if detect "FFFE" or "FEFF", assume UTF-16 1.765 + char16_t* beginChar = (char16_t*)data; 1.766 + if ((beginChar[0] == 0xFFFE) || (beginChar[0] == 0xFEFF)) { 1.767 + str.AssignLiteral("UTF-16"); 1.768 + return; 1.769 + } 1.770 + // no "FFFE" and "FEFF", assume ASCII first to find "charset" info 1.771 + const nsDependentCString htmlStr((const char *)data, dataLength); 1.772 + nsACString::const_iterator start, end; 1.773 + htmlStr.BeginReading(start); 1.774 + htmlStr.EndReading(end); 1.775 + nsACString::const_iterator valueStart(start), valueEnd(start); 1.776 + 1.777 + if (CaseInsensitiveFindInReadable( 1.778 + NS_LITERAL_CSTRING("CONTENT=\"text/html;"), 1.779 + start, end)) { 1.780 + start = end; 1.781 + htmlStr.EndReading(end); 1.782 + 1.783 + if (CaseInsensitiveFindInReadable( 1.784 + NS_LITERAL_CSTRING("charset="), 1.785 + start, end)) { 1.786 + valueStart = end; 1.787 + start = end; 1.788 + htmlStr.EndReading(end); 1.789 + 1.790 + if (FindCharInReadable('"', start, end)) 1.791 + valueEnd = start; 1.792 + } 1.793 + } 1.794 + // find "charset" in HTML 1.795 + if (valueStart != valueEnd) { 1.796 + str = Substring(valueStart, valueEnd); 1.797 + ToUpperCase(str); 1.798 +#ifdef DEBUG_CLIPBOARD 1.799 + printf("Charset of HTML = %s\n", charsetUpperStr.get()); 1.800 +#endif 1.801 + return; 1.802 + } 1.803 + str.AssignLiteral("UNKNOWN"); 1.804 +} 1.805 + 1.806 +static void 1.807 +DispatchSelectionNotifyEvent(GtkWidget *widget, XEvent *xevent) 1.808 +{ 1.809 + GdkEvent event; 1.810 + event.selection.type = GDK_SELECTION_NOTIFY; 1.811 + event.selection.window = gtk_widget_get_window(widget); 1.812 + event.selection.selection = gdk_x11_xatom_to_atom(xevent->xselection.selection); 1.813 + event.selection.target = gdk_x11_xatom_to_atom(xevent->xselection.target); 1.814 + event.selection.property = gdk_x11_xatom_to_atom(xevent->xselection.property); 1.815 + event.selection.time = xevent->xselection.time; 1.816 + 1.817 + gtk_widget_event(widget, &event); 1.818 +} 1.819 + 1.820 +static void 1.821 +DispatchPropertyNotifyEvent(GtkWidget *widget, XEvent *xevent) 1.822 +{ 1.823 + GdkWindow *window = gtk_widget_get_window(widget); 1.824 + if ((gdk_window_get_events(window)) & GDK_PROPERTY_CHANGE_MASK) { 1.825 + GdkEvent event; 1.826 + event.property.type = GDK_PROPERTY_NOTIFY; 1.827 + event.property.window = window; 1.828 + event.property.atom = gdk_x11_xatom_to_atom(xevent->xproperty.atom); 1.829 + event.property.time = xevent->xproperty.time; 1.830 + event.property.state = xevent->xproperty.state; 1.831 + 1.832 + gtk_widget_event(widget, &event); 1.833 + } 1.834 +} 1.835 + 1.836 +struct checkEventContext 1.837 +{ 1.838 + GtkWidget *cbWidget; 1.839 + Atom selAtom; 1.840 +}; 1.841 + 1.842 +static Bool 1.843 +checkEventProc(Display *display, XEvent *event, XPointer arg) 1.844 +{ 1.845 + checkEventContext *context = (checkEventContext *) arg; 1.846 + 1.847 + if (event->xany.type == SelectionNotify || 1.848 + (event->xany.type == PropertyNotify && 1.849 + event->xproperty.atom == context->selAtom)) { 1.850 + 1.851 + GdkWindow *cbWindow = 1.852 + gdk_x11_window_lookup_for_display(gdk_x11_lookup_xdisplay(display), 1.853 + event->xany.window); 1.854 + if (cbWindow) { 1.855 + GtkWidget *cbWidget = nullptr; 1.856 + gdk_window_get_user_data(cbWindow, (gpointer *)&cbWidget); 1.857 + if (cbWidget && GTK_IS_WIDGET(cbWidget)) { 1.858 + context->cbWidget = cbWidget; 1.859 + return True; 1.860 + } 1.861 + } 1.862 + } 1.863 + 1.864 + return False; 1.865 +} 1.866 + 1.867 +// Idle timeout for receiving selection and property notify events (microsec) 1.868 +static const int kClipboardTimeout = 500000; 1.869 + 1.870 +static gchar* CopyRetrievedData(const gchar *aData) 1.871 +{ 1.872 + return g_strdup(aData); 1.873 +} 1.874 + 1.875 +static GtkSelectionData* CopyRetrievedData(GtkSelectionData *aData) 1.876 +{ 1.877 + // A negative length indicates that retrieving the data failed. 1.878 + return gtk_selection_data_get_length(aData) >= 0 ? 1.879 + gtk_selection_data_copy(aData) : nullptr; 1.880 +} 1.881 + 1.882 +class RetrievalContext { 1.883 + ~RetrievalContext() 1.884 + { 1.885 + MOZ_ASSERT(!mData, "Wait() wasn't called"); 1.886 + } 1.887 + 1.888 +public: 1.889 + NS_INLINE_DECL_REFCOUNTING(RetrievalContext) 1.890 + enum State { INITIAL, COMPLETED, TIMED_OUT }; 1.891 + 1.892 + RetrievalContext() : mState(INITIAL), mData(nullptr) {} 1.893 + 1.894 + /** 1.895 + * Call this when data has been retrieved. 1.896 + */ 1.897 + template <class T> void Complete(T *aData) 1.898 + { 1.899 + if (mState == INITIAL) { 1.900 + mState = COMPLETED; 1.901 + mData = CopyRetrievedData(aData); 1.902 + } else { 1.903 + // Already timed out 1.904 + MOZ_ASSERT(mState == TIMED_OUT); 1.905 + } 1.906 + } 1.907 + 1.908 + /** 1.909 + * Spins X event loop until timing out or being completed. Returns 1.910 + * null if we time out, otherwise returns the completed data (passing 1.911 + * ownership to caller). 1.912 + */ 1.913 + void *Wait(); 1.914 + 1.915 +protected: 1.916 + State mState; 1.917 + void* mData; 1.918 +}; 1.919 + 1.920 +void * 1.921 +RetrievalContext::Wait() 1.922 +{ 1.923 + if (mState == COMPLETED) { // the request completed synchronously 1.924 + void *data = mData; 1.925 + mData = nullptr; 1.926 + return data; 1.927 + } 1.928 + 1.929 + Display *xDisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()) ; 1.930 + checkEventContext context; 1.931 + context.cbWidget = nullptr; 1.932 + context.selAtom = gdk_x11_atom_to_xatom(gdk_atom_intern("GDK_SELECTION", 1.933 + FALSE)); 1.934 + 1.935 + // Send X events which are relevant to the ongoing selection retrieval 1.936 + // to the clipboard widget. Wait until either the operation completes, or 1.937 + // we hit our timeout. All other X events remain queued. 1.938 + 1.939 + int select_result; 1.940 + 1.941 + int cnumber = ConnectionNumber(xDisplay); 1.942 + fd_set select_set; 1.943 + FD_ZERO(&select_set); 1.944 + FD_SET(cnumber, &select_set); 1.945 + ++cnumber; 1.946 + TimeStamp start = TimeStamp::Now(); 1.947 + 1.948 + do { 1.949 + XEvent xevent; 1.950 + 1.951 + while (XCheckIfEvent(xDisplay, &xevent, checkEventProc, 1.952 + (XPointer) &context)) { 1.953 + 1.954 + if (xevent.xany.type == SelectionNotify) 1.955 + DispatchSelectionNotifyEvent(context.cbWidget, &xevent); 1.956 + else 1.957 + DispatchPropertyNotifyEvent(context.cbWidget, &xevent); 1.958 + 1.959 + if (mState == COMPLETED) { 1.960 + void *data = mData; 1.961 + mData = nullptr; 1.962 + return data; 1.963 + } 1.964 + } 1.965 + 1.966 + TimeStamp now = TimeStamp::Now(); 1.967 + struct timeval tv; 1.968 + tv.tv_sec = 0; 1.969 + tv.tv_usec = std::max<int32_t>(0, 1.970 + kClipboardTimeout - (now - start).ToMicroseconds()); 1.971 + select_result = select(cnumber, &select_set, nullptr, nullptr, &tv); 1.972 + } while (select_result == 1 || 1.973 + (select_result == -1 && errno == EINTR)); 1.974 + 1.975 +#ifdef DEBUG_CLIPBOARD 1.976 + printf("exceeded clipboard timeout\n"); 1.977 +#endif 1.978 + mState = TIMED_OUT; 1.979 + return nullptr; 1.980 +} 1.981 + 1.982 +static void 1.983 +clipboard_contents_received(GtkClipboard *clipboard, 1.984 + GtkSelectionData *selection_data, 1.985 + gpointer data) 1.986 +{ 1.987 + RetrievalContext *context = static_cast<RetrievalContext*>(data); 1.988 + context->Complete(selection_data); 1.989 + context->Release(); 1.990 +} 1.991 + 1.992 +static GtkSelectionData * 1.993 +wait_for_contents(GtkClipboard *clipboard, GdkAtom target) 1.994 +{ 1.995 + RefPtr<RetrievalContext> context = new RetrievalContext(); 1.996 + // Balanced by Release in clipboard_contents_received 1.997 + context->AddRef(); 1.998 + gtk_clipboard_request_contents(clipboard, target, 1.999 + clipboard_contents_received, 1.1000 + context.get()); 1.1001 + return static_cast<GtkSelectionData*>(context->Wait()); 1.1002 +} 1.1003 + 1.1004 +static void 1.1005 +clipboard_text_received(GtkClipboard *clipboard, 1.1006 + const gchar *text, 1.1007 + gpointer data) 1.1008 +{ 1.1009 + RetrievalContext *context = static_cast<RetrievalContext*>(data); 1.1010 + context->Complete(text); 1.1011 + context->Release(); 1.1012 +} 1.1013 + 1.1014 +static gchar * 1.1015 +wait_for_text(GtkClipboard *clipboard) 1.1016 +{ 1.1017 + RefPtr<RetrievalContext> context = new RetrievalContext(); 1.1018 + // Balanced by Release in clipboard_text_received 1.1019 + context->AddRef(); 1.1020 + gtk_clipboard_request_text(clipboard, clipboard_text_received, context.get()); 1.1021 + return static_cast<gchar*>(context->Wait()); 1.1022 +}