forked from mirrors/gecko-dev
		
	- Return early when mClipboardRequestNumber does not match and we're getting old data. - Implement kClipboardFastIterationNum to allow fast unthrottled iterations. Depends on D123610 Differential Revision: https://phabricator.services.mozilla.com/D123611
		
			
				
	
	
		
			274 lines
		
	
	
	
		
			9.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			274 lines
		
	
	
	
		
			9.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | 
						|
/* vim:expandtab:shiftwidth=4:tabstop=4:
 | 
						|
 */
 | 
						|
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
#include "mozilla/ArrayUtils.h"
 | 
						|
 | 
						|
#include "nsArrayUtils.h"
 | 
						|
#include "nsClipboard.h"
 | 
						|
#include "nsClipboardWaylandAsync.h"
 | 
						|
#include "nsSupportsPrimitives.h"
 | 
						|
#include "nsString.h"
 | 
						|
#include "nsReadableUtils.h"
 | 
						|
#include "nsPrimitiveHelpers.h"
 | 
						|
#include "nsImageToPixbuf.h"
 | 
						|
#include "nsStringStream.h"
 | 
						|
#include "mozilla/RefPtr.h"
 | 
						|
#include "mozilla/TimeStamp.h"
 | 
						|
#include "mozilla/ScopeExit.h"
 | 
						|
#include "nsWindow.h"
 | 
						|
 | 
						|
#include <gtk/gtk.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
 | 
						|
using namespace mozilla;
 | 
						|
using namespace mozilla::widget;
 | 
						|
 | 
						|
nsRetrievalContextWaylandAsync::nsRetrievalContextWaylandAsync(void)
 | 
						|
    : mClipboardRequestNumber(0),
 | 
						|
      mClipboardDataReceived(),
 | 
						|
      mClipboardData(nullptr),
 | 
						|
      mClipboardDataLength(0),
 | 
						|
      mMutex("nsRetrievalContextWaylandAsync") {}
 | 
						|
 | 
						|
struct AsyncClipboardData {
 | 
						|
  AsyncClipboardData(ClipboardDataType aDataType, int aClipboardRequestNumber,
 | 
						|
                     RefPtr<nsRetrievalContextWaylandAsync> aRetrievalContex)
 | 
						|
      : mClipboardRequestNumber(aClipboardRequestNumber),
 | 
						|
        mRetrievalContex(std::move(aRetrievalContex)),
 | 
						|
        mDataType(aDataType) {}
 | 
						|
  int mClipboardRequestNumber;
 | 
						|
  RefPtr<nsRetrievalContextWaylandAsync> mRetrievalContex;
 | 
						|
  ClipboardDataType mDataType;
 | 
						|
};
 | 
						|
 | 
						|
static void wayland_clipboard_contents_received_async(
 | 
						|
    GtkClipboard* clipboard, GtkSelectionData* selection_data, gpointer data) {
 | 
						|
  LOGCLIP(("wayland_clipboard_contents_received_async() selection_data = %p\n",
 | 
						|
           selection_data));
 | 
						|
  AsyncClipboardData* fastTrack = static_cast<AsyncClipboardData*>(data);
 | 
						|
  fastTrack->mRetrievalContex->TransferAsyncClipboardData(
 | 
						|
      fastTrack->mDataType, fastTrack->mClipboardRequestNumber, selection_data);
 | 
						|
  delete fastTrack;
 | 
						|
}
 | 
						|
 | 
						|
static void wayland_clipboard_text_received(GtkClipboard* clipboard,
 | 
						|
                                            const gchar* text, gpointer data) {
 | 
						|
  LOGCLIP(("wayland_clipboard_text_received() text = %p\n", text));
 | 
						|
  AsyncClipboardData* fastTrack = static_cast<AsyncClipboardData*>(data);
 | 
						|
  fastTrack->mRetrievalContex->TransferAsyncClipboardData(
 | 
						|
      fastTrack->mDataType, fastTrack->mClipboardRequestNumber, (void*)text);
 | 
						|
  delete fastTrack;
 | 
						|
}
 | 
						|
 | 
						|
void nsRetrievalContextWaylandAsync::TransferAsyncClipboardData(
 | 
						|
    ClipboardDataType aDataType, int aClipboardRequestNumber,
 | 
						|
    const void* aData) {
 | 
						|
  LOGCLIP(
 | 
						|
      ("nsRetrievalContextWaylandAsync::TransferAsyncClipboardData(), "
 | 
						|
       "aSelectionData = %p\n",
 | 
						|
       aData));
 | 
						|
 | 
						|
  MOZ_RELEASE_ASSERT(mClipboardData == nullptr && mClipboardDataLength == 0,
 | 
						|
                     "Clipboard contains old data?");
 | 
						|
 | 
						|
  if (mClipboardRequestNumber != aClipboardRequestNumber) {
 | 
						|
    LOGCLIP(("    request number does not match!\n"));
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  LOGCLIP(("    request number matches\n"));
 | 
						|
 | 
						|
  int dataLength = 0;
 | 
						|
  if (aDataType == CLIPBOARD_TARGETS || aDataType == CLIPBOARD_DATA) {
 | 
						|
    dataLength = gtk_selection_data_get_length((GtkSelectionData*)aData);
 | 
						|
  } else {
 | 
						|
    dataLength = aData ? strlen((const char*)aData) : 0;
 | 
						|
  }
 | 
						|
 | 
						|
  mClipboardDataReceived = true;
 | 
						|
 | 
						|
  // Negative size means no data or data error.
 | 
						|
  if (dataLength <= 0) {
 | 
						|
    LOGCLIP(("    zero dataLength, quit.\n"));
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  switch (aDataType) {
 | 
						|
    case CLIPBOARD_TARGETS: {
 | 
						|
      LOGCLIP(("    getting %d bytes of clipboard targets.\n", dataLength));
 | 
						|
      gint n_targets = 0;
 | 
						|
      GdkAtom* targets = nullptr;
 | 
						|
      if (!gtk_selection_data_get_targets((GtkSelectionData*)aData, &targets,
 | 
						|
                                          &n_targets) ||
 | 
						|
          !n_targets) {
 | 
						|
        // We failed to get targes
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      mClipboardData = reinterpret_cast<char*>(targets);
 | 
						|
      mClipboardDataLength = n_targets;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case CLIPBOARD_TEXT: {
 | 
						|
      LOGCLIP(("    getting %d bytes of text.\n", dataLength));
 | 
						|
      mClipboardDataLength = dataLength;
 | 
						|
      mClipboardData = reinterpret_cast<char*>(
 | 
						|
          g_malloc(sizeof(char) * (mClipboardDataLength + 1)));
 | 
						|
      memcpy(mClipboardData, aData, sizeof(char) * mClipboardDataLength);
 | 
						|
      mClipboardData[mClipboardDataLength] = '\0';
 | 
						|
      LOGCLIP(("    done, mClipboardData = %p\n", mClipboardData));
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case CLIPBOARD_DATA: {
 | 
						|
      LOGCLIP(("    getting %d bytes of data.\n", dataLength));
 | 
						|
      mClipboardDataLength = dataLength;
 | 
						|
      mClipboardData = reinterpret_cast<char*>(
 | 
						|
          g_malloc(sizeof(char) * mClipboardDataLength));
 | 
						|
      memcpy(mClipboardData,
 | 
						|
             gtk_selection_data_get_data((GtkSelectionData*)aData),
 | 
						|
             sizeof(char) * mClipboardDataLength);
 | 
						|
      LOGCLIP(("    done, mClipboardData = %p\n", mClipboardData));
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
GdkAtom* nsRetrievalContextWaylandAsync::GetTargets(int32_t aWhichClipboard,
 | 
						|
                                                    int* aTargetNum) {
 | 
						|
  LOGCLIP(("nsRetrievalContextWaylandAsync::GetTargets()\n"));
 | 
						|
 | 
						|
  if (!mMutex.TryLock()) {
 | 
						|
    LOGCLIP(("  nsRetrievalContextWaylandAsync is already used!\n"));
 | 
						|
    *aTargetNum = 0;
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  // GetTargets() does not use ReleaseClipboardData() so we always need to
 | 
						|
  // unlock nsRetrievalContextWaylandAsync.
 | 
						|
  auto unlock = mozilla::MakeScopeExit([&] { mMutex.Unlock(); });
 | 
						|
 | 
						|
  MOZ_RELEASE_ASSERT(mClipboardData == nullptr && mClipboardDataLength == 0,
 | 
						|
                     "Clipboard contains old data?");
 | 
						|
 | 
						|
  GdkAtom selection = GetSelectionAtom(aWhichClipboard);
 | 
						|
  mClipboardDataReceived = false;
 | 
						|
  mClipboardRequestNumber++;
 | 
						|
  gtk_clipboard_request_contents(
 | 
						|
      gtk_clipboard_get(selection), gdk_atom_intern("TARGETS", FALSE),
 | 
						|
      wayland_clipboard_contents_received_async,
 | 
						|
      new AsyncClipboardData(CLIPBOARD_TARGETS, mClipboardRequestNumber, this));
 | 
						|
 | 
						|
  if (!WaitForClipboardContent()) {
 | 
						|
    *aTargetNum = 0;
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  // mClipboardDataLength is only signed integer, see
 | 
						|
  // nsRetrievalContextWaylandAsync::TransferAsyncClipboardData()
 | 
						|
  *aTargetNum = (int)mClipboardDataLength;
 | 
						|
  GdkAtom* targets = static_cast<GdkAtom*>((void*)mClipboardData);
 | 
						|
 | 
						|
  // We don't hold the target list internally but we transfer the ownership.
 | 
						|
  mClipboardData = nullptr;
 | 
						|
  mClipboardDataLength = 0;
 | 
						|
 | 
						|
  return targets;
 | 
						|
}
 | 
						|
 | 
						|
const char* nsRetrievalContextWaylandAsync::GetClipboardData(
 | 
						|
    const char* aMimeType, int32_t aWhichClipboard, uint32_t* aContentLength) {
 | 
						|
  LOGCLIP(("nsRetrievalContextWaylandAsync::GetClipboardData() mime %s\n",
 | 
						|
           aMimeType));
 | 
						|
 | 
						|
  if (!mMutex.TryLock()) {
 | 
						|
    LOGCLIP(("  nsRetrievalContextWaylandAsync is already used!\n"));
 | 
						|
    *aContentLength = 0;
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  MOZ_RELEASE_ASSERT(mClipboardData == nullptr && mClipboardDataLength == 0,
 | 
						|
                     "Clipboard contains old data?");
 | 
						|
 | 
						|
  GdkAtom selection = GetSelectionAtom(aWhichClipboard);
 | 
						|
  mClipboardDataReceived = false;
 | 
						|
  mClipboardRequestNumber++;
 | 
						|
  gtk_clipboard_request_contents(
 | 
						|
      gtk_clipboard_get(selection), gdk_atom_intern(aMimeType, FALSE),
 | 
						|
      wayland_clipboard_contents_received_async,
 | 
						|
      new AsyncClipboardData(CLIPBOARD_DATA, mClipboardRequestNumber, this));
 | 
						|
 | 
						|
  if (!WaitForClipboardContent()) {
 | 
						|
    *aContentLength = 0;
 | 
						|
    mMutex.Unlock();
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  *aContentLength = mClipboardDataLength;
 | 
						|
  return reinterpret_cast<const char*>(mClipboardData);
 | 
						|
}
 | 
						|
 | 
						|
const char* nsRetrievalContextWaylandAsync::GetClipboardText(
 | 
						|
    int32_t aWhichClipboard) {
 | 
						|
  GdkAtom selection = GetSelectionAtom(aWhichClipboard);
 | 
						|
 | 
						|
  LOGCLIP(("nsRetrievalContextWaylandAsync::GetClipboardText(), clipboard %s\n",
 | 
						|
           (selection == GDK_SELECTION_PRIMARY) ? "Primary" : "Selection"));
 | 
						|
 | 
						|
  if (!mMutex.TryLock()) {
 | 
						|
    LOGCLIP(("  nsRetrievalContextWaylandAsync is already used!\n"));
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  MOZ_RELEASE_ASSERT(mClipboardData == nullptr && mClipboardDataLength == 0,
 | 
						|
                     "Clipboard contains old data?");
 | 
						|
 | 
						|
  mClipboardDataReceived = false;
 | 
						|
  mClipboardRequestNumber++;
 | 
						|
  gtk_clipboard_request_text(
 | 
						|
      gtk_clipboard_get(selection), wayland_clipboard_text_received,
 | 
						|
      new AsyncClipboardData(CLIPBOARD_TEXT, mClipboardRequestNumber, this));
 | 
						|
 | 
						|
  if (!WaitForClipboardContent()) {
 | 
						|
    mMutex.Unlock();
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
  return reinterpret_cast<const char*>(mClipboardData);
 | 
						|
}
 | 
						|
 | 
						|
bool nsRetrievalContextWaylandAsync::WaitForClipboardContent() {
 | 
						|
  int iteration = 1;
 | 
						|
 | 
						|
  PRTime entryTime = PR_Now();
 | 
						|
  while (!mClipboardDataReceived) {
 | 
						|
    if (iteration++ > kClipboardFastIterationNum) {
 | 
						|
      /* sleep for 10 ms/iteration */
 | 
						|
      PR_Sleep(PR_MillisecondsToInterval(10));
 | 
						|
      if (PR_Now() - entryTime > kClipboardTimeout) {
 | 
						|
        LOGCLIP(("  failed to get async clipboard data in time limit\n"));
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    LOGCLIP(("doing iteration %d msec %ld ...\n", (iteration - 1),
 | 
						|
             (long)((PR_Now() - entryTime) / 1000)));
 | 
						|
    gtk_main_iteration();
 | 
						|
  }
 | 
						|
 | 
						|
  return mClipboardDataReceived && mClipboardData != nullptr;
 | 
						|
}
 | 
						|
 | 
						|
void nsRetrievalContextWaylandAsync::ReleaseClipboardData(
 | 
						|
    const char* aClipboardData) {
 | 
						|
  LOGCLIP(("nsRetrievalContextWaylandAsync::ReleaseClipboardData [%p]\n",
 | 
						|
           aClipboardData));
 | 
						|
  if (aClipboardData != mClipboardData) {
 | 
						|
    NS_WARNING("Wayland clipboard: Releasing unknown clipboard data!");
 | 
						|
  }
 | 
						|
  g_free((void*)mClipboardData);
 | 
						|
  mClipboardDataLength = 0;
 | 
						|
  mClipboardData = nullptr;
 | 
						|
 | 
						|
  mMutex.Unlock();
 | 
						|
}
 |