Blob Blame History Raw
/* 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/dom/ContentChild.h"
#include "mozilla/Unused.h"
#include "nsArrayUtils.h"
#include "nsClipboardProxy.h"
#include "nsISupportsPrimitives.h"
#include "nsCOMPtr.h"
#include "nsComponentManagerUtils.h"
#include "nsXULAppAPI.h"
#include "nsContentUtils.h"
#include "nsStringStream.h"

using namespace mozilla;
using namespace mozilla::dom;

NS_IMPL_ISUPPORTS(nsClipboardProxy, nsIClipboard, nsIClipboardProxy)

nsClipboardProxy::nsClipboardProxy() : mClipboardCaps(false, false) {}

NS_IMETHODIMP
nsClipboardProxy::SetData(nsITransferable *aTransferable,
                          nsIClipboardOwner *anOwner, int32_t aWhichClipboard) {
  ContentChild *child = ContentChild::GetSingleton();

  IPCDataTransfer ipcDataTransfer;
  nsContentUtils::TransferableToIPCTransferable(aTransferable, &ipcDataTransfer,
                                                false, child, nullptr);

  bool isPrivateData = false;
  aTransferable->GetIsPrivateData(&isPrivateData);
  nsCOMPtr<nsIPrincipal> requestingPrincipal;
  aTransferable->GetRequestingPrincipal(getter_AddRefs(requestingPrincipal));
  nsContentPolicyType contentPolicyType = nsIContentPolicy::TYPE_OTHER;
  aTransferable->GetContentPolicyType(&contentPolicyType);
  child->SendSetClipboard(ipcDataTransfer, isPrivateData,
                          IPC::Principal(requestingPrincipal),
                          contentPolicyType, aWhichClipboard);

  return NS_OK;
}

NS_IMETHODIMP
nsClipboardProxy::GetData(nsITransferable *aTransferable,
                          int32_t aWhichClipboard) {
  nsTArray<nsCString> types;

  nsCOMPtr<nsIArray> flavorList;
  aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavorList));
  if (flavorList) {
    uint32_t flavorCount = 0;
    flavorList->GetLength(&flavorCount);
    for (uint32_t j = 0; j < flavorCount; ++j) {
      nsCOMPtr<nsISupportsCString> flavor = do_QueryElementAt(flavorList, j);
      if (flavor) {
        nsAutoCString flavorStr;
        flavor->GetData(flavorStr);
        if (flavorStr.Length()) {
          types.AppendElement(flavorStr);
        }
      }
    }
  }

  nsresult rv;
  IPCDataTransfer dataTransfer;
  ContentChild::GetSingleton()->SendGetClipboard(types, aWhichClipboard,
                                                 &dataTransfer);

  auto &items = dataTransfer.items();
  for (uint32_t j = 0; j < items.Length(); ++j) {
    const IPCDataTransferItem &item = items[j];

    if (item.data().type() == IPCDataTransferData::TnsString) {
      nsCOMPtr<nsISupportsString> dataWrapper =
          do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
      NS_ENSURE_SUCCESS(rv, rv);

      nsString data = item.data().get_nsString();
      rv = dataWrapper->SetData(data);
      NS_ENSURE_SUCCESS(rv, rv);

      rv = aTransferable->SetTransferData(item.flavor().get(), dataWrapper,
                                          data.Length() * sizeof(char16_t));
      NS_ENSURE_SUCCESS(rv, rv);
    } else if (item.data().type() == IPCDataTransferData::TShmem) {
      // If this is an image, convert it into an nsIInputStream.
      nsCString flavor = item.flavor();
      mozilla::ipc::Shmem data = item.data().get_Shmem();
      if (flavor.EqualsLiteral(kJPEGImageMime) ||
          flavor.EqualsLiteral(kJPGImageMime) ||
          flavor.EqualsLiteral(kPNGImageMime) ||
          flavor.EqualsLiteral(kGIFImageMime)) {
        nsCOMPtr<nsIInputStream> stream;

        NS_NewCStringInputStream(
            getter_AddRefs(stream),
            nsDependentCSubstring(data.get<char>(), data.Size<char>()));

        rv = aTransferable->SetTransferData(flavor.get(), stream,
                                            sizeof(nsISupports *));
        NS_ENSURE_SUCCESS(rv, rv);
      } else if (flavor.EqualsLiteral(kNativeHTMLMime) ||
                 flavor.EqualsLiteral(kRTFMime) ||
                 flavor.EqualsLiteral(kCustomTypesMime)) {
        nsCOMPtr<nsISupportsCString> dataWrapper =
            do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv);
        NS_ENSURE_SUCCESS(rv, rv);

        rv = dataWrapper->SetData(
            nsDependentCSubstring(data.get<char>(), data.Size<char>()));
        NS_ENSURE_SUCCESS(rv, rv);

        rv = aTransferable->SetTransferData(item.flavor().get(), dataWrapper,
                                            data.Size<char>());
        NS_ENSURE_SUCCESS(rv, rv);
      }

      mozilla::Unused << ContentChild::GetSingleton()->DeallocShmem(data);
    }
  }

  return NS_OK;
}

NS_IMETHODIMP
nsClipboardProxy::EmptyClipboard(int32_t aWhichClipboard) {
  ContentChild::GetSingleton()->SendEmptyClipboard(aWhichClipboard);
  return NS_OK;
}

NS_IMETHODIMP
nsClipboardProxy::HasDataMatchingFlavors(const char **aFlavorList,
                                         uint32_t aLength,
                                         int32_t aWhichClipboard,
                                         bool *aHasType) {
  *aHasType = false;

  nsTArray<nsCString> types;
  nsCString *t = types.AppendElements(aLength);
  for (uint32_t j = 0; j < aLength; ++j) {
    t[j].Rebind(aFlavorList[j], nsCharTraits<char>::length(aFlavorList[j]));
  }

  ContentChild::GetSingleton()->SendClipboardHasType(types, aWhichClipboard,
                                                     aHasType);

  return NS_OK;
}

NS_IMETHODIMP
nsClipboardProxy::SupportsSelectionClipboard(bool *aIsSupported) {
  *aIsSupported = mClipboardCaps.supportsSelectionClipboard();
  return NS_OK;
}

NS_IMETHODIMP
nsClipboardProxy::SupportsFindClipboard(bool *aIsSupported) {
  *aIsSupported = mClipboardCaps.supportsFindClipboard();
  return NS_OK;
}

void nsClipboardProxy::SetCapabilities(
    const ClipboardCapabilities &aClipboardCaps) {
  mClipboardCaps = aClipboardCaps;
}