mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 02:09:05 +02:00 
			
		
		
		
	HasNativeClipboardDataMatchingFlavors() also does the same thing which is useful for debugging. Differential Revision: https://phabricator.services.mozilla.com/D209799
		
			
				
	
	
		
			1468 lines
		
	
	
	
		
			51 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1468 lines
		
	
	
	
		
			51 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | 
						|
/* vim:expandtab:shiftwidth=2:tabstop=2:
 | 
						|
 */
 | 
						|
/* 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"
 | 
						|
#if defined(MOZ_X11)
 | 
						|
#  include "nsClipboardX11.h"
 | 
						|
#endif
 | 
						|
#if defined(MOZ_WAYLAND)
 | 
						|
#  include "nsClipboardWayland.h"
 | 
						|
#  include "nsWaylandDisplay.h"
 | 
						|
#endif
 | 
						|
#include "nsGtkUtils.h"
 | 
						|
#include "nsIURI.h"
 | 
						|
#include "nsIFile.h"
 | 
						|
#include "nsNetUtil.h"
 | 
						|
#include "nsContentUtils.h"
 | 
						|
#include "HeadlessClipboard.h"
 | 
						|
#include "nsSupportsPrimitives.h"
 | 
						|
#include "nsString.h"
 | 
						|
#include "nsReadableUtils.h"
 | 
						|
#include "nsPrimitiveHelpers.h"
 | 
						|
#include "nsImageToPixbuf.h"
 | 
						|
#include "nsStringStream.h"
 | 
						|
#include "nsIFileURL.h"
 | 
						|
#include "nsIObserverService.h"
 | 
						|
#include "mozilla/Services.h"
 | 
						|
#include "mozilla/RefPtr.h"
 | 
						|
#include "mozilla/GRefPtr.h"
 | 
						|
#include "mozilla/SchedulerGroup.h"
 | 
						|
#include "mozilla/StaticPrefs_widget.h"
 | 
						|
#include "mozilla/TimeStamp.h"
 | 
						|
#include "GRefPtr.h"
 | 
						|
#include "WidgetUtilsGtk.h"
 | 
						|
 | 
						|
#include "imgIContainer.h"
 | 
						|
 | 
						|
#include <gtk/gtk.h>
 | 
						|
#if defined(MOZ_X11)
 | 
						|
#  include <gtk/gtkx.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#include "mozilla/Encoding.h"
 | 
						|
 | 
						|
using namespace mozilla;
 | 
						|
 | 
						|
// Idle timeout for receiving selection and property notify events (microsec)
 | 
						|
// Right now it's set to 1 sec.
 | 
						|
const int kClipboardTimeout = 1000000;
 | 
						|
 | 
						|
// Defines how many event loop iterations will be done without sleep.
 | 
						|
// We ususally get data in first 2-3 iterations unless some large object
 | 
						|
// (an image for instance) is transferred through clipboard.
 | 
						|
const int kClipboardFastIterationNum = 3;
 | 
						|
 | 
						|
// We add this prefix to HTML markup, so that GetHTMLCharset can correctly
 | 
						|
// detect the HTML as UTF-8 encoded.
 | 
						|
static const char kHTMLMarkupPrefix[] =
 | 
						|
    R"(<meta http-equiv="content-type" content="text/html; charset=utf-8">)";
 | 
						|
 | 
						|
static const char kURIListMime[] = "text/uri-list";
 | 
						|
 | 
						|
ClipboardTargets nsRetrievalContext::sClipboardTargets;
 | 
						|
ClipboardTargets nsRetrievalContext::sPrimaryTargets;
 | 
						|
 | 
						|
// Callback when someone asks us for the data
 | 
						|
static void clipboard_get_cb(GtkClipboard* aGtkClipboard,
 | 
						|
                             GtkSelectionData* aSelectionData, guint info,
 | 
						|
                             gpointer user_data);
 | 
						|
 | 
						|
// Callback when someone asks us to clear a clipboard
 | 
						|
static void clipboard_clear_cb(GtkClipboard* aGtkClipboard, gpointer user_data);
 | 
						|
 | 
						|
// Callback when owner of clipboard data is changed
 | 
						|
static void clipboard_owner_change_cb(GtkClipboard* aGtkClipboard,
 | 
						|
                                      GdkEventOwnerChange* aEvent,
 | 
						|
                                      gpointer aUserData);
 | 
						|
 | 
						|
static bool GetHTMLCharset(Span<const char> aData, nsCString& str);
 | 
						|
 | 
						|
static void SetTransferableData(nsITransferable* aTransferable,
 | 
						|
                                const nsACString& aFlavor,
 | 
						|
                                const char* aClipboardData,
 | 
						|
                                uint32_t aClipboardDataLength) {
 | 
						|
  MOZ_CLIPBOARD_LOG("SetTransferableData MIME %s\n",
 | 
						|
                    PromiseFlatCString(aFlavor).get());
 | 
						|
  nsCOMPtr<nsISupports> wrapper;
 | 
						|
  nsPrimitiveHelpers::CreatePrimitiveForData(
 | 
						|
      aFlavor, aClipboardData, aClipboardDataLength, getter_AddRefs(wrapper));
 | 
						|
  aTransferable->SetTransferData(PromiseFlatCString(aFlavor).get(), wrapper);
 | 
						|
}
 | 
						|
 | 
						|
ClipboardTargets ClipboardTargets::Clone() {
 | 
						|
  ClipboardTargets ret;
 | 
						|
  ret.mCount = mCount;
 | 
						|
  if (mCount) {
 | 
						|
    ret.mTargets.reset(
 | 
						|
        reinterpret_cast<GdkAtom*>(g_malloc(sizeof(GdkAtom) * mCount)));
 | 
						|
    memcpy(ret.mTargets.get(), mTargets.get(), sizeof(GdkAtom) * mCount);
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
void ClipboardTargets::Set(ClipboardTargets aTargets) {
 | 
						|
  mCount = aTargets.mCount;
 | 
						|
  mTargets = std::move(aTargets.mTargets);
 | 
						|
}
 | 
						|
 | 
						|
void ClipboardData::SetData(Span<const uint8_t> aData) {
 | 
						|
  mData = nullptr;
 | 
						|
  mLength = aData.Length();
 | 
						|
  if (mLength) {
 | 
						|
    mData.reset(reinterpret_cast<char*>(g_malloc(sizeof(char) * mLength)));
 | 
						|
    memcpy(mData.get(), aData.data(), sizeof(char) * mLength);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void ClipboardData::SetText(Span<const char> aData) {
 | 
						|
  mData = nullptr;
 | 
						|
  mLength = aData.Length();
 | 
						|
  if (mLength) {
 | 
						|
    mData.reset(
 | 
						|
        reinterpret_cast<char*>(g_malloc(sizeof(char) * (mLength + 1))));
 | 
						|
    memcpy(mData.get(), aData.data(), sizeof(char) * mLength);
 | 
						|
    mData.get()[mLength] = '\0';
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void ClipboardData::SetTargets(ClipboardTargets aTargets) {
 | 
						|
  mLength = aTargets.mCount;
 | 
						|
  mData.reset(reinterpret_cast<char*>(aTargets.mTargets.release()));
 | 
						|
}
 | 
						|
 | 
						|
ClipboardTargets ClipboardData::ExtractTargets() {
 | 
						|
  GUniquePtr<GdkAtom> targets(reinterpret_cast<GdkAtom*>(mData.release()));
 | 
						|
  uint32_t length = std::exchange(mLength, 0);
 | 
						|
  return ClipboardTargets{std::move(targets), length};
 | 
						|
}
 | 
						|
 | 
						|
GdkAtom GetSelectionAtom(int32_t aWhichClipboard) {
 | 
						|
  if (aWhichClipboard == nsIClipboard::kGlobalClipboard)
 | 
						|
    return GDK_SELECTION_CLIPBOARD;
 | 
						|
 | 
						|
  return GDK_SELECTION_PRIMARY;
 | 
						|
}
 | 
						|
 | 
						|
int GetGeckoClipboardType(GtkClipboard* aGtkClipboard) {
 | 
						|
  if (aGtkClipboard == gtk_clipboard_get(GDK_SELECTION_PRIMARY))
 | 
						|
    return nsClipboard::kSelectionClipboard;
 | 
						|
  else if (aGtkClipboard == gtk_clipboard_get(GDK_SELECTION_CLIPBOARD))
 | 
						|
    return nsClipboard::kGlobalClipboard;
 | 
						|
 | 
						|
  return -1;  // THAT AIN'T NO CLIPBOARD I EVER HEARD OF
 | 
						|
}
 | 
						|
 | 
						|
void nsRetrievalContext::ClearCachedTargetsClipboard(GtkClipboard* aClipboard,
 | 
						|
                                                     GdkEvent* aEvent,
 | 
						|
                                                     gpointer data) {
 | 
						|
  MOZ_CLIPBOARD_LOG("nsRetrievalContext::ClearCachedTargetsClipboard()");
 | 
						|
  sClipboardTargets.Clear();
 | 
						|
}
 | 
						|
 | 
						|
void nsRetrievalContext::ClearCachedTargetsPrimary(GtkClipboard* aClipboard,
 | 
						|
                                                   GdkEvent* aEvent,
 | 
						|
                                                   gpointer data) {
 | 
						|
  MOZ_CLIPBOARD_LOG("nsRetrievalContext::ClearCachedTargetsPrimary()");
 | 
						|
  sPrimaryTargets.Clear();
 | 
						|
}
 | 
						|
 | 
						|
ClipboardTargets nsRetrievalContext::GetTargets(int32_t aWhichClipboard) {
 | 
						|
  MOZ_CLIPBOARD_LOG("nsRetrievalContext::GetTargets(%s)\n",
 | 
						|
                    aWhichClipboard == nsClipboard::kSelectionClipboard
 | 
						|
                        ? "primary"
 | 
						|
                        : "clipboard");
 | 
						|
  ClipboardTargets& storedTargets =
 | 
						|
      (aWhichClipboard == nsClipboard::kSelectionClipboard) ? sPrimaryTargets
 | 
						|
                                                            : sClipboardTargets;
 | 
						|
  if (!storedTargets) {
 | 
						|
    MOZ_CLIPBOARD_LOG("  getting targets from system");
 | 
						|
    storedTargets.Set(GetTargetsImpl(aWhichClipboard));
 | 
						|
  } else {
 | 
						|
    MOZ_CLIPBOARD_LOG("  using cached targets");
 | 
						|
  }
 | 
						|
  return storedTargets.Clone();
 | 
						|
}
 | 
						|
 | 
						|
nsRetrievalContext::~nsRetrievalContext() {
 | 
						|
  sClipboardTargets.Clear();
 | 
						|
  sPrimaryTargets.Clear();
 | 
						|
}
 | 
						|
 | 
						|
nsClipboard::nsClipboard()
 | 
						|
    : nsBaseClipboard(mozilla::dom::ClipboardCapabilities(
 | 
						|
#ifdef MOZ_WAYLAND
 | 
						|
          widget::GdkIsWaylandDisplay()
 | 
						|
              ? widget::WaylandDisplayGet()->IsPrimarySelectionEnabled()
 | 
						|
              : true,
 | 
						|
#else
 | 
						|
          true, /* supportsSelectionClipboard */
 | 
						|
#endif
 | 
						|
          false /* supportsFindClipboard */,
 | 
						|
          false /* supportsSelectionCache */)) {
 | 
						|
  g_signal_connect(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), "owner-change",
 | 
						|
                   G_CALLBACK(clipboard_owner_change_cb), this);
 | 
						|
  g_signal_connect(gtk_clipboard_get(GDK_SELECTION_PRIMARY), "owner-change",
 | 
						|
                   G_CALLBACK(clipboard_owner_change_cb), this);
 | 
						|
}
 | 
						|
 | 
						|
nsClipboard::~nsClipboard() {
 | 
						|
  g_signal_handlers_disconnect_by_func(
 | 
						|
      gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
 | 
						|
      FuncToGpointer(clipboard_owner_change_cb), this);
 | 
						|
  g_signal_handlers_disconnect_by_func(
 | 
						|
      gtk_clipboard_get(GDK_SELECTION_PRIMARY),
 | 
						|
      FuncToGpointer(clipboard_owner_change_cb), this);
 | 
						|
}
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS_INHERITED(nsClipboard, nsBaseClipboard, nsIObserver)
 | 
						|
 | 
						|
nsresult nsClipboard::Init(void) {
 | 
						|
#if defined(MOZ_X11)
 | 
						|
  if (widget::GdkIsX11Display()) {
 | 
						|
    mContext = new nsRetrievalContextX11();
 | 
						|
  }
 | 
						|
#endif
 | 
						|
#if defined(MOZ_WAYLAND)
 | 
						|
  if (widget::GdkIsWaylandDisplay()) {
 | 
						|
    mContext = new nsRetrievalContextWayland();
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
 | 
						|
  if (os) {
 | 
						|
    os->AddObserver(this, "xpcom-shutdown", false);
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsClipboard::Observe(nsISupports* aSubject, const char* aTopic,
 | 
						|
                     const char16_t* aData) {
 | 
						|
  // Save global clipboard content to CLIPBOARD_MANAGER.
 | 
						|
  // gtk_clipboard_store() can run an event loop, so call from a dedicated
 | 
						|
  // runnable.
 | 
						|
  return SchedulerGroup::Dispatch(
 | 
						|
      NS_NewRunnableFunction("gtk_clipboard_store()", []() {
 | 
						|
        MOZ_CLIPBOARD_LOG("nsClipboard storing clipboard content\n");
 | 
						|
        gtk_clipboard_store(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
 | 
						|
      }));
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsClipboard::SetNativeClipboardData(nsITransferable* aTransferable,
 | 
						|
                                    int32_t aWhichClipboard) {
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(aTransferable);
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(
 | 
						|
      nsIClipboard::IsClipboardTypeSupported(aWhichClipboard));
 | 
						|
 | 
						|
  // See if we can short cut
 | 
						|
  if ((aWhichClipboard == kGlobalClipboard &&
 | 
						|
       aTransferable == mGlobalTransferable.get()) ||
 | 
						|
      (aWhichClipboard == kSelectionClipboard &&
 | 
						|
       aTransferable == mSelectionTransferable.get())) {
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  MOZ_CLIPBOARD_LOG(
 | 
						|
      "nsClipboard::SetNativeClipboardData (%s)\n",
 | 
						|
      aWhichClipboard == kSelectionClipboard ? "primary" : "clipboard");
 | 
						|
 | 
						|
  // List of suported targets
 | 
						|
  GtkTargetList* list = gtk_target_list_new(nullptr, 0);
 | 
						|
 | 
						|
  // Get the types of supported flavors
 | 
						|
  nsTArray<nsCString> flavors;
 | 
						|
  nsresult rv = aTransferable->FlavorsTransferableCanExport(flavors);
 | 
						|
  if (NS_FAILED(rv)) {
 | 
						|
    MOZ_CLIPBOARD_LOG("    FlavorsTransferableCanExport failed!\n");
 | 
						|
    // Fall through.  |gtkTargets| will be null below.
 | 
						|
  }
 | 
						|
 | 
						|
  // Add all the flavors to this widget's supported type.
 | 
						|
  bool imagesAdded = false;
 | 
						|
  for (uint32_t i = 0; i < flavors.Length(); i++) {
 | 
						|
    nsCString& flavorStr = flavors[i];
 | 
						|
    MOZ_CLIPBOARD_LOG("    processing target %s\n", flavorStr.get());
 | 
						|
 | 
						|
    // Special case text/plain since we can handle all of the string types.
 | 
						|
    if (flavorStr.EqualsLiteral(kTextMime)) {
 | 
						|
      MOZ_CLIPBOARD_LOG("    adding TEXT targets\n");
 | 
						|
      gtk_target_list_add_text_targets(list, 0);
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    if (nsContentUtils::IsFlavorImage(flavorStr)) {
 | 
						|
      // Don't bother adding image targets twice
 | 
						|
      if (!imagesAdded) {
 | 
						|
        // accept any writable image type
 | 
						|
        MOZ_CLIPBOARD_LOG("    adding IMAGE targets\n");
 | 
						|
        gtk_target_list_add_image_targets(list, 0, TRUE);
 | 
						|
        imagesAdded = true;
 | 
						|
      }
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    if (flavorStr.EqualsLiteral(kFileMime)) {
 | 
						|
      MOZ_CLIPBOARD_LOG("    adding text/uri-list target\n");
 | 
						|
      GdkAtom atom = gdk_atom_intern(kURIListMime, FALSE);
 | 
						|
      gtk_target_list_add(list, atom, 0, 0);
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // Add this to our list of valid targets
 | 
						|
    MOZ_CLIPBOARD_LOG("    adding OTHER target %s\n", flavorStr.get());
 | 
						|
    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 = 0;
 | 
						|
  GtkTargetEntry* gtkTargets =
 | 
						|
      gtk_target_table_new_from_list(list, &numTargets);
 | 
						|
  if (!gtkTargets || numTargets == 0) {
 | 
						|
    MOZ_CLIPBOARD_LOG(
 | 
						|
        "    gtk_target_table_new_from_list() failed or empty list of "
 | 
						|
        "targets!\n");
 | 
						|
    // Clear references to the any old data and let GTK know that it is no
 | 
						|
    // longer available.
 | 
						|
    EmptyNativeClipboardData(aWhichClipboard);
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
  ClearCachedTargets(aWhichClipboard);
 | 
						|
 | 
						|
  // Set getcallback and request to store data after an application exit
 | 
						|
  if (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) {
 | 
						|
      mSelectionSequenceNumber++;
 | 
						|
      mSelectionTransferable = aTransferable;
 | 
						|
    } else {
 | 
						|
      mGlobalSequenceNumber++;
 | 
						|
      mGlobalTransferable = aTransferable;
 | 
						|
      gtk_clipboard_set_can_store(gtkClipboard, gtkTargets, numTargets);
 | 
						|
    }
 | 
						|
 | 
						|
    rv = NS_OK;
 | 
						|
  } else {
 | 
						|
    MOZ_CLIPBOARD_LOG("    gtk_clipboard_set_with_data() failed!\n");
 | 
						|
    EmptyNativeClipboardData(aWhichClipboard);
 | 
						|
    rv = NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
  gtk_target_table_free(gtkTargets, numTargets);
 | 
						|
  gtk_target_list_unref(list);
 | 
						|
 | 
						|
  return rv;
 | 
						|
}
 | 
						|
 | 
						|
mozilla::Result<int32_t, nsresult>
 | 
						|
nsClipboard::GetNativeClipboardSequenceNumber(int32_t aWhichClipboard) {
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(
 | 
						|
      nsIClipboard::IsClipboardTypeSupported(aWhichClipboard));
 | 
						|
  return aWhichClipboard == kSelectionClipboard ? mSelectionSequenceNumber
 | 
						|
                                                : mGlobalSequenceNumber;
 | 
						|
}
 | 
						|
 | 
						|
static bool IsMIMEAtFlavourList(const nsTArray<nsCString>& aFlavourList,
 | 
						|
                                const char* aMime) {
 | 
						|
  for (const auto& flavorStr : aFlavourList) {
 | 
						|
    if (flavorStr.Equals(aMime)) {
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
// When clipboard contains only images, X11/Gtk tries to convert them
 | 
						|
// to text when we request text instead of just fail to provide the data.
 | 
						|
// So if clipboard contains images only remove text MIME offer.
 | 
						|
bool nsClipboard::FilterImportedFlavors(int32_t aWhichClipboard,
 | 
						|
                                        nsTArray<nsCString>& aFlavors) {
 | 
						|
  MOZ_CLIPBOARD_LOG("nsClipboard::FilterImportedFlavors");
 | 
						|
 | 
						|
  auto targets = mContext->GetTargets(aWhichClipboard);
 | 
						|
  if (!targets) {
 | 
						|
    MOZ_CLIPBOARD_LOG("    X11: no targes at clipboard (null), quit.\n");
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  for (const auto& atom : targets.AsSpan()) {
 | 
						|
    GUniquePtr<gchar> atom_name(gdk_atom_name(atom));
 | 
						|
    if (!atom_name) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    // Filter out system MIME types.
 | 
						|
    if (strcmp(atom_name.get(), "TARGETS") == 0 ||
 | 
						|
        strcmp(atom_name.get(), "TIMESTAMP") == 0 ||
 | 
						|
        strcmp(atom_name.get(), "SAVE_TARGETS") == 0 ||
 | 
						|
        strcmp(atom_name.get(), "MULTIPLE") == 0) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    // Filter out types which can't be converted to text.
 | 
						|
    if (strncmp(atom_name.get(), "image/", 6) == 0 ||
 | 
						|
        strncmp(atom_name.get(), "application/", 12) == 0 ||
 | 
						|
        strncmp(atom_name.get(), "audio/", 6) == 0 ||
 | 
						|
        strncmp(atom_name.get(), "video/", 6) == 0) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    // We have some other MIME type on clipboard which can be hopefully
 | 
						|
    // converted to text without any problem.
 | 
						|
    MOZ_CLIPBOARD_LOG(
 | 
						|
        "    X11: text types in clipboard, no need to filter them.\n");
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  // So make sure we offer only types we have at clipboard.
 | 
						|
  nsTArray<nsCString> clipboardFlavors;
 | 
						|
  for (const auto& atom : targets.AsSpan()) {
 | 
						|
    GUniquePtr<gchar> atom_name(gdk_atom_name(atom));
 | 
						|
    if (!atom_name) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    if (IsMIMEAtFlavourList(aFlavors, atom_name.get())) {
 | 
						|
      clipboardFlavors.AppendElement(nsCString(atom_name.get()));
 | 
						|
    }
 | 
						|
  }
 | 
						|
  aFlavors.SwapElements(clipboardFlavors);
 | 
						|
#ifdef MOZ_LOGGING
 | 
						|
  MOZ_CLIPBOARD_LOG("    X11: Flavors which match clipboard content:\n");
 | 
						|
  for (uint32_t i = 0; i < aFlavors.Length(); i++) {
 | 
						|
    MOZ_CLIPBOARD_LOG("    %s\n", aFlavors[i].get());
 | 
						|
  }
 | 
						|
#endif
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
static nsresult GetTransferableFlavors(nsITransferable* aTransferable,
 | 
						|
                                       nsTArray<nsCString>& aFlavors) {
 | 
						|
  if (!aTransferable) {
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
  // Get a list of flavors this transferable can import
 | 
						|
  nsresult rv = aTransferable->FlavorsTransferableCanImport(aFlavors);
 | 
						|
  if (NS_FAILED(rv)) {
 | 
						|
    MOZ_CLIPBOARD_LOG("  FlavorsTransferableCanImport falied!\n");
 | 
						|
    return rv;
 | 
						|
  }
 | 
						|
#ifdef MOZ_LOGGING
 | 
						|
  MOZ_CLIPBOARD_LOG("  Flavors which can be imported:");
 | 
						|
  for (const auto& flavor : aFlavors) {
 | 
						|
    MOZ_CLIPBOARD_LOG("    %s", flavor.get());
 | 
						|
  }
 | 
						|
#endif
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
static bool TransferableSetFile(nsITransferable* aTransferable,
 | 
						|
                                const nsACString& aURIList) {
 | 
						|
  nsresult rv;
 | 
						|
  nsTArray<nsCString> uris = mozilla::widget::ParseTextURIList(aURIList);
 | 
						|
  if (!uris.IsEmpty()) {
 | 
						|
    nsCOMPtr<nsIURI> fileURI;
 | 
						|
    NS_NewURI(getter_AddRefs(fileURI), uris[0]);
 | 
						|
    if (nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(fileURI, &rv)) {
 | 
						|
      nsCOMPtr<nsIFile> file;
 | 
						|
      rv = fileURL->GetFile(getter_AddRefs(file));
 | 
						|
      if (NS_SUCCEEDED(rv)) {
 | 
						|
        aTransferable->SetTransferData(kFileMime, file);
 | 
						|
        MOZ_CLIPBOARD_LOG("  successfully set file to clipboard\n");
 | 
						|
        return true;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
static bool TransferableSetHTML(nsITransferable* aTransferable,
 | 
						|
                                Span<const char> aData) {
 | 
						|
  nsLiteralCString mimeType(kHTMLMime);
 | 
						|
 | 
						|
  // Convert text/html into our text format
 | 
						|
  nsAutoCString charset;
 | 
						|
  if (!GetHTMLCharset(aData, charset)) {
 | 
						|
    // Fall back to utf-8 in case html/data is missing kHTMLMarkupPrefix.
 | 
						|
    MOZ_CLIPBOARD_LOG(
 | 
						|
        "Failed to get html/text encoding, fall back to utf-8.\n");
 | 
						|
    charset.AssignLiteral("utf-8");
 | 
						|
  }
 | 
						|
 | 
						|
  MOZ_CLIPBOARD_LOG("TransferableSetHTML: HTML detected charset %s",
 | 
						|
                    charset.get());
 | 
						|
  // app which use "text/html" to copy&paste
 | 
						|
  // get the decoder
 | 
						|
  auto encoding = Encoding::ForLabelNoReplacement(charset);
 | 
						|
  if (!encoding) {
 | 
						|
    MOZ_CLIPBOARD_LOG(
 | 
						|
        "TransferableSetHTML: get unicode decoder error (charset: %s)",
 | 
						|
        charset.get());
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  // According to spec html UTF-16BE/LE should be switched to UTF-8
 | 
						|
  // https://html.spec.whatwg.org/#determining-the-character-encoding:utf-16-encoding-2
 | 
						|
  if (encoding == UTF_16LE_ENCODING || encoding == UTF_16BE_ENCODING) {
 | 
						|
    encoding = UTF_8_ENCODING;
 | 
						|
  }
 | 
						|
 | 
						|
  // Remove kHTMLMarkupPrefix again, it won't necessarily cause any
 | 
						|
  // issues, but might confuse other users.
 | 
						|
  const size_t prefixLen = ArrayLength(kHTMLMarkupPrefix) - 1;
 | 
						|
  if (aData.Length() >= prefixLen && nsDependentCSubstring(aData.To(prefixLen))
 | 
						|
                                         .EqualsLiteral(kHTMLMarkupPrefix)) {
 | 
						|
    aData = aData.From(prefixLen);
 | 
						|
  }
 | 
						|
 | 
						|
  nsAutoString unicodeData;
 | 
						|
  auto [rv, enc] = encoding->Decode(AsBytes(aData), unicodeData);
 | 
						|
#if MOZ_LOGGING
 | 
						|
  if (enc != UTF_8_ENCODING && MOZ_CLIPBOARD_LOG_ENABLED()) {
 | 
						|
    nsCString decoderName;
 | 
						|
    enc->Name(decoderName);
 | 
						|
    MOZ_CLIPBOARD_LOG("TransferableSetHTML: expected UTF-8 decoder but got %s",
 | 
						|
                      decoderName.get());
 | 
						|
  }
 | 
						|
#endif
 | 
						|
  if (NS_FAILED(rv)) {
 | 
						|
    MOZ_CLIPBOARD_LOG("TransferableSetHTML: failed to decode HTML");
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  SetTransferableData(aTransferable, mimeType,
 | 
						|
                      (const char*)unicodeData.BeginReading(),
 | 
						|
                      unicodeData.Length() * sizeof(char16_t));
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsClipboard::GetNativeClipboardData(nsITransferable* aTransferable,
 | 
						|
                                    int32_t aWhichClipboard) {
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(aTransferable);
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(
 | 
						|
      nsIClipboard::IsClipboardTypeSupported(aWhichClipboard));
 | 
						|
 | 
						|
  MOZ_CLIPBOARD_LOG(
 | 
						|
      "nsClipboard::GetNativeClipboardData (%s)\n",
 | 
						|
      aWhichClipboard == kSelectionClipboard ? "primary" : "clipboard");
 | 
						|
 | 
						|
  // TODO: Ensure we don't re-enter here.
 | 
						|
  if (!mContext) {
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
  nsTArray<nsCString> flavors;
 | 
						|
  nsresult rv = GetTransferableFlavors(aTransferable, flavors);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
  // Filter out MIME types on X11 to prevent unwanted conversions,
 | 
						|
  // see Bug 1611407
 | 
						|
  if (widget::GdkIsX11Display() &&
 | 
						|
      !FilterImportedFlavors(aWhichClipboard, flavors)) {
 | 
						|
    MOZ_CLIPBOARD_LOG("    Missing suitable clipboard data, quit.");
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  for (uint32_t i = 0; i < flavors.Length(); i++) {
 | 
						|
    nsCString& flavorStr = flavors[i];
 | 
						|
 | 
						|
    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);
 | 
						|
      }
 | 
						|
 | 
						|
      MOZ_CLIPBOARD_LOG("    Getting image %s MIME clipboard data\n",
 | 
						|
                        flavorStr.get());
 | 
						|
 | 
						|
      auto clipboardData =
 | 
						|
          mContext->GetClipboardData(flavorStr.get(), aWhichClipboard);
 | 
						|
      if (!clipboardData) {
 | 
						|
        MOZ_CLIPBOARD_LOG("    %s type is missing\n", flavorStr.get());
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      nsCOMPtr<nsIInputStream> byteStream;
 | 
						|
      NS_NewByteInputStream(getter_AddRefs(byteStream), clipboardData.AsSpan(),
 | 
						|
                            NS_ASSIGNMENT_COPY);
 | 
						|
      aTransferable->SetTransferData(flavorStr.get(), byteStream);
 | 
						|
      MOZ_CLIPBOARD_LOG("    got %s MIME data\n", flavorStr.get());
 | 
						|
      return NS_OK;
 | 
						|
    }
 | 
						|
 | 
						|
    // Special case text/plain since we can convert any
 | 
						|
    // string into text/plain
 | 
						|
    if (flavorStr.EqualsLiteral(kTextMime)) {
 | 
						|
      MOZ_CLIPBOARD_LOG("    Getting text %s MIME clipboard data\n",
 | 
						|
                        flavorStr.get());
 | 
						|
 | 
						|
      auto clipboardData = mContext->GetClipboardText(aWhichClipboard);
 | 
						|
      if (!clipboardData) {
 | 
						|
        MOZ_CLIPBOARD_LOG("    failed to get text data\n");
 | 
						|
        // If the type was text/plain and we couldn't get
 | 
						|
        // text off the clipboard, run the next loop
 | 
						|
        // iteration.
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      // Convert utf-8 into our text format.
 | 
						|
      NS_ConvertUTF8toUTF16 ucs2string(clipboardData.get());
 | 
						|
      SetTransferableData(aTransferable, flavorStr,
 | 
						|
                          (const char*)ucs2string.BeginReading(),
 | 
						|
                          ucs2string.Length() * 2);
 | 
						|
 | 
						|
      MOZ_CLIPBOARD_LOG("    got text data, length %zd\n", ucs2string.Length());
 | 
						|
      return NS_OK;
 | 
						|
    }
 | 
						|
 | 
						|
    if (flavorStr.EqualsLiteral(kFileMime)) {
 | 
						|
      MOZ_CLIPBOARD_LOG("    Getting %s file clipboard data\n",
 | 
						|
                        flavorStr.get());
 | 
						|
 | 
						|
      auto clipboardData =
 | 
						|
          mContext->GetClipboardData(kURIListMime, aWhichClipboard);
 | 
						|
      if (!clipboardData) {
 | 
						|
        MOZ_CLIPBOARD_LOG("    text/uri-list type is missing\n");
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      nsDependentCSubstring fileName(clipboardData.AsSpan());
 | 
						|
      if (!TransferableSetFile(aTransferable, fileName)) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      return NS_OK;
 | 
						|
    }
 | 
						|
 | 
						|
    MOZ_CLIPBOARD_LOG("    Getting %s MIME clipboard data\n", flavorStr.get());
 | 
						|
 | 
						|
    auto clipboardData =
 | 
						|
        mContext->GetClipboardData(flavorStr.get(), aWhichClipboard);
 | 
						|
 | 
						|
#ifdef MOZ_LOGGING
 | 
						|
    if (!clipboardData) {
 | 
						|
      MOZ_CLIPBOARD_LOG("    %s type is missing\n", flavorStr.get());
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
    if (clipboardData) {
 | 
						|
      MOZ_CLIPBOARD_LOG("    got %s mime type data.\n", flavorStr.get());
 | 
						|
 | 
						|
      // Special case text/html since we can convert into UCS2
 | 
						|
      if (flavorStr.EqualsLiteral(kHTMLMime)) {
 | 
						|
        if (!TransferableSetHTML(aTransferable, clipboardData.AsSpan())) {
 | 
						|
          continue;
 | 
						|
        }
 | 
						|
      } else {
 | 
						|
        auto span = clipboardData.AsSpan();
 | 
						|
        SetTransferableData(aTransferable, flavorStr, span.data(),
 | 
						|
                            span.Length());
 | 
						|
      }
 | 
						|
      return NS_OK;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  MOZ_CLIPBOARD_LOG("    failed to get clipboard content.\n");
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
enum DataType {
 | 
						|
  DATATYPE_IMAGE,
 | 
						|
  DATATYPE_FILE,
 | 
						|
  DATATYPE_HTML,
 | 
						|
  DATATYPE_RAW,
 | 
						|
};
 | 
						|
 | 
						|
struct DataCallbackHandler {
 | 
						|
  RefPtr<nsITransferable> mTransferable;
 | 
						|
  nsBaseClipboard::GetDataCallback mDataCallback;
 | 
						|
  nsCString mMimeType;
 | 
						|
  DataType mDataType;
 | 
						|
 | 
						|
  explicit DataCallbackHandler(RefPtr<nsITransferable> aTransferable,
 | 
						|
                               nsBaseClipboard::GetDataCallback&& aDataCallback,
 | 
						|
                               const char* aMimeType,
 | 
						|
                               DataType aDataType = DATATYPE_RAW)
 | 
						|
      : mTransferable(std::move(aTransferable)),
 | 
						|
        mDataCallback(std::move(aDataCallback)),
 | 
						|
        mMimeType(aMimeType),
 | 
						|
        mDataType(aDataType) {
 | 
						|
    MOZ_COUNT_CTOR(DataCallbackHandler);
 | 
						|
    MOZ_CLIPBOARD_LOG("DataCallbackHandler created [%p] MIME %s type %d", this,
 | 
						|
                      mMimeType.get(), mDataType);
 | 
						|
  }
 | 
						|
  ~DataCallbackHandler() {
 | 
						|
    MOZ_CLIPBOARD_LOG("DataCallbackHandler deleted [%p]", this);
 | 
						|
    MOZ_COUNT_DTOR(DataCallbackHandler);
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
static void AsyncGetTextImpl(nsITransferable* aTransferable,
 | 
						|
                             int32_t aWhichClipboard,
 | 
						|
                             nsBaseClipboard::GetDataCallback&& aCallback) {
 | 
						|
  MOZ_CLIPBOARD_LOG("AsyncGetText() type '%s'",
 | 
						|
                    aWhichClipboard == nsClipboard::kSelectionClipboard
 | 
						|
                        ? "primary"
 | 
						|
                        : "clipboard");
 | 
						|
 | 
						|
  gtk_clipboard_request_text(
 | 
						|
      gtk_clipboard_get(GetSelectionAtom(aWhichClipboard)),
 | 
						|
      [](GtkClipboard* aClipboard, const gchar* aText, gpointer aData) -> void {
 | 
						|
        UniquePtr<DataCallbackHandler> ref(
 | 
						|
            static_cast<DataCallbackHandler*>(aData));
 | 
						|
        MOZ_CLIPBOARD_LOG("AsyncGetText async handler of [%p]", aData);
 | 
						|
 | 
						|
        size_t dataLength = aText ? strlen(aText) : 0;
 | 
						|
        if (dataLength <= 0) {
 | 
						|
          MOZ_CLIPBOARD_LOG("  quit, text is not available");
 | 
						|
          ref->mDataCallback(NS_OK);
 | 
						|
          return;
 | 
						|
        }
 | 
						|
 | 
						|
        // Convert utf-8 into our unicode format.
 | 
						|
        NS_ConvertUTF8toUTF16 utf16string(aText, dataLength);
 | 
						|
        nsLiteralCString flavor(kTextMime);
 | 
						|
        SetTransferableData(ref->mTransferable, flavor,
 | 
						|
                            (const char*)utf16string.BeginReading(),
 | 
						|
                            utf16string.Length() * 2);
 | 
						|
        MOZ_CLIPBOARD_LOG("  text is set, length = %d", (int)dataLength);
 | 
						|
        ref->mDataCallback(NS_OK);
 | 
						|
      },
 | 
						|
      new DataCallbackHandler(aTransferable, std::move(aCallback), kTextMime));
 | 
						|
}
 | 
						|
 | 
						|
static void AsyncGetDataImpl(nsITransferable* aTransferable,
 | 
						|
                             int32_t aWhichClipboard, const char* aMimeType,
 | 
						|
                             DataType aDataType,
 | 
						|
                             nsBaseClipboard::GetDataCallback&& aCallback) {
 | 
						|
  MOZ_CLIPBOARD_LOG("AsyncGetData() type '%s'",
 | 
						|
                    aWhichClipboard == nsClipboard::kSelectionClipboard
 | 
						|
                        ? "primary"
 | 
						|
                        : "clipboard");
 | 
						|
 | 
						|
  const char* gtkMIMEType = nullptr;
 | 
						|
  switch (aDataType) {
 | 
						|
    case DATATYPE_FILE:
 | 
						|
      // Don't ask Gtk for application/x-moz-file
 | 
						|
      gtkMIMEType = kURIListMime;
 | 
						|
      break;
 | 
						|
    case DATATYPE_IMAGE:
 | 
						|
    case DATATYPE_HTML:
 | 
						|
    case DATATYPE_RAW:
 | 
						|
      gtkMIMEType = aMimeType;
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  gtk_clipboard_request_contents(
 | 
						|
      gtk_clipboard_get(GetSelectionAtom(aWhichClipboard)),
 | 
						|
      gdk_atom_intern(gtkMIMEType, FALSE),
 | 
						|
      [](GtkClipboard* aClipboard, GtkSelectionData* aSelection,
 | 
						|
         gpointer aData) -> void {
 | 
						|
        UniquePtr<DataCallbackHandler> ref(
 | 
						|
            static_cast<DataCallbackHandler*>(aData));
 | 
						|
        MOZ_CLIPBOARD_LOG("AsyncGetData async handler [%p] MIME %s type %d",
 | 
						|
                          aData, ref->mMimeType.get(), ref->mDataType);
 | 
						|
 | 
						|
        int dataLength = gtk_selection_data_get_length(aSelection);
 | 
						|
        if (dataLength <= 0) {
 | 
						|
          ref->mDataCallback(NS_OK);
 | 
						|
          return;
 | 
						|
        }
 | 
						|
        const char* data = (const char*)gtk_selection_data_get_data(aSelection);
 | 
						|
        if (!data) {
 | 
						|
          ref->mDataCallback(NS_OK);
 | 
						|
          return;
 | 
						|
        }
 | 
						|
        switch (ref->mDataType) {
 | 
						|
          case DATATYPE_IMAGE: {
 | 
						|
            MOZ_CLIPBOARD_LOG("  set image clipboard data");
 | 
						|
            nsCOMPtr<nsIInputStream> byteStream;
 | 
						|
            NS_NewByteInputStream(getter_AddRefs(byteStream),
 | 
						|
                                  Span(data, dataLength), NS_ASSIGNMENT_COPY);
 | 
						|
            ref->mTransferable->SetTransferData(ref->mMimeType.get(),
 | 
						|
                                                byteStream);
 | 
						|
            break;
 | 
						|
          }
 | 
						|
          case DATATYPE_FILE: {
 | 
						|
            MOZ_CLIPBOARD_LOG("  set file clipboard data");
 | 
						|
            nsDependentCSubstring file(data, dataLength);
 | 
						|
            TransferableSetFile(ref->mTransferable, file);
 | 
						|
            break;
 | 
						|
          }
 | 
						|
          case DATATYPE_HTML: {
 | 
						|
            MOZ_CLIPBOARD_LOG("  html clipboard data");
 | 
						|
            Span dataSpan(data, dataLength);
 | 
						|
            TransferableSetHTML(ref->mTransferable, dataSpan);
 | 
						|
            break;
 | 
						|
          }
 | 
						|
          case DATATYPE_RAW: {
 | 
						|
            MOZ_CLIPBOARD_LOG("  raw clipboard data %s", ref->mMimeType.get());
 | 
						|
            SetTransferableData(ref->mTransferable, ref->mMimeType, data,
 | 
						|
                                dataLength);
 | 
						|
            break;
 | 
						|
          }
 | 
						|
        }
 | 
						|
        ref->mDataCallback(NS_OK);
 | 
						|
      },
 | 
						|
      new DataCallbackHandler(aTransferable, std::move(aCallback), aMimeType,
 | 
						|
                              aDataType));
 | 
						|
}
 | 
						|
 | 
						|
static void AsyncGetDataFlavor(nsITransferable* aTransferable,
 | 
						|
                               int32_t aWhichClipboard, nsCString& aFlavorStr,
 | 
						|
                               nsBaseClipboard::GetDataCallback&& aCallback) {
 | 
						|
  if (aFlavorStr.EqualsLiteral(kJPEGImageMime) ||
 | 
						|
      aFlavorStr.EqualsLiteral(kJPGImageMime) ||
 | 
						|
      aFlavorStr.EqualsLiteral(kPNGImageMime) ||
 | 
						|
      aFlavorStr.EqualsLiteral(kGIFImageMime)) {
 | 
						|
    // Emulate support for image/jpg
 | 
						|
    if (aFlavorStr.EqualsLiteral(kJPGImageMime)) {
 | 
						|
      aFlavorStr.Assign(kJPEGImageMime);
 | 
						|
    }
 | 
						|
    MOZ_CLIPBOARD_LOG("  Getting image %s MIME clipboard data",
 | 
						|
                      aFlavorStr.get());
 | 
						|
    AsyncGetDataImpl(aTransferable, aWhichClipboard, aFlavorStr.get(),
 | 
						|
                     DATATYPE_IMAGE, std::move(aCallback));
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  // Special case text/plain since we can convert any
 | 
						|
  // string into text/plain
 | 
						|
  if (aFlavorStr.EqualsLiteral(kTextMime)) {
 | 
						|
    MOZ_CLIPBOARD_LOG("  Getting unicode clipboard data");
 | 
						|
    AsyncGetTextImpl(aTransferable, aWhichClipboard, std::move(aCallback));
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (aFlavorStr.EqualsLiteral(kFileMime)) {
 | 
						|
    MOZ_CLIPBOARD_LOG("  Getting file clipboard data\n");
 | 
						|
    AsyncGetDataImpl(aTransferable, aWhichClipboard, aFlavorStr.get(),
 | 
						|
                     DATATYPE_FILE, std::move(aCallback));
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (aFlavorStr.EqualsLiteral(kHTMLMime)) {
 | 
						|
    MOZ_CLIPBOARD_LOG("  Getting HTML clipboard data");
 | 
						|
    AsyncGetDataImpl(aTransferable, aWhichClipboard, aFlavorStr.get(),
 | 
						|
                     DATATYPE_HTML, std::move(aCallback));
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  MOZ_CLIPBOARD_LOG("  Getting raw %s MIME clipboard data\n", aFlavorStr.get());
 | 
						|
  AsyncGetDataImpl(aTransferable, aWhichClipboard, aFlavorStr.get(),
 | 
						|
                   DATATYPE_RAW, std::move(aCallback));
 | 
						|
}
 | 
						|
 | 
						|
void nsClipboard::AsyncGetNativeClipboardData(nsITransferable* aTransferable,
 | 
						|
                                              int32_t aWhichClipboard,
 | 
						|
                                              GetDataCallback&& aCallback) {
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(aTransferable);
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(
 | 
						|
      nsIClipboard::IsClipboardTypeSupported(aWhichClipboard));
 | 
						|
 | 
						|
  MOZ_CLIPBOARD_LOG("nsClipboard::AsyncGetNativeClipboardData (%s)",
 | 
						|
                    aWhichClipboard == nsClipboard::kSelectionClipboard
 | 
						|
                        ? "primary"
 | 
						|
                        : "clipboard");
 | 
						|
  nsTArray<nsCString> importedFlavors;
 | 
						|
  nsresult rv = GetTransferableFlavors(aTransferable, importedFlavors);
 | 
						|
  if (NS_FAILED(rv)) {
 | 
						|
    aCallback(rv);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  auto flavorsNum = importedFlavors.Length();
 | 
						|
  if (!flavorsNum) {
 | 
						|
    aCallback(NS_OK);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
#ifdef MOZ_LOGGING
 | 
						|
  if (flavorsNum > 1) {
 | 
						|
    MOZ_CLIPBOARD_LOG(
 | 
						|
        "  Only first MIME type (%s) will be imported from clipboard!",
 | 
						|
        importedFlavors[0].get());
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  // Filter out MIME types on X11 to prevent unwanted conversions,
 | 
						|
  // see Bug 1611407
 | 
						|
  if (widget::GdkIsX11Display()) {
 | 
						|
    AsyncHasNativeClipboardDataMatchingFlavors(
 | 
						|
        importedFlavors, aWhichClipboard,
 | 
						|
        [aWhichClipboard, transferable = nsCOMPtr{aTransferable},
 | 
						|
         callback = std::move(aCallback)](auto aResultOrError) mutable {
 | 
						|
          if (aResultOrError.isErr()) {
 | 
						|
            callback(aResultOrError.unwrapErr());
 | 
						|
            return;
 | 
						|
          }
 | 
						|
 | 
						|
          nsTArray<nsCString> clipboardFlavors =
 | 
						|
              std::move(aResultOrError.unwrap());
 | 
						|
          if (!clipboardFlavors.Length()) {
 | 
						|
            MOZ_CLIPBOARD_LOG("  no flavors in clipboard, quit.");
 | 
						|
            callback(NS_OK);
 | 
						|
            return;
 | 
						|
          }
 | 
						|
 | 
						|
          AsyncGetDataFlavor(transferable, aWhichClipboard, clipboardFlavors[0],
 | 
						|
                             std::move(callback));
 | 
						|
        });
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // Read clipboard directly on Wayland
 | 
						|
  AsyncGetDataFlavor(aTransferable, aWhichClipboard, importedFlavors[0],
 | 
						|
                     std::move(aCallback));
 | 
						|
}
 | 
						|
 | 
						|
nsresult nsClipboard::EmptyNativeClipboardData(int32_t aWhichClipboard) {
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(
 | 
						|
      nsIClipboard::IsClipboardTypeSupported(aWhichClipboard));
 | 
						|
 | 
						|
  MOZ_CLIPBOARD_LOG(
 | 
						|
      "nsClipboard::EmptyNativeClipboardData (%s)\n",
 | 
						|
      aWhichClipboard == kSelectionClipboard ? "primary" : "clipboard");
 | 
						|
  if (aWhichClipboard == kSelectionClipboard) {
 | 
						|
    if (mSelectionTransferable) {
 | 
						|
      gtk_clipboard_clear(gtk_clipboard_get(GDK_SELECTION_PRIMARY));
 | 
						|
      MOZ_ASSERT(!mSelectionTransferable);
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    if (mGlobalTransferable) {
 | 
						|
      gtk_clipboard_clear(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
 | 
						|
      MOZ_ASSERT(!mGlobalTransferable);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  ClearCachedTargets(aWhichClipboard);
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
void nsClipboard::ClearTransferable(int32_t aWhichClipboard) {
 | 
						|
  if (aWhichClipboard == kSelectionClipboard) {
 | 
						|
    mSelectionSequenceNumber++;
 | 
						|
    mSelectionTransferable = nullptr;
 | 
						|
  } else {
 | 
						|
    mGlobalSequenceNumber++;
 | 
						|
    mGlobalTransferable = nullptr;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static bool FlavorMatchesTarget(const nsACString& aFlavor, GdkAtom aTarget) {
 | 
						|
  GUniquePtr<gchar> atom_name(gdk_atom_name(aTarget));
 | 
						|
  if (!atom_name) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  if (aFlavor.Equals(atom_name.get())) {
 | 
						|
    MOZ_CLIPBOARD_LOG("    has %s\n", atom_name.get());
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
  // X clipboard supports image/jpeg, but we want to emulate support
 | 
						|
  // for image/jpg as well
 | 
						|
  if (aFlavor.EqualsLiteral(kJPGImageMime) &&
 | 
						|
      !strcmp(atom_name.get(), kJPEGImageMime)) {
 | 
						|
    MOZ_CLIPBOARD_LOG("    has image/jpg\n");
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
  // application/x-moz-file should be treated like text/uri-list
 | 
						|
  if (aFlavor.EqualsLiteral(kFileMime) &&
 | 
						|
      !strcmp(atom_name.get(), kURIListMime)) {
 | 
						|
    MOZ_CLIPBOARD_LOG(
 | 
						|
        "    has text/uri-list treating as application/x-moz-file");
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
mozilla::Result<bool, nsresult>
 | 
						|
nsClipboard::HasNativeClipboardDataMatchingFlavors(
 | 
						|
    const nsTArray<nsCString>& aFlavorList, int32_t aWhichClipboard) {
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(
 | 
						|
      nsIClipboard::IsClipboardTypeSupported(aWhichClipboard));
 | 
						|
 | 
						|
  MOZ_CLIPBOARD_LOG(
 | 
						|
      "nsClipboard::HasNativeClipboardDataMatchingFlavors (%s)\n",
 | 
						|
      aWhichClipboard == kSelectionClipboard ? "primary" : "clipboard");
 | 
						|
 | 
						|
  if (!mContext) {
 | 
						|
    return Err(NS_ERROR_FAILURE);
 | 
						|
  }
 | 
						|
 | 
						|
  auto targets = mContext->GetTargets(aWhichClipboard);
 | 
						|
  if (!targets) {
 | 
						|
    MOZ_CLIPBOARD_LOG("    no targes at clipboard (null)\n");
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
#ifdef MOZ_LOGGING
 | 
						|
  if (MOZ_CLIPBOARD_LOG_ENABLED()) {
 | 
						|
    MOZ_CLIPBOARD_LOG("    Asking for content:\n");
 | 
						|
    for (auto& flavor : aFlavorList) {
 | 
						|
      MOZ_CLIPBOARD_LOG("        MIME %s\n", flavor.get());
 | 
						|
    }
 | 
						|
    MOZ_CLIPBOARD_LOG("    Clipboard content (target nums %zu):\n",
 | 
						|
                      targets.AsSpan().Length());
 | 
						|
    for (const auto& target : targets.AsSpan()) {
 | 
						|
      GUniquePtr<gchar> atom_name(gdk_atom_name(target));
 | 
						|
      if (!atom_name) {
 | 
						|
        MOZ_CLIPBOARD_LOG("        failed to get MIME\n");
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      MOZ_CLIPBOARD_LOG("        MIME %s\n", atom_name.get());
 | 
						|
    }
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  // Walk through the provided types and try to match it to a
 | 
						|
  // provided type.
 | 
						|
  for (auto& flavor : aFlavorList) {
 | 
						|
    // We special case text/plain here.
 | 
						|
    if (flavor.EqualsLiteral(kTextMime) &&
 | 
						|
        gtk_targets_include_text(targets.AsSpan().data(),
 | 
						|
                                 targets.AsSpan().Length())) {
 | 
						|
      MOZ_CLIPBOARD_LOG("    has kTextMime\n");
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    for (const auto& target : targets.AsSpan()) {
 | 
						|
      if (FlavorMatchesTarget(flavor, target)) {
 | 
						|
        return true;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  MOZ_CLIPBOARD_LOG("    no targes at clipboard (bad match)\n");
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
struct TragetCallbackHandler {
 | 
						|
  TragetCallbackHandler(const nsTArray<nsCString>& aAcceptedFlavorList,
 | 
						|
                        nsBaseClipboard::HasMatchingFlavorsCallback&& aCallback)
 | 
						|
      : mAcceptedFlavorList(aAcceptedFlavorList.Clone()),
 | 
						|
        mCallback(std::move(aCallback)) {
 | 
						|
    MOZ_CLIPBOARD_LOG("TragetCallbackHandler(%p) created", this);
 | 
						|
  }
 | 
						|
  ~TragetCallbackHandler() {
 | 
						|
    MOZ_CLIPBOARD_LOG("TragetCallbackHandler(%p) deleted", this);
 | 
						|
  }
 | 
						|
  nsTArray<nsCString> mAcceptedFlavorList;
 | 
						|
  nsBaseClipboard::HasMatchingFlavorsCallback mCallback;
 | 
						|
};
 | 
						|
 | 
						|
void nsClipboard::AsyncHasNativeClipboardDataMatchingFlavors(
 | 
						|
    const nsTArray<nsCString>& aFlavorList, int32_t aWhichClipboard,
 | 
						|
    nsBaseClipboard::HasMatchingFlavorsCallback&& aCallback) {
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(
 | 
						|
      nsIClipboard::IsClipboardTypeSupported(aWhichClipboard));
 | 
						|
 | 
						|
  MOZ_CLIPBOARD_LOG(
 | 
						|
      "nsClipboard::AsyncHasNativeClipboardDataMatchingFlavors (%s)",
 | 
						|
      aWhichClipboard == kSelectionClipboard ? "primary" : "clipboard");
 | 
						|
 | 
						|
  gtk_clipboard_request_contents(
 | 
						|
      gtk_clipboard_get(GetSelectionAtom(aWhichClipboard)),
 | 
						|
      gdk_atom_intern("TARGETS", FALSE),
 | 
						|
      [](GtkClipboard* aClipboard, GtkSelectionData* aSelection,
 | 
						|
         gpointer aData) -> void {
 | 
						|
        MOZ_CLIPBOARD_LOG("gtk_clipboard_request_contents async handler (%p)",
 | 
						|
                          aData);
 | 
						|
        UniquePtr<TragetCallbackHandler> handler(
 | 
						|
            static_cast<TragetCallbackHandler*>(aData));
 | 
						|
 | 
						|
        GdkAtom* targets = nullptr;
 | 
						|
        gint targetsNum = 0;
 | 
						|
        if (gtk_selection_data_get_length(aSelection) > 0) {
 | 
						|
          gtk_selection_data_get_targets(aSelection, &targets, &targetsNum);
 | 
						|
 | 
						|
#ifdef MOZ_LOGGING
 | 
						|
          if (MOZ_CLIPBOARD_LOG_ENABLED()) {
 | 
						|
            MOZ_CLIPBOARD_LOG("  Clipboard content (target nums %d):\n",
 | 
						|
                              targetsNum);
 | 
						|
            for (int i = 0; i < targetsNum; i++) {
 | 
						|
              GUniquePtr<gchar> atom_name(gdk_atom_name(targets[i]));
 | 
						|
              if (!atom_name) {
 | 
						|
                MOZ_CLIPBOARD_LOG("    failed to get MIME\n");
 | 
						|
                continue;
 | 
						|
              }
 | 
						|
              MOZ_CLIPBOARD_LOG("    MIME %s\n", atom_name.get());
 | 
						|
            }
 | 
						|
          }
 | 
						|
#endif
 | 
						|
        }
 | 
						|
        nsTArray<nsCString> results;
 | 
						|
        if (targetsNum) {
 | 
						|
          for (auto& flavor : handler->mAcceptedFlavorList) {
 | 
						|
            MOZ_CLIPBOARD_LOG("  looking for %s", flavor.get());
 | 
						|
            if (flavor.EqualsLiteral(kTextMime) &&
 | 
						|
                gtk_targets_include_text(targets, targetsNum)) {
 | 
						|
              results.AppendElement(flavor);
 | 
						|
              MOZ_CLIPBOARD_LOG("    has kTextMime\n");
 | 
						|
              continue;
 | 
						|
            }
 | 
						|
            for (int i = 0; i < targetsNum; i++) {
 | 
						|
              if (FlavorMatchesTarget(flavor, targets[i])) {
 | 
						|
                results.AppendElement(flavor);
 | 
						|
              }
 | 
						|
            }
 | 
						|
          }
 | 
						|
        }
 | 
						|
        handler->mCallback(std::move(results));
 | 
						|
      },
 | 
						|
      new TragetCallbackHandler(aFlavorList, std::move(aCallback)));
 | 
						|
}
 | 
						|
 | 
						|
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/plain 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
 | 
						|
 | 
						|
  MOZ_CLIPBOARD_LOG(
 | 
						|
      "nsClipboard::SelectionGetEvent (%s)\n",
 | 
						|
      whichClipboard == kSelectionClipboard ? "primary" : "clipboard");
 | 
						|
 | 
						|
  nsCOMPtr<nsITransferable> trans = GetTransferable(whichClipboard);
 | 
						|
  if (!trans) {
 | 
						|
    // We have nothing to serve
 | 
						|
    MOZ_CLIPBOARD_LOG(
 | 
						|
        "nsClipboard::SelectionGetEvent() - %s clipboard is empty!\n",
 | 
						|
        whichClipboard == kSelectionClipboard ? "Primary" : "Clipboard");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  nsresult rv;
 | 
						|
  nsCOMPtr<nsISupports> item;
 | 
						|
 | 
						|
  GdkAtom selectionTarget = gtk_selection_data_get_target(aSelectionData);
 | 
						|
  MOZ_CLIPBOARD_LOG("  selection target %s\n",
 | 
						|
                    GUniquePtr<gchar>(gdk_atom_name(selectionTarget)).get());
 | 
						|
 | 
						|
  // Check to see if the selection data is some text type.
 | 
						|
  if (gtk_targets_include_text(&selectionTarget, 1)) {
 | 
						|
    MOZ_CLIPBOARD_LOG("  providing text/plain data\n");
 | 
						|
    // Try to convert our internal type into a text string.  Get
 | 
						|
    // the transferable for this clipboard and try to get the
 | 
						|
    // text/plain type for it.
 | 
						|
    rv = trans->GetTransferData("text/plain", getter_AddRefs(item));
 | 
						|
    if (NS_FAILED(rv) || !item) {
 | 
						|
      MOZ_CLIPBOARD_LOG("  GetTransferData() failed to get text/plain!\n");
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    nsCOMPtr<nsISupportsString> wideString;
 | 
						|
    wideString = do_QueryInterface(item);
 | 
						|
    if (!wideString) return;
 | 
						|
 | 
						|
    nsAutoString ucs2string;
 | 
						|
    wideString->GetData(ucs2string);
 | 
						|
    NS_ConvertUTF16toUTF8 utf8string(ucs2string);
 | 
						|
 | 
						|
    MOZ_CLIPBOARD_LOG("  sent %zd bytes of utf-8 data\n", utf8string.Length());
 | 
						|
    if (selectionTarget == gdk_atom_intern("text/plain;charset=utf-8", FALSE)) {
 | 
						|
      MOZ_CLIPBOARD_LOG(
 | 
						|
          "  using gtk_selection_data_set for 'text/plain;charset=utf-8'\n");
 | 
						|
      // Bypass gtk_selection_data_set_text, which will convert \n to \r\n
 | 
						|
      // in some versions of GTK.
 | 
						|
      gtk_selection_data_set(aSelectionData, selectionTarget, 8,
 | 
						|
                             reinterpret_cast<const guchar*>(utf8string.get()),
 | 
						|
                             utf8string.Length());
 | 
						|
    } else {
 | 
						|
      gtk_selection_data_set_text(aSelectionData, utf8string.get(),
 | 
						|
                                  utf8string.Length());
 | 
						|
    }
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // Check to see if the selection data is an image type
 | 
						|
  if (gtk_targets_include_image(&selectionTarget, 1, TRUE)) {
 | 
						|
    MOZ_CLIPBOARD_LOG("  providing image data\n");
 | 
						|
    // Look through our transfer data for the image
 | 
						|
    static const char* const imageMimeTypes[] = {kNativeImageMime,
 | 
						|
                                                 kPNGImageMime, kJPEGImageMime,
 | 
						|
                                                 kJPGImageMime, kGIFImageMime};
 | 
						|
    nsCOMPtr<nsISupports> imageItem;
 | 
						|
    nsCOMPtr<imgIContainer> image;
 | 
						|
    for (uint32_t i = 0; i < ArrayLength(imageMimeTypes); i++) {
 | 
						|
      rv = trans->GetTransferData(imageMimeTypes[i], getter_AddRefs(imageItem));
 | 
						|
      if (NS_FAILED(rv)) {
 | 
						|
        MOZ_CLIPBOARD_LOG("    %s is missing at GetTransferData()\n",
 | 
						|
                          imageMimeTypes[i]);
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      image = do_QueryInterface(imageItem);
 | 
						|
      if (image) {
 | 
						|
        MOZ_CLIPBOARD_LOG("    %s is available at GetTransferData()\n",
 | 
						|
                          imageMimeTypes[i]);
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    if (!image) {  // Not getting an image for an image mime type!?
 | 
						|
      MOZ_CLIPBOARD_LOG(
 | 
						|
          "    Failed to get any image mime from GetTransferData()!\n");
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    RefPtr<GdkPixbuf> pixbuf = nsImageToPixbuf::ImageToPixbuf(image);
 | 
						|
    if (!pixbuf) {
 | 
						|
      MOZ_CLIPBOARD_LOG("    nsImageToPixbuf::ImageToPixbuf() failed!\n");
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    MOZ_CLIPBOARD_LOG("    Setting pixbuf image data as %s\n",
 | 
						|
                      GUniquePtr<gchar>(gdk_atom_name(selectionTarget)).get());
 | 
						|
    gtk_selection_data_set_pixbuf(aSelectionData, pixbuf);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (selectionTarget == gdk_atom_intern(kHTMLMime, FALSE)) {
 | 
						|
    MOZ_CLIPBOARD_LOG("  providing %s data\n", kHTMLMime);
 | 
						|
    rv = trans->GetTransferData(kHTMLMime, getter_AddRefs(item));
 | 
						|
    if (NS_FAILED(rv) || !item) {
 | 
						|
      MOZ_CLIPBOARD_LOG("  failed to get %s data by GetTransferData()!\n",
 | 
						|
                        kHTMLMime);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    nsCOMPtr<nsISupportsString> wideString;
 | 
						|
    wideString = do_QueryInterface(item);
 | 
						|
    if (!wideString) {
 | 
						|
      MOZ_CLIPBOARD_LOG("  failed to get wideString interface!");
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    nsAutoString ucs2string;
 | 
						|
    wideString->GetData(ucs2string);
 | 
						|
 | 
						|
    nsAutoCString html;
 | 
						|
    // Add the prefix so the encoding is correctly detected.
 | 
						|
    html.AppendLiteral(kHTMLMarkupPrefix);
 | 
						|
    AppendUTF16toUTF8(ucs2string, html);
 | 
						|
 | 
						|
    MOZ_CLIPBOARD_LOG("  Setting %zd bytes of %s data\n", html.Length(),
 | 
						|
                      GUniquePtr<gchar>(gdk_atom_name(selectionTarget)).get());
 | 
						|
    gtk_selection_data_set(aSelectionData, selectionTarget, 8,
 | 
						|
                           (const guchar*)html.get(), html.Length());
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // We put kFileMime onto the clipboard as kURIListMime.
 | 
						|
  if (selectionTarget == gdk_atom_intern(kURIListMime, FALSE)) {
 | 
						|
    MOZ_CLIPBOARD_LOG("  providing %s data\n", kURIListMime);
 | 
						|
    rv = trans->GetTransferData(kFileMime, getter_AddRefs(item));
 | 
						|
    if (NS_FAILED(rv) || !item) {
 | 
						|
      MOZ_CLIPBOARD_LOG("  failed to get %s data by GetTransferData()!\n",
 | 
						|
                        kFileMime);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    nsCOMPtr<nsIFile> file = do_QueryInterface(item);
 | 
						|
    if (!file) {
 | 
						|
      MOZ_CLIPBOARD_LOG("  failed to get nsIFile interface!");
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    nsCOMPtr<nsIURI> fileURI;
 | 
						|
    rv = NS_NewFileURI(getter_AddRefs(fileURI), file);
 | 
						|
    if (NS_FAILED(rv)) {
 | 
						|
      MOZ_CLIPBOARD_LOG("  failed to get fileURI\n");
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    nsAutoCString uri;
 | 
						|
    if (NS_FAILED(fileURI->GetSpec(uri))) {
 | 
						|
      MOZ_CLIPBOARD_LOG("  failed to get fileURI spec\n");
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    MOZ_CLIPBOARD_LOG("  Setting %zd bytes of data\n", uri.Length());
 | 
						|
    gtk_selection_data_set(aSelectionData, selectionTarget, 8,
 | 
						|
                           (const guchar*)uri.get(), uri.Length());
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  MOZ_CLIPBOARD_LOG("  Try if we have anything at GetTransferData() for %s\n",
 | 
						|
                    GUniquePtr<gchar>(gdk_atom_name(selectionTarget)).get());
 | 
						|
 | 
						|
  // Try to match up the selection data target to something our
 | 
						|
  // transferable provides.
 | 
						|
  GUniquePtr<gchar> target_name(gdk_atom_name(selectionTarget));
 | 
						|
  if (!target_name) {
 | 
						|
    MOZ_CLIPBOARD_LOG("  Failed to get target name!\n");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  rv = trans->GetTransferData(target_name.get(), getter_AddRefs(item));
 | 
						|
  // nothing found?
 | 
						|
  if (NS_FAILED(rv) || !item) {
 | 
						|
    MOZ_CLIPBOARD_LOG("  Failed to get anything from GetTransferData()!\n");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  void* primitive_data = nullptr;
 | 
						|
  uint32_t dataLen = 0;
 | 
						|
  nsPrimitiveHelpers::CreateDataFromPrimitive(
 | 
						|
      nsDependentCString(target_name.get()), item, &primitive_data, &dataLen);
 | 
						|
  if (!primitive_data) {
 | 
						|
    MOZ_CLIPBOARD_LOG("  Failed to get primitive data!\n");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  MOZ_CLIPBOARD_LOG("  Setting %s as a primitive data type, %d bytes\n",
 | 
						|
                    target_name.get(), dataLen);
 | 
						|
  gtk_selection_data_set(aSelectionData, selectionTarget,
 | 
						|
                         8, /* 8 bits in a unit */
 | 
						|
                         (const guchar*)primitive_data, dataLen);
 | 
						|
  free(primitive_data);
 | 
						|
}
 | 
						|
 | 
						|
void nsClipboard::ClearCachedTargets(int32_t aWhichClipboard) {
 | 
						|
  if (aWhichClipboard == kSelectionClipboard) {
 | 
						|
    nsRetrievalContext::ClearCachedTargetsPrimary(nullptr, nullptr, nullptr);
 | 
						|
  } else {
 | 
						|
    nsRetrievalContext::ClearCachedTargetsClipboard(nullptr, nullptr, nullptr);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void nsClipboard::SelectionClearEvent(GtkClipboard* aGtkClipboard) {
 | 
						|
  int32_t whichClipboard = GetGeckoClipboardType(aGtkClipboard);
 | 
						|
  if (whichClipboard < 0) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  MOZ_CLIPBOARD_LOG(
 | 
						|
      "nsClipboard::SelectionClearEvent (%s)\n",
 | 
						|
      whichClipboard == kSelectionClipboard ? "primary" : "clipboard");
 | 
						|
  ClearCachedTargets(whichClipboard);
 | 
						|
  ClearTransferable(whichClipboard);
 | 
						|
  ClearClipboardCache(whichClipboard);
 | 
						|
}
 | 
						|
 | 
						|
void nsClipboard::OwnerChangedEvent(GtkClipboard* aGtkClipboard,
 | 
						|
                                    GdkEventOwnerChange* aEvent) {
 | 
						|
  int32_t whichClipboard = GetGeckoClipboardType(aGtkClipboard);
 | 
						|
  if (whichClipboard < 0) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  MOZ_CLIPBOARD_LOG(
 | 
						|
      "nsClipboard::OwnerChangedEvent (%s)\n",
 | 
						|
      whichClipboard == kSelectionClipboard ? "primary" : "clipboard");
 | 
						|
  GtkWidget* gtkWidget = [aEvent]() -> GtkWidget* {
 | 
						|
    if (!aEvent->owner) {
 | 
						|
      return nullptr;
 | 
						|
    }
 | 
						|
    gpointer user_data = nullptr;
 | 
						|
    gdk_window_get_user_data(aEvent->owner, &user_data);
 | 
						|
    return GTK_WIDGET(user_data);
 | 
						|
  }();
 | 
						|
  // If we can get GtkWidget from the current clipboard owner, this
 | 
						|
  // owner-changed event must be triggered by ourself via calling
 | 
						|
  // gtk_clipboard_set_with_data, the sequence number should already be handled.
 | 
						|
  if (!gtkWidget) {
 | 
						|
    if (whichClipboard == kSelectionClipboard) {
 | 
						|
      mSelectionSequenceNumber++;
 | 
						|
    } else {
 | 
						|
      mGlobalSequenceNumber++;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  ClearCachedTargets(whichClipboard);
 | 
						|
}
 | 
						|
 | 
						|
void clipboard_get_cb(GtkClipboard* aGtkClipboard,
 | 
						|
                      GtkSelectionData* aSelectionData, guint info,
 | 
						|
                      gpointer user_data) {
 | 
						|
  MOZ_CLIPBOARD_LOG("clipboard_get_cb() callback\n");
 | 
						|
  nsClipboard* clipboard = static_cast<nsClipboard*>(user_data);
 | 
						|
  clipboard->SelectionGetEvent(aGtkClipboard, aSelectionData);
 | 
						|
}
 | 
						|
 | 
						|
void clipboard_clear_cb(GtkClipboard* aGtkClipboard, gpointer user_data) {
 | 
						|
  MOZ_CLIPBOARD_LOG("clipboard_clear_cb() callback\n");
 | 
						|
  nsClipboard* clipboard = static_cast<nsClipboard*>(user_data);
 | 
						|
  clipboard->SelectionClearEvent(aGtkClipboard);
 | 
						|
}
 | 
						|
 | 
						|
void clipboard_owner_change_cb(GtkClipboard* aGtkClipboard,
 | 
						|
                               GdkEventOwnerChange* aEvent,
 | 
						|
                               gpointer aUserData) {
 | 
						|
  MOZ_CLIPBOARD_LOG("clipboard_owner_change_cb() callback\n");
 | 
						|
  nsClipboard* clipboard = static_cast<nsClipboard*>(aUserData);
 | 
						|
  clipboard->OwnerChangedEvent(aGtkClipboard, aEvent);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * This function extracts the encoding label from the subset of HTML internal
 | 
						|
 * encoding declaration syntax that uses the old long form with double quotes
 | 
						|
 * and without spaces around the equals sign between the "content" attribute
 | 
						|
 * name and the attribute value.
 | 
						|
 *
 | 
						|
 * This was added for the sake of an ancient version of StarOffice
 | 
						|
 * in the pre-UTF-8 era in bug 123389. It is unclear if supporting
 | 
						|
 * non-UTF-8 encodings is still necessary and if this function
 | 
						|
 * still needs to exist.
 | 
						|
 *
 | 
						|
 * As of December 2022, both Gecko and LibreOffice emit an UTF-8
 | 
						|
 * declaration that this function successfully extracts "UTF-8" from,
 | 
						|
 * but that's also the default that we fall back on if this function
 | 
						|
 * fails to extract a label.
 | 
						|
 */
 | 
						|
bool GetHTMLCharset(Span<const char> aData, nsCString& aFoundCharset) {
 | 
						|
  // Assume ASCII first to find "charset" info
 | 
						|
  const nsDependentCSubstring htmlStr(aData);
 | 
						|
  nsACString::const_iterator start, end;
 | 
						|
  htmlStr.BeginReading(start);
 | 
						|
  htmlStr.EndReading(end);
 | 
						|
  nsACString::const_iterator valueStart(start), valueEnd(start);
 | 
						|
 | 
						|
  if (CaseInsensitiveFindInReadable("CONTENT=\"text/html;"_ns, start, end)) {
 | 
						|
    start = end;
 | 
						|
    htmlStr.EndReading(end);
 | 
						|
 | 
						|
    if (CaseInsensitiveFindInReadable("charset="_ns, start, end)) {
 | 
						|
      valueStart = end;
 | 
						|
      start = end;
 | 
						|
      htmlStr.EndReading(end);
 | 
						|
 | 
						|
      if (FindCharInReadable('"', start, end)) valueEnd = start;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  // find "charset" in HTML
 | 
						|
  if (valueStart != valueEnd) {
 | 
						|
    aFoundCharset = Substring(valueStart, valueEnd);
 | 
						|
    ToUpperCase(aFoundCharset);
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 |