1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:expandtab:shiftwidth=2:tabstop=2:
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "mozilla/ArrayUtils.h"
10 #include "nsArrayUtils.h"
11 #include "nsClipboard.h"
13 # include "nsClipboardX11.h"
15 #if defined(MOZ_WAYLAND)
16 # include "nsClipboardWayland.h"
17 # include "nsWaylandDisplay.h"
19 #include "nsGtkUtils.h"
22 #include "nsNetUtil.h"
23 #include "nsContentUtils.h"
24 #include "HeadlessClipboard.h"
25 #include "nsSupportsPrimitives.h"
27 #include "nsReadableUtils.h"
28 #include "nsPrimitiveHelpers.h"
29 #include "nsImageToPixbuf.h"
30 #include "nsStringStream.h"
31 #include "nsIFileURL.h"
32 #include "nsIObserverService.h"
33 #include "mozilla/GRefPtr.h"
34 #include "mozilla/RefPtr.h"
35 #include "mozilla/SchedulerGroup.h"
36 #include "mozilla/Services.h"
37 #include "mozilla/StaticPrefs_widget.h"
38 #include "mozilla/TimeStamp.h"
40 #include "WidgetUtilsGtk.h"
42 #include "imgIContainer.h"
46 # include <gtk/gtkx.h>
49 #include "mozilla/Encoding.h"
51 using namespace mozilla
;
53 // Idle timeout for receiving selection and property notify events (microsec)
54 // Right now it's set to 1 sec.
55 const int kClipboardTimeout
= 1000000;
57 // Defines how many event loop iterations will be done without sleep.
58 // We ususally get data in first 2-3 iterations unless some large object
59 // (an image for instance) is transferred through clipboard.
60 const int kClipboardFastIterationNum
= 3;
62 // We add this prefix to HTML markup, so that GetHTMLCharset can correctly
63 // detect the HTML as UTF-8 encoded.
64 static const char kHTMLMarkupPrefix
[] =
65 R
"(<meta http-equiv="content
-type
" content="text
/html
; charset
=utf
-8">)";
67 static const char kURIListMime
[] = "text/uri-list";
69 MOZ_CONSTINIT ClipboardTargets
nsRetrievalContext::sClipboardTargets
;
70 MOZ_CONSTINIT ClipboardTargets
nsRetrievalContext::sPrimaryTargets
;
72 // Callback when someone asks us for the data
73 static void clipboard_get_cb(GtkClipboard
* aGtkClipboard
,
74 GtkSelectionData
* aSelectionData
, guint info
,
77 // Callback when someone asks us to clear a clipboard
78 static void clipboard_clear_cb(GtkClipboard
* aGtkClipboard
, gpointer user_data
);
80 // Callback when owner of clipboard data is changed
81 static void clipboard_owner_change_cb(GtkClipboard
* aGtkClipboard
,
82 GdkEventOwnerChange
* aEvent
,
85 static bool GetHTMLCharset(Span
<const char> aData
, nsCString
& str
);
87 static void SetTransferableData(nsITransferable
* aTransferable
,
88 const nsACString
& aFlavor
,
89 const char* aClipboardData
,
90 uint32_t aClipboardDataLength
) {
91 MOZ_CLIPBOARD_LOG("SetTransferableData MIME %s\n",
92 PromiseFlatCString(aFlavor
).get());
93 nsCOMPtr
<nsISupports
> wrapper
;
94 nsPrimitiveHelpers::CreatePrimitiveForData(
95 aFlavor
, aClipboardData
, aClipboardDataLength
, getter_AddRefs(wrapper
));
96 aTransferable
->SetTransferData(PromiseFlatCString(aFlavor
).get(), wrapper
);
99 ClipboardTargets
ClipboardTargets::Clone() {
100 ClipboardTargets ret
;
104 reinterpret_cast<GdkAtom
*>(g_malloc(sizeof(GdkAtom
) * mCount
)));
105 memcpy(ret
.mTargets
.get(), mTargets
.get(), sizeof(GdkAtom
) * mCount
);
110 void ClipboardTargets::Set(ClipboardTargets aTargets
) {
111 mCount
= aTargets
.mCount
;
112 mTargets
= std::move(aTargets
.mTargets
);
115 void ClipboardData::SetData(Span
<const uint8_t> aData
) {
117 mLength
= aData
.Length();
119 mData
.reset(reinterpret_cast<char*>(g_malloc(sizeof(char) * mLength
)));
120 memcpy(mData
.get(), aData
.data(), sizeof(char) * mLength
);
124 void ClipboardData::SetText(Span
<const char> aData
) {
126 mLength
= aData
.Length();
129 reinterpret_cast<char*>(g_malloc(sizeof(char) * (mLength
+ 1))));
130 memcpy(mData
.get(), aData
.data(), sizeof(char) * mLength
);
131 mData
.get()[mLength
] = '\0';
135 void ClipboardData::SetTargets(ClipboardTargets aTargets
) {
136 mLength
= aTargets
.mCount
;
137 mData
.reset(reinterpret_cast<char*>(aTargets
.mTargets
.release()));
140 ClipboardTargets
ClipboardData::ExtractTargets() {
141 GUniquePtr
<GdkAtom
> targets(reinterpret_cast<GdkAtom
*>(mData
.release()));
142 uint32_t length
= std::exchange(mLength
, 0);
143 return ClipboardTargets
{std::move(targets
), length
};
146 GdkAtom
GetSelectionAtom(int32_t aWhichClipboard
) {
147 if (aWhichClipboard
== nsIClipboard::kGlobalClipboard
)
148 return GDK_SELECTION_CLIPBOARD
;
150 return GDK_SELECTION_PRIMARY
;
153 Maybe
<nsIClipboard::ClipboardType
> GetGeckoClipboardType(
154 GtkClipboard
* aGtkClipboard
) {
155 if (aGtkClipboard
== gtk_clipboard_get(GDK_SELECTION_PRIMARY
)) {
156 return Some(nsClipboard::kSelectionClipboard
);
158 if (aGtkClipboard
== gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
)) {
159 return Some(nsClipboard::kGlobalClipboard
);
161 return Nothing(); // THAT AIN'T NO CLIPBOARD I EVER HEARD OF
164 void nsRetrievalContext::ClearCachedTargetsClipboard(GtkClipboard
* aClipboard
,
167 MOZ_CLIPBOARD_LOG("nsRetrievalContext::ClearCachedTargetsClipboard()");
168 sClipboardTargets
.Clear();
171 void nsRetrievalContext::ClearCachedTargetsPrimary(GtkClipboard
* aClipboard
,
174 MOZ_CLIPBOARD_LOG("nsRetrievalContext::ClearCachedTargetsPrimary()");
175 sPrimaryTargets
.Clear();
178 ClipboardTargets
nsRetrievalContext::GetTargets(int32_t aWhichClipboard
) {
179 MOZ_CLIPBOARD_LOG("nsRetrievalContext::GetTargets(%s)\n",
180 aWhichClipboard
== nsClipboard::kSelectionClipboard
183 ClipboardTargets
& storedTargets
=
184 (aWhichClipboard
== nsClipboard::kSelectionClipboard
) ? sPrimaryTargets
186 if (!storedTargets
) {
187 MOZ_CLIPBOARD_LOG(" getting targets from system");
188 storedTargets
.Set(GetTargetsImpl(aWhichClipboard
));
190 MOZ_CLIPBOARD_LOG(" using cached targets");
192 return storedTargets
.Clone();
195 nsRetrievalContext::~nsRetrievalContext() {
196 sClipboardTargets
.Clear();
197 sPrimaryTargets
.Clear();
200 nsClipboard::nsClipboard()
201 : nsBaseClipboard(mozilla::dom::ClipboardCapabilities(
203 widget::GdkIsWaylandDisplay()
204 ? widget::WaylandDisplayGet()->IsPrimarySelectionEnabled()
207 true, /* supportsSelectionClipboard */
209 false /* supportsFindClipboard */,
210 false /* supportsSelectionCache */)) {
211 g_signal_connect(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
), "owner-change",
212 G_CALLBACK(clipboard_owner_change_cb
), this);
213 g_signal_connect(gtk_clipboard_get(GDK_SELECTION_PRIMARY
), "owner-change",
214 G_CALLBACK(clipboard_owner_change_cb
), this);
217 nsClipboard::~nsClipboard() {
218 g_signal_handlers_disconnect_by_func(
219 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
),
220 FuncToGpointer(clipboard_owner_change_cb
), this);
221 g_signal_handlers_disconnect_by_func(
222 gtk_clipboard_get(GDK_SELECTION_PRIMARY
),
223 FuncToGpointer(clipboard_owner_change_cb
), this);
226 NS_IMPL_ISUPPORTS_INHERITED(nsClipboard
, nsBaseClipboard
, nsIObserver
)
228 nsresult
nsClipboard::Init(void) {
230 if (widget::GdkIsX11Display()) {
231 mContext
= new nsRetrievalContextX11();
234 #if defined(MOZ_WAYLAND)
235 if (widget::GdkIsWaylandDisplay()) {
236 mContext
= new nsRetrievalContextWayland();
240 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
242 os
->AddObserver(this, "xpcom-shutdown", false);
249 nsClipboard::Observe(nsISupports
* aSubject
, const char* aTopic
,
250 const char16_t
* aData
) {
251 // Save global clipboard content to CLIPBOARD_MANAGER.
252 // gtk_clipboard_store() can run an event loop, so call from a dedicated
254 return SchedulerGroup::Dispatch(
255 NS_NewRunnableFunction("gtk_clipboard_store()", []() {
256 MOZ_CLIPBOARD_LOG("nsClipboard storing clipboard content\n");
257 gtk_clipboard_store(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
));
262 nsClipboard::SetNativeClipboardData(nsITransferable
* aTransferable
,
263 ClipboardType aWhichClipboard
) {
264 MOZ_DIAGNOSTIC_ASSERT(aTransferable
);
265 MOZ_DIAGNOSTIC_ASSERT(
266 nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
));
268 // See if we can short cut
269 if ((aWhichClipboard
== kGlobalClipboard
&&
270 aTransferable
== mGlobalTransferable
.get()) ||
271 (aWhichClipboard
== kSelectionClipboard
&&
272 aTransferable
== mSelectionTransferable
.get())) {
277 "nsClipboard::SetNativeClipboardData (%s)\n",
278 aWhichClipboard
== kSelectionClipboard
? "primary" : "clipboard");
280 // List of suported targets
281 GtkTargetList
* list
= gtk_target_list_new(nullptr, 0);
283 // Get the types of supported flavors
284 nsTArray
<nsCString
> flavors
;
285 nsresult rv
= aTransferable
->FlavorsTransferableCanExport(flavors
);
287 MOZ_CLIPBOARD_LOG(" FlavorsTransferableCanExport failed!\n");
288 // Fall through. |gtkTargets| will be null below.
291 // Add all the flavors to this widget's supported type.
292 bool imagesAdded
= false;
293 for (uint32_t i
= 0; i
< flavors
.Length(); i
++) {
294 nsCString
& flavorStr
= flavors
[i
];
295 MOZ_CLIPBOARD_LOG(" processing target %s\n", flavorStr
.get());
297 // Special case text/plain since we can handle all of the string types.
298 if (flavorStr
.EqualsLiteral(kTextMime
)) {
299 MOZ_CLIPBOARD_LOG(" adding TEXT targets\n");
300 gtk_target_list_add_text_targets(list
, 0);
304 if (nsContentUtils::IsFlavorImage(flavorStr
)) {
305 // Don't bother adding image targets twice
307 // accept any writable image type
308 MOZ_CLIPBOARD_LOG(" adding IMAGE targets\n");
309 gtk_target_list_add_image_targets(list
, 0, TRUE
);
315 if (flavorStr
.EqualsLiteral(kFileMime
)) {
316 MOZ_CLIPBOARD_LOG(" adding text/uri-list target\n");
317 GdkAtom atom
= gdk_atom_intern(kURIListMime
, FALSE
);
318 gtk_target_list_add(list
, atom
, 0, 0);
322 // Add this to our list of valid targets
323 MOZ_CLIPBOARD_LOG(" adding OTHER target %s\n", flavorStr
.get());
324 GdkAtom atom
= gdk_atom_intern(flavorStr
.get(), FALSE
);
325 gtk_target_list_add(list
, atom
, 0, 0);
328 // Get GTK clipboard (CLIPBOARD or PRIMARY)
329 GtkClipboard
* gtkClipboard
=
330 gtk_clipboard_get(GetSelectionAtom(aWhichClipboard
));
333 GtkTargetEntry
* gtkTargets
=
334 gtk_target_table_new_from_list(list
, &numTargets
);
335 if (!gtkTargets
|| numTargets
== 0) {
337 " gtk_target_table_new_from_list() failed or empty list of "
339 // Clear references to the any old data and let GTK know that it is no
341 EmptyNativeClipboardData(aWhichClipboard
);
342 return NS_ERROR_FAILURE
;
345 ClearCachedTargets(aWhichClipboard
);
347 // Set getcallback and request to store data after an application exit
348 if (gtk_clipboard_set_with_data(gtkClipboard
, gtkTargets
, numTargets
,
349 clipboard_get_cb
, clipboard_clear_cb
, this)) {
350 // We managed to set-up the clipboard so update internal state
351 // We have to set it now because gtk_clipboard_set_with_data() calls
352 // clipboard_clear_cb() which reset our internal state
353 if (aWhichClipboard
== kSelectionClipboard
) {
354 mSelectionSequenceNumber
++;
355 mSelectionTransferable
= aTransferable
;
357 mGlobalSequenceNumber
++;
358 mGlobalTransferable
= aTransferable
;
359 gtk_clipboard_set_can_store(gtkClipboard
, gtkTargets
, numTargets
);
364 MOZ_CLIPBOARD_LOG(" gtk_clipboard_set_with_data() failed!\n");
365 EmptyNativeClipboardData(aWhichClipboard
);
366 rv
= NS_ERROR_FAILURE
;
369 gtk_target_table_free(gtkTargets
, numTargets
);
370 gtk_target_list_unref(list
);
375 mozilla::Result
<int32_t, nsresult
>
376 nsClipboard::GetNativeClipboardSequenceNumber(ClipboardType aWhichClipboard
) {
377 MOZ_DIAGNOSTIC_ASSERT(
378 nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
));
379 return aWhichClipboard
== kSelectionClipboard
? mSelectionSequenceNumber
380 : mGlobalSequenceNumber
;
383 static bool IsMIMEAtFlavourList(const nsTArray
<nsCString
>& aFlavourList
,
385 for (const auto& flavorStr
: aFlavourList
) {
386 if (flavorStr
.Equals(aMime
)) {
393 // When clipboard contains only images, X11/Gtk tries to convert them
394 // to text when we request text instead of just fail to provide the data.
395 // So if clipboard contains images only remove text MIME offer.
396 bool nsClipboard::FilterImportedFlavors(int32_t aWhichClipboard
,
397 nsTArray
<nsCString
>& aFlavors
) {
398 MOZ_CLIPBOARD_LOG("nsClipboard::FilterImportedFlavors");
400 auto targets
= mContext
->GetTargets(aWhichClipboard
);
402 MOZ_CLIPBOARD_LOG(" X11: no targes at clipboard (null), quit.\n");
406 for (const auto& atom
: targets
.AsSpan()) {
407 GUniquePtr
<gchar
> atom_name(gdk_atom_name(atom
));
411 // Filter out system MIME types.
412 if (strcmp(atom_name
.get(), "TARGETS") == 0 ||
413 strcmp(atom_name
.get(), "TIMESTAMP") == 0 ||
414 strcmp(atom_name
.get(), "SAVE_TARGETS") == 0 ||
415 strcmp(atom_name
.get(), "MULTIPLE") == 0) {
418 // Filter out types which can't be converted to text.
419 if (strncmp(atom_name
.get(), "image/", 6) == 0 ||
420 strncmp(atom_name
.get(), "application/", 12) == 0 ||
421 strncmp(atom_name
.get(), "audio/", 6) == 0 ||
422 strncmp(atom_name
.get(), "video/", 6) == 0) {
425 // We have some other MIME type on clipboard which can be hopefully
426 // converted to text without any problem.
428 " X11: text types in clipboard, no need to filter them.\n");
432 // So make sure we offer only types we have at clipboard.
433 nsTArray
<nsCString
> clipboardFlavors
;
434 for (const auto& atom
: targets
.AsSpan()) {
435 GUniquePtr
<gchar
> atom_name(gdk_atom_name(atom
));
439 if (IsMIMEAtFlavourList(aFlavors
, atom_name
.get())) {
440 clipboardFlavors
.AppendElement(nsCString(atom_name
.get()));
443 aFlavors
.SwapElements(clipboardFlavors
);
445 MOZ_CLIPBOARD_LOG(" X11: Flavors which match clipboard content:\n");
446 for (uint32_t i
= 0; i
< aFlavors
.Length(); i
++) {
447 MOZ_CLIPBOARD_LOG(" %s\n", aFlavors
[i
].get());
453 static nsresult
GetTransferableFlavors(nsITransferable
* aTransferable
,
454 nsTArray
<nsCString
>& aFlavors
) {
455 if (!aTransferable
) {
456 return NS_ERROR_FAILURE
;
458 // Get a list of flavors this transferable can import
459 nsresult rv
= aTransferable
->FlavorsTransferableCanImport(aFlavors
);
461 MOZ_CLIPBOARD_LOG(" FlavorsTransferableCanImport falied!\n");
465 MOZ_CLIPBOARD_LOG(" Flavors which can be imported:");
466 for (const auto& flavor
: aFlavors
) {
467 MOZ_CLIPBOARD_LOG(" %s", flavor
.get());
473 static bool TransferableSetFile(nsITransferable
* aTransferable
,
474 const nsACString
& aURIList
) {
476 nsTArray
<nsCString
> uris
= mozilla::widget::ParseTextURIList(aURIList
);
477 if (!uris
.IsEmpty()) {
478 nsCOMPtr
<nsIURI
> fileURI
;
479 NS_NewURI(getter_AddRefs(fileURI
), uris
[0]);
480 if (nsCOMPtr
<nsIFileURL
> fileURL
= do_QueryInterface(fileURI
, &rv
)) {
481 nsCOMPtr
<nsIFile
> file
;
482 rv
= fileURL
->GetFile(getter_AddRefs(file
));
483 if (NS_SUCCEEDED(rv
)) {
484 aTransferable
->SetTransferData(kFileMime
, file
);
485 MOZ_CLIPBOARD_LOG(" successfully set file to clipboard\n");
493 static bool TransferableSetHTML(nsITransferable
* aTransferable
,
494 Span
<const char> aData
) {
495 nsLiteralCString
mimeType(kHTMLMime
);
497 // Convert text/html into our text format
498 nsAutoCString charset
;
499 if (!GetHTMLCharset(aData
, charset
)) {
500 // Fall back to utf-8 in case html/data is missing kHTMLMarkupPrefix.
502 "Failed to get html/text encoding, fall back to utf-8.\n");
503 charset
.AssignLiteral("utf-8");
506 MOZ_CLIPBOARD_LOG("TransferableSetHTML: HTML detected charset %s",
508 // app which use "text/html" to copy&paste
510 auto encoding
= Encoding::ForLabelNoReplacement(charset
);
513 "TransferableSetHTML: get unicode decoder error (charset: %s)",
518 // According to spec html UTF-16BE/LE should be switched to UTF-8
519 // https://html.spec.whatwg.org/#determining-the-character-encoding:utf-16-encoding-2
520 if (encoding
== UTF_16LE_ENCODING
|| encoding
== UTF_16BE_ENCODING
) {
521 encoding
= UTF_8_ENCODING
;
524 // Remove kHTMLMarkupPrefix again, it won't necessarily cause any
525 // issues, but might confuse other users.
526 const size_t prefixLen
= std::size(kHTMLMarkupPrefix
) - 1;
527 if (aData
.Length() >= prefixLen
&& nsDependentCSubstring(aData
.To(prefixLen
))
528 .EqualsLiteral(kHTMLMarkupPrefix
)) {
529 aData
= aData
.From(prefixLen
);
532 nsAutoString unicodeData
;
533 auto [rv
, enc
] = encoding
->Decode(AsBytes(aData
), unicodeData
);
535 if (enc
!= UTF_8_ENCODING
&& MOZ_CLIPBOARD_LOG_ENABLED()) {
536 nsCString decoderName
;
537 enc
->Name(decoderName
);
538 MOZ_CLIPBOARD_LOG("TransferableSetHTML: expected UTF-8 decoder but got %s",
543 MOZ_CLIPBOARD_LOG("TransferableSetHTML: failed to decode HTML");
546 SetTransferableData(aTransferable
, mimeType
,
547 (const char*)unicodeData
.BeginReading(),
548 unicodeData
.Length() * sizeof(char16_t
));
553 nsClipboard::GetNativeClipboardData(nsITransferable
* aTransferable
,
554 ClipboardType aWhichClipboard
) {
555 MOZ_DIAGNOSTIC_ASSERT(aTransferable
);
556 MOZ_DIAGNOSTIC_ASSERT(
557 nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
));
560 "nsClipboard::GetNativeClipboardData (%s)\n",
561 aWhichClipboard
== kSelectionClipboard
? "primary" : "clipboard");
563 // TODO: Ensure we don't re-enter here.
565 return NS_ERROR_FAILURE
;
568 nsTArray
<nsCString
> flavors
;
569 nsresult rv
= GetTransferableFlavors(aTransferable
, flavors
);
570 NS_ENSURE_SUCCESS(rv
, rv
);
572 // Filter out MIME types on X11 to prevent unwanted conversions,
574 if (widget::GdkIsX11Display() &&
575 !FilterImportedFlavors(aWhichClipboard
, flavors
)) {
576 MOZ_CLIPBOARD_LOG(" Missing suitable clipboard data, quit.");
580 for (uint32_t i
= 0; i
< flavors
.Length(); i
++) {
581 nsCString
& flavorStr
= flavors
[i
];
583 if (flavorStr
.EqualsLiteral(kJPEGImageMime
) ||
584 flavorStr
.EqualsLiteral(kJPGImageMime
) ||
585 flavorStr
.EqualsLiteral(kPNGImageMime
) ||
586 flavorStr
.EqualsLiteral(kGIFImageMime
)) {
587 // Emulate support for image/jpg
588 if (flavorStr
.EqualsLiteral(kJPGImageMime
)) {
589 flavorStr
.Assign(kJPEGImageMime
);
592 MOZ_CLIPBOARD_LOG(" Getting image %s MIME clipboard data\n",
596 mContext
->GetClipboardData(flavorStr
.get(), aWhichClipboard
);
597 if (!clipboardData
) {
598 MOZ_CLIPBOARD_LOG(" %s type is missing\n", flavorStr
.get());
602 nsCOMPtr
<nsIInputStream
> byteStream
;
603 NS_NewByteInputStream(getter_AddRefs(byteStream
), clipboardData
.AsSpan(),
605 aTransferable
->SetTransferData(flavorStr
.get(), byteStream
);
606 MOZ_CLIPBOARD_LOG(" got %s MIME data\n", flavorStr
.get());
610 // Special case text/plain since we can convert any
611 // string into text/plain
612 if (flavorStr
.EqualsLiteral(kTextMime
)) {
613 MOZ_CLIPBOARD_LOG(" Getting text %s MIME clipboard data\n",
616 auto clipboardData
= mContext
->GetClipboardText(aWhichClipboard
);
617 if (!clipboardData
) {
618 MOZ_CLIPBOARD_LOG(" failed to get text data\n");
619 // If the type was text/plain and we couldn't get
620 // text off the clipboard, run the next loop
625 // Convert utf-8 into our text format.
626 NS_ConvertUTF8toUTF16
ucs2string(clipboardData
.get());
627 SetTransferableData(aTransferable
, flavorStr
,
628 (const char*)ucs2string
.BeginReading(),
629 ucs2string
.Length() * 2);
631 MOZ_CLIPBOARD_LOG(" got text data, length %zd\n", ucs2string
.Length());
635 if (flavorStr
.EqualsLiteral(kFileMime
)) {
636 MOZ_CLIPBOARD_LOG(" Getting %s file clipboard data\n",
640 mContext
->GetClipboardData(kURIListMime
, aWhichClipboard
);
641 if (!clipboardData
) {
642 MOZ_CLIPBOARD_LOG(" text/uri-list type is missing\n");
646 nsDependentCSubstring
fileName(clipboardData
.AsSpan());
647 if (!TransferableSetFile(aTransferable
, fileName
)) {
653 MOZ_CLIPBOARD_LOG(" Getting %s MIME clipboard data\n", flavorStr
.get());
656 mContext
->GetClipboardData(flavorStr
.get(), aWhichClipboard
);
659 if (!clipboardData
) {
660 MOZ_CLIPBOARD_LOG(" %s type is missing\n", flavorStr
.get());
665 MOZ_CLIPBOARD_LOG(" got %s mime type data.\n", flavorStr
.get());
667 // Special case text/html since we can convert into UCS2
668 if (flavorStr
.EqualsLiteral(kHTMLMime
)) {
669 if (!TransferableSetHTML(aTransferable
, clipboardData
.AsSpan())) {
673 auto span
= clipboardData
.AsSpan();
674 SetTransferableData(aTransferable
, flavorStr
, span
.data(),
681 MOZ_CLIPBOARD_LOG(" failed to get clipboard content.\n");
692 struct DataCallbackHandler
{
693 RefPtr
<nsITransferable
> mTransferable
;
694 nsBaseClipboard::GetDataCallback mDataCallback
;
698 explicit DataCallbackHandler(RefPtr
<nsITransferable
> aTransferable
,
699 nsBaseClipboard::GetDataCallback
&& aDataCallback
,
700 const char* aMimeType
,
701 DataType aDataType
= DATATYPE_RAW
)
702 : mTransferable(std::move(aTransferable
)),
703 mDataCallback(std::move(aDataCallback
)),
704 mMimeType(aMimeType
),
705 mDataType(aDataType
) {
706 MOZ_COUNT_CTOR(DataCallbackHandler
);
707 MOZ_CLIPBOARD_LOG("DataCallbackHandler created [%p] MIME %s type %d", this,
708 mMimeType
.get(), mDataType
);
710 ~DataCallbackHandler() {
711 MOZ_CLIPBOARD_LOG("DataCallbackHandler deleted [%p]", this);
712 MOZ_COUNT_DTOR(DataCallbackHandler
);
716 static void AsyncGetTextImpl(nsITransferable
* aTransferable
,
717 int32_t aWhichClipboard
,
718 nsBaseClipboard::GetDataCallback
&& aCallback
) {
719 MOZ_CLIPBOARD_LOG("AsyncGetText() type '%s'",
720 aWhichClipboard
== nsClipboard::kSelectionClipboard
724 gtk_clipboard_request_text(
725 gtk_clipboard_get(GetSelectionAtom(aWhichClipboard
)),
726 [](GtkClipboard
* aClipboard
, const gchar
* aText
, gpointer aData
) -> void {
727 UniquePtr
<DataCallbackHandler
> ref(
728 static_cast<DataCallbackHandler
*>(aData
));
729 MOZ_CLIPBOARD_LOG("AsyncGetText async handler of [%p]", aData
);
731 size_t dataLength
= aText
? strlen(aText
) : 0;
732 if (dataLength
<= 0) {
733 MOZ_CLIPBOARD_LOG(" quit, text is not available");
734 ref
->mDataCallback(NS_OK
);
738 // Convert utf-8 into our unicode format.
739 NS_ConvertUTF8toUTF16
utf16string(aText
, dataLength
);
740 nsLiteralCString
flavor(kTextMime
);
741 SetTransferableData(ref
->mTransferable
, flavor
,
742 (const char*)utf16string
.BeginReading(),
743 utf16string
.Length() * 2);
744 MOZ_CLIPBOARD_LOG(" text is set, length = %d", (int)dataLength
);
745 ref
->mDataCallback(NS_OK
);
747 new DataCallbackHandler(aTransferable
, std::move(aCallback
), kTextMime
));
750 static void AsyncGetDataImpl(nsITransferable
* aTransferable
,
751 int32_t aWhichClipboard
, const char* aMimeType
,
753 nsBaseClipboard::GetDataCallback
&& aCallback
) {
754 MOZ_CLIPBOARD_LOG("AsyncGetData() type '%s'",
755 aWhichClipboard
== nsClipboard::kSelectionClipboard
759 const char* gtkMIMEType
= nullptr;
762 // Don't ask Gtk for application/x-moz-file
763 gtkMIMEType
= kURIListMime
;
768 gtkMIMEType
= aMimeType
;
772 gtk_clipboard_request_contents(
773 gtk_clipboard_get(GetSelectionAtom(aWhichClipboard
)),
774 gdk_atom_intern(gtkMIMEType
, FALSE
),
775 [](GtkClipboard
* aClipboard
, GtkSelectionData
* aSelection
,
776 gpointer aData
) -> void {
777 UniquePtr
<DataCallbackHandler
> ref(
778 static_cast<DataCallbackHandler
*>(aData
));
779 MOZ_CLIPBOARD_LOG("AsyncGetData async handler [%p] MIME %s type %d",
780 aData
, ref
->mMimeType
.get(), ref
->mDataType
);
782 int dataLength
= gtk_selection_data_get_length(aSelection
);
783 if (dataLength
<= 0) {
784 ref
->mDataCallback(NS_OK
);
787 const char* data
= (const char*)gtk_selection_data_get_data(aSelection
);
789 ref
->mDataCallback(NS_OK
);
792 switch (ref
->mDataType
) {
793 case DATATYPE_IMAGE
: {
794 MOZ_CLIPBOARD_LOG(" set image clipboard data");
795 nsCOMPtr
<nsIInputStream
> byteStream
;
796 NS_NewByteInputStream(getter_AddRefs(byteStream
),
797 Span(data
, dataLength
), NS_ASSIGNMENT_COPY
);
798 ref
->mTransferable
->SetTransferData(ref
->mMimeType
.get(),
802 case DATATYPE_FILE
: {
803 MOZ_CLIPBOARD_LOG(" set file clipboard data");
804 nsDependentCSubstring
file(data
, dataLength
);
805 TransferableSetFile(ref
->mTransferable
, file
);
808 case DATATYPE_HTML
: {
809 MOZ_CLIPBOARD_LOG(" html clipboard data");
810 Span
dataSpan(data
, dataLength
);
811 TransferableSetHTML(ref
->mTransferable
, dataSpan
);
815 MOZ_CLIPBOARD_LOG(" raw clipboard data %s", ref
->mMimeType
.get());
816 SetTransferableData(ref
->mTransferable
, ref
->mMimeType
, data
,
821 ref
->mDataCallback(NS_OK
);
823 new DataCallbackHandler(aTransferable
, std::move(aCallback
), aMimeType
,
827 static void AsyncGetDataFlavor(nsITransferable
* aTransferable
,
828 int32_t aWhichClipboard
, nsCString
& aFlavorStr
,
829 nsBaseClipboard::GetDataCallback
&& aCallback
) {
830 if (aFlavorStr
.EqualsLiteral(kJPEGImageMime
) ||
831 aFlavorStr
.EqualsLiteral(kJPGImageMime
) ||
832 aFlavorStr
.EqualsLiteral(kPNGImageMime
) ||
833 aFlavorStr
.EqualsLiteral(kGIFImageMime
)) {
834 // Emulate support for image/jpg
835 if (aFlavorStr
.EqualsLiteral(kJPGImageMime
)) {
836 aFlavorStr
.Assign(kJPEGImageMime
);
838 MOZ_CLIPBOARD_LOG(" Getting image %s MIME clipboard data",
840 AsyncGetDataImpl(aTransferable
, aWhichClipboard
, aFlavorStr
.get(),
841 DATATYPE_IMAGE
, std::move(aCallback
));
844 // Special case text/plain since we can convert any
845 // string into text/plain
846 if (aFlavorStr
.EqualsLiteral(kTextMime
)) {
847 MOZ_CLIPBOARD_LOG(" Getting unicode clipboard data");
848 AsyncGetTextImpl(aTransferable
, aWhichClipboard
, std::move(aCallback
));
851 if (aFlavorStr
.EqualsLiteral(kFileMime
)) {
852 MOZ_CLIPBOARD_LOG(" Getting file clipboard data\n");
853 AsyncGetDataImpl(aTransferable
, aWhichClipboard
, aFlavorStr
.get(),
854 DATATYPE_FILE
, std::move(aCallback
));
857 if (aFlavorStr
.EqualsLiteral(kHTMLMime
)) {
858 MOZ_CLIPBOARD_LOG(" Getting HTML clipboard data");
859 AsyncGetDataImpl(aTransferable
, aWhichClipboard
, aFlavorStr
.get(),
860 DATATYPE_HTML
, std::move(aCallback
));
863 MOZ_CLIPBOARD_LOG(" Getting raw %s MIME clipboard data\n", aFlavorStr
.get());
864 AsyncGetDataImpl(aTransferable
, aWhichClipboard
, aFlavorStr
.get(),
865 DATATYPE_RAW
, std::move(aCallback
));
868 void nsClipboard::AsyncGetNativeClipboardData(nsITransferable
* aTransferable
,
869 ClipboardType aWhichClipboard
,
870 GetDataCallback
&& aCallback
) {
871 MOZ_DIAGNOSTIC_ASSERT(aTransferable
);
872 MOZ_DIAGNOSTIC_ASSERT(
873 nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
));
875 MOZ_CLIPBOARD_LOG("nsClipboard::AsyncGetNativeClipboardData (%s)",
876 aWhichClipboard
== nsClipboard::kSelectionClipboard
879 nsTArray
<nsCString
> importedFlavors
;
880 nsresult rv
= GetTransferableFlavors(aTransferable
, importedFlavors
);
886 auto flavorsNum
= importedFlavors
.Length();
892 if (flavorsNum
> 1) {
894 " Only first MIME type (%s) will be imported from clipboard!",
895 importedFlavors
[0].get());
899 // Filter out MIME types on X11 to prevent unwanted conversions,
901 if (widget::GdkIsX11Display()) {
902 AsyncHasNativeClipboardDataMatchingFlavors(
903 importedFlavors
, aWhichClipboard
,
904 [aWhichClipboard
, transferable
= nsCOMPtr
{aTransferable
},
905 callback
= std::move(aCallback
)](auto aResultOrError
) mutable {
906 if (aResultOrError
.isErr()) {
907 callback(aResultOrError
.unwrapErr());
911 nsTArray
<nsCString
> clipboardFlavors
=
912 std::move(aResultOrError
.unwrap());
913 if (!clipboardFlavors
.Length()) {
914 MOZ_CLIPBOARD_LOG(" no flavors in clipboard, quit.");
919 AsyncGetDataFlavor(transferable
, aWhichClipboard
, clipboardFlavors
[0],
920 std::move(callback
));
925 // Read clipboard directly on Wayland
926 AsyncGetDataFlavor(aTransferable
, aWhichClipboard
, importedFlavors
[0],
927 std::move(aCallback
));
930 nsresult
nsClipboard::EmptyNativeClipboardData(ClipboardType aWhichClipboard
) {
931 MOZ_DIAGNOSTIC_ASSERT(
932 nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
));
935 "nsClipboard::EmptyNativeClipboardData (%s)\n",
936 aWhichClipboard
== kSelectionClipboard
? "primary" : "clipboard");
937 if (aWhichClipboard
== kSelectionClipboard
) {
938 if (mSelectionTransferable
) {
939 gtk_clipboard_clear(gtk_clipboard_get(GDK_SELECTION_PRIMARY
));
940 MOZ_ASSERT(!mSelectionTransferable
);
943 if (mGlobalTransferable
) {
944 gtk_clipboard_clear(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
));
945 MOZ_ASSERT(!mGlobalTransferable
);
948 ClearCachedTargets(aWhichClipboard
);
952 void nsClipboard::ClearTransferable(int32_t aWhichClipboard
) {
953 if (aWhichClipboard
== kSelectionClipboard
) {
954 mSelectionSequenceNumber
++;
955 mSelectionTransferable
= nullptr;
957 mGlobalSequenceNumber
++;
958 mGlobalTransferable
= nullptr;
962 static bool FlavorMatchesTarget(const nsACString
& aFlavor
, GdkAtom aTarget
) {
963 GUniquePtr
<gchar
> atom_name(gdk_atom_name(aTarget
));
967 if (aFlavor
.Equals(atom_name
.get())) {
968 MOZ_CLIPBOARD_LOG(" has %s\n", atom_name
.get());
971 // X clipboard supports image/jpeg, but we want to emulate support
972 // for image/jpg as well
973 if (aFlavor
.EqualsLiteral(kJPGImageMime
) &&
974 !strcmp(atom_name
.get(), kJPEGImageMime
)) {
975 MOZ_CLIPBOARD_LOG(" has image/jpg\n");
978 // application/x-moz-file should be treated like text/uri-list
979 if (aFlavor
.EqualsLiteral(kFileMime
) &&
980 !strcmp(atom_name
.get(), kURIListMime
)) {
982 " has text/uri-list treating as application/x-moz-file");
988 mozilla::Result
<bool, nsresult
>
989 nsClipboard::HasNativeClipboardDataMatchingFlavors(
990 const nsTArray
<nsCString
>& aFlavorList
, ClipboardType aWhichClipboard
) {
991 MOZ_DIAGNOSTIC_ASSERT(
992 nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
));
995 "nsClipboard::HasNativeClipboardDataMatchingFlavors (%s)\n",
996 aWhichClipboard
== kSelectionClipboard
? "primary" : "clipboard");
999 return Err(NS_ERROR_FAILURE
);
1002 auto targets
= mContext
->GetTargets(aWhichClipboard
);
1004 MOZ_CLIPBOARD_LOG(" no targes at clipboard (null)\n");
1009 if (MOZ_CLIPBOARD_LOG_ENABLED()) {
1010 MOZ_CLIPBOARD_LOG(" Asking for content:\n");
1011 for (auto& flavor
: aFlavorList
) {
1012 MOZ_CLIPBOARD_LOG(" MIME %s\n", flavor
.get());
1014 MOZ_CLIPBOARD_LOG(" Clipboard content (target nums %zu):\n",
1015 targets
.AsSpan().Length());
1016 for (const auto& target
: targets
.AsSpan()) {
1017 GUniquePtr
<gchar
> atom_name(gdk_atom_name(target
));
1019 MOZ_CLIPBOARD_LOG(" failed to get MIME\n");
1022 MOZ_CLIPBOARD_LOG(" MIME %s\n", atom_name
.get());
1027 // Walk through the provided types and try to match it to a
1029 for (auto& flavor
: aFlavorList
) {
1030 // We special case text/plain here.
1031 if (flavor
.EqualsLiteral(kTextMime
) &&
1032 gtk_targets_include_text(targets
.AsSpan().data(),
1033 targets
.AsSpan().Length())) {
1034 MOZ_CLIPBOARD_LOG(" has kTextMime\n");
1037 for (const auto& target
: targets
.AsSpan()) {
1038 if (FlavorMatchesTarget(flavor
, target
)) {
1044 MOZ_CLIPBOARD_LOG(" no targes at clipboard (bad match)\n");
1048 struct TragetCallbackHandler
{
1049 TragetCallbackHandler(const nsTArray
<nsCString
>& aAcceptedFlavorList
,
1050 nsBaseClipboard::HasMatchingFlavorsCallback
&& aCallback
)
1051 : mAcceptedFlavorList(aAcceptedFlavorList
.Clone()),
1052 mCallback(std::move(aCallback
)) {
1053 MOZ_CLIPBOARD_LOG("TragetCallbackHandler(%p) created", this);
1055 ~TragetCallbackHandler() {
1056 MOZ_CLIPBOARD_LOG("TragetCallbackHandler(%p) deleted", this);
1058 nsTArray
<nsCString
> mAcceptedFlavorList
;
1059 nsBaseClipboard::HasMatchingFlavorsCallback mCallback
;
1062 void nsClipboard::AsyncHasNativeClipboardDataMatchingFlavors(
1063 const nsTArray
<nsCString
>& aFlavorList
, ClipboardType aWhichClipboard
,
1064 nsBaseClipboard::HasMatchingFlavorsCallback
&& aCallback
) {
1065 MOZ_DIAGNOSTIC_ASSERT(
1066 nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
));
1069 "nsClipboard::AsyncHasNativeClipboardDataMatchingFlavors (%s)",
1070 aWhichClipboard
== kSelectionClipboard
? "primary" : "clipboard");
1072 gtk_clipboard_request_contents(
1073 gtk_clipboard_get(GetSelectionAtom(aWhichClipboard
)),
1074 gdk_atom_intern("TARGETS", FALSE
),
1075 [](GtkClipboard
* aClipboard
, GtkSelectionData
* aSelection
,
1076 gpointer aData
) -> void {
1077 MOZ_CLIPBOARD_LOG("gtk_clipboard_request_contents async handler (%p)",
1079 UniquePtr
<TragetCallbackHandler
> handler(
1080 static_cast<TragetCallbackHandler
*>(aData
));
1082 GdkAtom
* targets
= nullptr;
1083 gint targetsNum
= 0;
1084 if (gtk_selection_data_get_length(aSelection
) > 0) {
1085 gtk_selection_data_get_targets(aSelection
, &targets
, &targetsNum
);
1088 if (MOZ_CLIPBOARD_LOG_ENABLED()) {
1089 MOZ_CLIPBOARD_LOG(" Clipboard content (target nums %d):\n",
1091 for (int i
= 0; i
< targetsNum
; i
++) {
1092 GUniquePtr
<gchar
> atom_name(gdk_atom_name(targets
[i
]));
1094 MOZ_CLIPBOARD_LOG(" failed to get MIME\n");
1097 MOZ_CLIPBOARD_LOG(" MIME %s\n", atom_name
.get());
1102 nsTArray
<nsCString
> results
;
1104 for (auto& flavor
: handler
->mAcceptedFlavorList
) {
1105 MOZ_CLIPBOARD_LOG(" looking for %s", flavor
.get());
1106 if (flavor
.EqualsLiteral(kTextMime
) &&
1107 gtk_targets_include_text(targets
, targetsNum
)) {
1108 results
.AppendElement(flavor
);
1109 MOZ_CLIPBOARD_LOG(" has kTextMime\n");
1112 for (int i
= 0; i
< targetsNum
; i
++) {
1113 if (FlavorMatchesTarget(flavor
, targets
[i
])) {
1114 results
.AppendElement(flavor
);
1119 handler
->mCallback(std::move(results
));
1121 new TragetCallbackHandler(aFlavorList
, std::move(aCallback
)));
1124 nsITransferable
* nsClipboard::GetTransferable(int32_t aWhichClipboard
) {
1125 nsITransferable
* retval
;
1127 if (aWhichClipboard
== kSelectionClipboard
)
1128 retval
= mSelectionTransferable
.get();
1130 retval
= mGlobalTransferable
.get();
1135 void nsClipboard::SelectionGetEvent(GtkClipboard
* aClipboard
,
1136 GtkSelectionData
* aSelectionData
) {
1137 // Someone has asked us to hand them something. The first thing
1138 // that we want to do is see if that something includes text. If
1139 // it does, try to give it text/plain after converting it to
1142 int32_t whichClipboard
;
1145 GdkAtom selection
= gtk_selection_data_get_selection(aSelectionData
);
1146 if (selection
== GDK_SELECTION_PRIMARY
)
1147 whichClipboard
= kSelectionClipboard
;
1148 else if (selection
== GDK_SELECTION_CLIPBOARD
)
1149 whichClipboard
= kGlobalClipboard
;
1151 return; // THAT AIN'T NO CLIPBOARD I EVER HEARD OF
1154 "nsClipboard::SelectionGetEvent (%s)\n",
1155 whichClipboard
== kSelectionClipboard
? "primary" : "clipboard");
1157 nsCOMPtr
<nsITransferable
> trans
= GetTransferable(whichClipboard
);
1159 // We have nothing to serve
1161 "nsClipboard::SelectionGetEvent() - %s clipboard is empty!\n",
1162 whichClipboard
== kSelectionClipboard
? "Primary" : "Clipboard");
1167 nsCOMPtr
<nsISupports
> item
;
1169 GdkAtom selectionTarget
= gtk_selection_data_get_target(aSelectionData
);
1170 MOZ_CLIPBOARD_LOG(" selection target %s\n",
1171 GUniquePtr
<gchar
>(gdk_atom_name(selectionTarget
)).get());
1173 // Check to see if the selection data is some text type.
1174 if (gtk_targets_include_text(&selectionTarget
, 1)) {
1175 MOZ_CLIPBOARD_LOG(" providing text/plain data\n");
1176 // Try to convert our internal type into a text string. Get
1177 // the transferable for this clipboard and try to get the
1178 // text/plain type for it.
1179 rv
= trans
->GetTransferData("text/plain", getter_AddRefs(item
));
1180 if (NS_FAILED(rv
) || !item
) {
1181 MOZ_CLIPBOARD_LOG(" GetTransferData() failed to get text/plain!\n");
1185 nsCOMPtr
<nsISupportsString
> wideString
;
1186 wideString
= do_QueryInterface(item
);
1187 if (!wideString
) return;
1189 nsAutoString ucs2string
;
1190 wideString
->GetData(ucs2string
);
1191 NS_ConvertUTF16toUTF8
utf8string(ucs2string
);
1193 MOZ_CLIPBOARD_LOG(" sent %zd bytes of utf-8 data\n", utf8string
.Length());
1194 if (selectionTarget
== gdk_atom_intern("text/plain;charset=utf-8", FALSE
)) {
1196 " using gtk_selection_data_set for 'text/plain;charset=utf-8'\n");
1197 // Bypass gtk_selection_data_set_text, which will convert \n to \r\n
1198 // in some versions of GTK.
1199 gtk_selection_data_set(aSelectionData
, selectionTarget
, 8,
1200 reinterpret_cast<const guchar
*>(utf8string
.get()),
1201 utf8string
.Length());
1203 gtk_selection_data_set_text(aSelectionData
, utf8string
.get(),
1204 utf8string
.Length());
1209 // Check to see if the selection data is an image type
1210 if (gtk_targets_include_image(&selectionTarget
, 1, TRUE
)) {
1211 MOZ_CLIPBOARD_LOG(" providing image data\n");
1212 // Look through our transfer data for the image
1213 static const char* const imageMimeTypes
[] = {kNativeImageMime
,
1214 kPNGImageMime
, kJPEGImageMime
,
1215 kJPGImageMime
, kGIFImageMime
};
1216 nsCOMPtr
<nsISupports
> imageItem
;
1217 nsCOMPtr
<imgIContainer
> image
;
1218 for (uint32_t i
= 0; i
< std::size(imageMimeTypes
); i
++) {
1219 rv
= trans
->GetTransferData(imageMimeTypes
[i
], getter_AddRefs(imageItem
));
1220 if (NS_FAILED(rv
)) {
1221 MOZ_CLIPBOARD_LOG(" %s is missing at GetTransferData()\n",
1226 image
= do_QueryInterface(imageItem
);
1228 MOZ_CLIPBOARD_LOG(" %s is available at GetTransferData()\n",
1234 if (!image
) { // Not getting an image for an image mime type!?
1236 " Failed to get any image mime from GetTransferData()!\n");
1240 RefPtr
<GdkPixbuf
> pixbuf
= nsImageToPixbuf::ImageToPixbuf(image
);
1242 MOZ_CLIPBOARD_LOG(" nsImageToPixbuf::ImageToPixbuf() failed!\n");
1246 MOZ_CLIPBOARD_LOG(" Setting pixbuf image data as %s\n",
1247 GUniquePtr
<gchar
>(gdk_atom_name(selectionTarget
)).get());
1248 gtk_selection_data_set_pixbuf(aSelectionData
, pixbuf
);
1252 if (selectionTarget
== gdk_atom_intern(kHTMLMime
, FALSE
)) {
1253 MOZ_CLIPBOARD_LOG(" providing %s data\n", kHTMLMime
);
1254 rv
= trans
->GetTransferData(kHTMLMime
, getter_AddRefs(item
));
1255 if (NS_FAILED(rv
) || !item
) {
1256 MOZ_CLIPBOARD_LOG(" failed to get %s data by GetTransferData()!\n",
1261 nsCOMPtr
<nsISupportsString
> wideString
;
1262 wideString
= do_QueryInterface(item
);
1264 MOZ_CLIPBOARD_LOG(" failed to get wideString interface!");
1268 nsAutoString ucs2string
;
1269 wideString
->GetData(ucs2string
);
1272 // Add the prefix so the encoding is correctly detected.
1273 html
.AppendLiteral(kHTMLMarkupPrefix
);
1274 AppendUTF16toUTF8(ucs2string
, html
);
1276 MOZ_CLIPBOARD_LOG(" Setting %zd bytes of %s data\n", html
.Length(),
1277 GUniquePtr
<gchar
>(gdk_atom_name(selectionTarget
)).get());
1278 gtk_selection_data_set(aSelectionData
, selectionTarget
, 8,
1279 (const guchar
*)html
.get(), html
.Length());
1283 // We put kFileMime onto the clipboard as kURIListMime.
1284 if (selectionTarget
== gdk_atom_intern(kURIListMime
, FALSE
)) {
1285 MOZ_CLIPBOARD_LOG(" providing %s data\n", kURIListMime
);
1286 rv
= trans
->GetTransferData(kFileMime
, getter_AddRefs(item
));
1287 if (NS_FAILED(rv
) || !item
) {
1288 MOZ_CLIPBOARD_LOG(" failed to get %s data by GetTransferData()!\n",
1293 nsCOMPtr
<nsIFile
> file
= do_QueryInterface(item
);
1295 MOZ_CLIPBOARD_LOG(" failed to get nsIFile interface!");
1299 nsCOMPtr
<nsIURI
> fileURI
;
1300 rv
= NS_NewFileURI(getter_AddRefs(fileURI
), file
);
1301 if (NS_FAILED(rv
)) {
1302 MOZ_CLIPBOARD_LOG(" failed to get fileURI\n");
1307 if (NS_FAILED(fileURI
->GetSpec(uri
))) {
1308 MOZ_CLIPBOARD_LOG(" failed to get fileURI spec\n");
1312 MOZ_CLIPBOARD_LOG(" Setting %zd bytes of data\n", uri
.Length());
1313 gtk_selection_data_set(aSelectionData
, selectionTarget
, 8,
1314 (const guchar
*)uri
.get(), uri
.Length());
1318 MOZ_CLIPBOARD_LOG(" Try if we have anything at GetTransferData() for %s\n",
1319 GUniquePtr
<gchar
>(gdk_atom_name(selectionTarget
)).get());
1321 // Try to match up the selection data target to something our
1322 // transferable provides.
1323 GUniquePtr
<gchar
> target_name(gdk_atom_name(selectionTarget
));
1325 MOZ_CLIPBOARD_LOG(" Failed to get target name!\n");
1329 rv
= trans
->GetTransferData(target_name
.get(), getter_AddRefs(item
));
1331 if (NS_FAILED(rv
) || !item
) {
1332 MOZ_CLIPBOARD_LOG(" Failed to get anything from GetTransferData()!\n");
1336 void* primitive_data
= nullptr;
1337 uint32_t dataLen
= 0;
1338 nsPrimitiveHelpers::CreateDataFromPrimitive(
1339 nsDependentCString(target_name
.get()), item
, &primitive_data
, &dataLen
);
1340 if (!primitive_data
) {
1341 MOZ_CLIPBOARD_LOG(" Failed to get primitive data!\n");
1345 MOZ_CLIPBOARD_LOG(" Setting %s as a primitive data type, %d bytes\n",
1346 target_name
.get(), dataLen
);
1347 gtk_selection_data_set(aSelectionData
, selectionTarget
,
1348 8, /* 8 bits in a unit */
1349 (const guchar
*)primitive_data
, dataLen
);
1350 free(primitive_data
);
1353 void nsClipboard::ClearCachedTargets(int32_t aWhichClipboard
) {
1354 if (aWhichClipboard
== kSelectionClipboard
) {
1355 nsRetrievalContext::ClearCachedTargetsPrimary(nullptr, nullptr, nullptr);
1357 nsRetrievalContext::ClearCachedTargetsClipboard(nullptr, nullptr, nullptr);
1361 void nsClipboard::SelectionClearEvent(GtkClipboard
* aGtkClipboard
) {
1362 Maybe
<ClipboardType
> whichClipboard
= GetGeckoClipboardType(aGtkClipboard
);
1363 if (whichClipboard
.isNothing()) {
1367 "nsClipboard::SelectionClearEvent (%s)\n",
1368 *whichClipboard
== kSelectionClipboard
? "primary" : "clipboard");
1369 ClearCachedTargets(*whichClipboard
);
1370 ClearTransferable(*whichClipboard
);
1371 ClearClipboardCache(*whichClipboard
);
1374 void nsClipboard::OwnerChangedEvent(GtkClipboard
* aGtkClipboard
,
1375 GdkEventOwnerChange
* aEvent
) {
1376 Maybe
<ClipboardType
> whichClipboard
= GetGeckoClipboardType(aGtkClipboard
);
1377 if (whichClipboard
.isNothing()) {
1381 "nsClipboard::OwnerChangedEvent (%s)\n",
1382 *whichClipboard
== kSelectionClipboard
? "primary" : "clipboard");
1383 GtkWidget
* gtkWidget
= [aEvent
]() -> GtkWidget
* {
1384 if (!aEvent
->owner
) {
1387 gpointer user_data
= nullptr;
1388 gdk_window_get_user_data(aEvent
->owner
, &user_data
);
1389 return GTK_WIDGET(user_data
);
1391 // If we can get GtkWidget from the current clipboard owner, this
1392 // owner-changed event must be triggered by ourself via calling
1393 // gtk_clipboard_set_with_data, the sequence number should already be handled.
1395 if (*whichClipboard
== kSelectionClipboard
) {
1396 mSelectionSequenceNumber
++;
1398 mGlobalSequenceNumber
++;
1402 ClearCachedTargets(*whichClipboard
);
1405 void clipboard_get_cb(GtkClipboard
* aGtkClipboard
,
1406 GtkSelectionData
* aSelectionData
, guint info
,
1407 gpointer user_data
) {
1408 MOZ_CLIPBOARD_LOG("clipboard_get_cb() callback\n");
1409 nsClipboard
* clipboard
= static_cast<nsClipboard
*>(user_data
);
1410 clipboard
->SelectionGetEvent(aGtkClipboard
, aSelectionData
);
1413 void clipboard_clear_cb(GtkClipboard
* aGtkClipboard
, gpointer user_data
) {
1414 MOZ_CLIPBOARD_LOG("clipboard_clear_cb() callback\n");
1415 nsClipboard
* clipboard
= static_cast<nsClipboard
*>(user_data
);
1416 clipboard
->SelectionClearEvent(aGtkClipboard
);
1419 void clipboard_owner_change_cb(GtkClipboard
* aGtkClipboard
,
1420 GdkEventOwnerChange
* aEvent
,
1421 gpointer aUserData
) {
1422 MOZ_CLIPBOARD_LOG("clipboard_owner_change_cb() callback\n");
1423 nsClipboard
* clipboard
= static_cast<nsClipboard
*>(aUserData
);
1424 clipboard
->OwnerChangedEvent(aGtkClipboard
, aEvent
);
1428 * This function extracts the encoding label from the subset of HTML internal
1429 * encoding declaration syntax that uses the old long form with double quotes
1430 * and without spaces around the equals sign between the "content" attribute
1431 * name and the attribute value.
1433 * This was added for the sake of an ancient version of StarOffice
1434 * in the pre-UTF-8 era in bug 123389. It is unclear if supporting
1435 * non-UTF-8 encodings is still necessary and if this function
1436 * still needs to exist.
1438 * As of December 2022, both Gecko and LibreOffice emit an UTF-8
1439 * declaration that this function successfully extracts "UTF-8" from,
1440 * but that's also the default that we fall back on if this function
1441 * fails to extract a label.
1443 bool GetHTMLCharset(Span
<const char> aData
, nsCString
& aFoundCharset
) {
1444 // Assume ASCII first to find "charset" info
1445 const nsDependentCSubstring
htmlStr(aData
);
1446 nsACString::const_iterator start
, end
;
1447 htmlStr
.BeginReading(start
);
1448 htmlStr
.EndReading(end
);
1449 nsACString::const_iterator
valueStart(start
), valueEnd(start
);
1451 if (CaseInsensitiveFindInReadable("CONTENT=\"text/html;"_ns
, start
, end
)) {
1453 htmlStr
.EndReading(end
);
1455 if (CaseInsensitiveFindInReadable("charset="_ns
, start
, end
)) {
1458 htmlStr
.EndReading(end
);
1460 if (FindCharInReadable('"', start
, end
)) valueEnd
= start
;
1463 // find "charset" in HTML
1464 if (valueStart
!= valueEnd
) {
1465 aFoundCharset
= Substring(valueStart
, valueEnd
);
1466 ToUpperCase(aFoundCharset
);