summaryrefslogtreecommitdiffstats
path: root/widget/nsTransferable.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'widget/nsTransferable.cpp')
-rw-r--r--widget/nsTransferable.cpp531
1 files changed, 531 insertions, 0 deletions
diff --git a/widget/nsTransferable.cpp b/widget/nsTransferable.cpp
new file mode 100644
index 0000000000..c82549a4d1
--- /dev/null
+++ b/widget/nsTransferable.cpp
@@ -0,0 +1,531 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
+
+/*
+Notes to self:
+
+- at some point, strings will be accessible from JS, so we won't have to wrap
+ flavors in an nsISupportsCString. Until then, we're kinda stuck with
+ this crappy API of nsIArrays.
+
+*/
+
+#include "nsTransferable.h"
+#include "nsAnonymousTemporaryFile.h"
+#include "nsArray.h"
+#include "nsArrayUtils.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsTArray.h"
+#include "nsIFormatConverter.h"
+#include "nsIContentPolicy.h"
+#include "nsCOMPtr.h"
+#include "nsXPCOM.h"
+#include "nsISupportsPrimitives.h"
+#include "nsMemory.h"
+#include "nsPrimitiveHelpers.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryService.h"
+#include "nsCRT.h"
+#include "nsNetUtil.h"
+#include "nsILoadContext.h"
+#include "nsXULAppAPI.h"
+#include "mozilla/UniquePtr.h"
+
+using namespace mozilla;
+
+NS_IMPL_ISUPPORTS(nsTransferable, nsITransferable)
+
+DataStruct::DataStruct(DataStruct&& aRHS)
+ : mData(aRHS.mData.forget()),
+ mCacheFD(aRHS.mCacheFD),
+ mFlavor(aRHS.mFlavor) {
+ aRHS.mCacheFD = nullptr;
+}
+
+//-------------------------------------------------------------------------
+DataStruct::~DataStruct() {
+ if (mCacheFD) {
+ PR_Close(mCacheFD);
+ }
+}
+
+//-------------------------------------------------------------------------
+
+void DataStruct::SetData(nsISupports* aData, bool aIsPrivateData) {
+ // Now, check to see if we consider the data to be "too large"
+ // as well as ensuring that private browsing mode is disabled.
+ // File IO is not allowed in content processes.
+ if (!aIsPrivateData && XRE_IsParentProcess()) {
+ void* data = nullptr;
+ uint32_t dataLen = 0;
+ nsPrimitiveHelpers::CreateDataFromPrimitive(mFlavor, aData, &data,
+ &dataLen);
+
+ if (dataLen > kLargeDatasetSize) {
+ // Too large, cache it to disk instead of memory.
+ if (NS_SUCCEEDED(WriteCache(data, dataLen))) {
+ free(data);
+ // Clear previously set small data.
+ mData = nullptr;
+ return;
+ }
+
+ NS_WARNING("Oh no, couldn't write data to the cache file");
+ }
+
+ free(data);
+ }
+
+ if (mCacheFD) {
+ // Clear previously set big data.
+ PR_Close(mCacheFD);
+ mCacheFD = nullptr;
+ }
+
+ mData = aData;
+}
+
+//-------------------------------------------------------------------------
+void DataStruct::GetData(nsISupports** aData) {
+ // check here to see if the data is cached on disk
+ if (mCacheFD) {
+ // if so, read it in and pass it back
+ // ReadCache creates memory and copies the data into it.
+ if (NS_SUCCEEDED(ReadCache(aData))) {
+ return;
+ }
+
+ // oh shit, something went horribly wrong here.
+ NS_WARNING("Oh no, couldn't read data in from the cache file");
+ *aData = nullptr;
+ PR_Close(mCacheFD);
+ mCacheFD = nullptr;
+ return;
+ }
+
+ nsCOMPtr<nsISupports> data = mData;
+ data.forget(aData);
+}
+
+//-------------------------------------------------------------------------
+nsresult DataStruct::WriteCache(void* aData, uint32_t aDataLen) {
+ MOZ_ASSERT(aData && aDataLen);
+ MOZ_ASSERT(aDataLen <= uint32_t(std::numeric_limits<int32_t>::max()),
+ "too large size for PR_Write");
+
+ nsresult rv;
+ if (!mCacheFD) {
+ rv = NS_OpenAnonymousTemporaryFile(&mCacheFD);
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+ } else if (PR_Seek64(mCacheFD, 0, PR_SEEK_SET) == -1) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Write out the contents of the clipboard to the file.
+ int32_t written = PR_Write(mCacheFD, aData, aDataLen);
+ if (written == int32_t(aDataLen)) {
+ return NS_OK;
+ }
+
+ PR_Close(mCacheFD);
+ mCacheFD = nullptr;
+ return NS_ERROR_FAILURE;
+}
+
+//-------------------------------------------------------------------------
+nsresult DataStruct::ReadCache(nsISupports** aData) {
+ if (!mCacheFD) {
+ return NS_ERROR_FAILURE;
+ }
+
+ PRFileInfo fileInfo;
+ if (PR_GetOpenFileInfo(mCacheFD, &fileInfo) != PR_SUCCESS) {
+ return NS_ERROR_FAILURE;
+ }
+ if (PR_Seek64(mCacheFD, 0, PR_SEEK_SET) == -1) {
+ return NS_ERROR_FAILURE;
+ }
+ uint32_t fileSize = fileInfo.size;
+
+ auto data = MakeUnique<char[]>(fileSize);
+ if (!data) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ uint32_t actual = PR_Read(mCacheFD, data.get(), fileSize);
+ if (actual != fileSize) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsPrimitiveHelpers::CreatePrimitiveForData(mFlavor, data.get(), fileSize,
+ aData);
+ return NS_OK;
+}
+
+//-------------------------------------------------------------------------
+//
+// Transferable constructor
+//
+//-------------------------------------------------------------------------
+nsTransferable::nsTransferable()
+ : mPrivateData(false),
+ mContentPolicyType(nsIContentPolicy::TYPE_OTHER)
+#ifdef DEBUG
+ ,
+ mInitialized(false)
+#endif
+{
+}
+
+//-------------------------------------------------------------------------
+//
+// Transferable destructor
+//
+//-------------------------------------------------------------------------
+nsTransferable::~nsTransferable() = default;
+
+NS_IMETHODIMP
+nsTransferable::Init(nsILoadContext* aContext) {
+ MOZ_ASSERT(!mInitialized);
+
+ if (aContext) {
+ mPrivateData = aContext->UsePrivateBrowsing();
+ }
+#ifdef DEBUG
+ mInitialized = true;
+#endif
+ return NS_OK;
+}
+
+//
+// GetTransferDataFlavors
+//
+// Returns a copy of the internal list of flavors. This does NOT take into
+// account any converter that may be registered.
+//
+void nsTransferable::GetTransferDataFlavors(nsTArray<nsCString>& aFlavors) {
+ MOZ_ASSERT(mInitialized);
+
+ for (size_t i = 0; i < mDataArray.Length(); ++i) {
+ DataStruct& data = mDataArray.ElementAt(i);
+ aFlavors.AppendElement(data.GetFlavor());
+ }
+}
+
+Maybe<size_t> nsTransferable::FindDataFlavor(const char* aFlavor) {
+ nsDependentCString flavor(aFlavor);
+
+ for (size_t i = 0; i < mDataArray.Length(); ++i) {
+ if (mDataArray[i].GetFlavor().Equals(flavor)) {
+ return Some(i);
+ }
+ }
+
+ return Nothing();
+}
+
+//
+// GetTransferData
+//
+// Returns the data of the requested flavor, obtained from either having the
+// data on hand or using a converter to get it. The data is wrapped in a
+// nsISupports primitive so that it is accessible from JS.
+//
+NS_IMETHODIMP
+nsTransferable::GetTransferData(const char* aFlavor, nsISupports** aData) {
+ MOZ_ASSERT(mInitialized);
+
+ *aData = nullptr;
+
+ nsresult rv = NS_OK;
+
+ // First look and see if the data is present in one of the intrinsic flavors.
+ if (Maybe<size_t> index = FindDataFlavor(aFlavor)) {
+ nsCOMPtr<nsISupports> dataBytes;
+ mDataArray[index.value()].GetData(getter_AddRefs(dataBytes));
+
+ // Do we have a (lazy) data provider?
+ if (nsCOMPtr<nsIFlavorDataProvider> dataProvider =
+ do_QueryInterface(dataBytes)) {
+ rv =
+ dataProvider->GetFlavorData(this, aFlavor, getter_AddRefs(dataBytes));
+ if (NS_FAILED(rv)) {
+ dataBytes = nullptr;
+ // The provider failed, fall into the converter code below.
+ }
+ }
+
+ if (dataBytes) {
+ dataBytes.forget(aData);
+ return NS_OK;
+ }
+
+ // Empty data
+ }
+
+ // If not, try using a format converter to get the requested flavor.
+ if (mFormatConv) {
+ for (size_t i = 0; i < mDataArray.Length(); ++i) {
+ DataStruct& data = mDataArray.ElementAt(i);
+ bool canConvert = false;
+ mFormatConv->CanConvert(data.GetFlavor().get(), aFlavor, &canConvert);
+ if (canConvert) {
+ nsCOMPtr<nsISupports> dataBytes;
+ data.GetData(getter_AddRefs(dataBytes));
+
+ // Do we have a (lazy) data provider?
+ if (nsCOMPtr<nsIFlavorDataProvider> dataProvider =
+ do_QueryInterface(dataBytes)) {
+ rv = dataProvider->GetFlavorData(this, aFlavor,
+ getter_AddRefs(dataBytes));
+ if (NS_FAILED(rv)) {
+ // Give up.
+ return rv;
+ }
+ }
+
+ return mFormatConv->Convert(data.GetFlavor().get(), dataBytes, aFlavor,
+ aData);
+ }
+ }
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+//
+// GetAnyTransferData
+//
+// Returns the data of the first flavor found. Caller is responsible for
+// deleting the flavor string.
+//
+NS_IMETHODIMP
+nsTransferable::GetAnyTransferData(nsACString& aFlavor, nsISupports** aData) {
+ MOZ_ASSERT(mInitialized);
+
+ for (size_t i = 0; i < mDataArray.Length(); ++i) {
+ DataStruct& data = mDataArray.ElementAt(i);
+ if (data.IsDataAvailable()) {
+ aFlavor.Assign(data.GetFlavor());
+ data.GetData(aData);
+ return NS_OK;
+ }
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+//
+// SetTransferData
+//
+//
+//
+NS_IMETHODIMP
+nsTransferable::SetTransferData(const char* aFlavor, nsISupports* aData) {
+ MOZ_ASSERT(mInitialized);
+
+ // first check our intrinsic flavors to see if one has been registered.
+ if (Maybe<size_t> index = FindDataFlavor(aFlavor)) {
+ DataStruct& data = mDataArray.ElementAt(index.value());
+ data.SetData(aData, mPrivateData);
+ return NS_OK;
+ }
+
+ // if not, try using a format converter to find a flavor to put the data in
+ if (mFormatConv) {
+ for (size_t i = 0; i < mDataArray.Length(); ++i) {
+ DataStruct& data = mDataArray.ElementAt(i);
+ bool canConvert = false;
+ mFormatConv->CanConvert(aFlavor, data.GetFlavor().get(), &canConvert);
+
+ if (canConvert) {
+ nsCOMPtr<nsISupports> ConvertedData;
+ mFormatConv->Convert(aFlavor, aData, data.GetFlavor().get(),
+ getter_AddRefs(ConvertedData));
+ data.SetData(ConvertedData, mPrivateData);
+ return NS_OK;
+ }
+ }
+ }
+
+ // Can't set data neither directly nor through converter. Just add this flavor
+ // and try again
+ if (NS_SUCCEEDED(AddDataFlavor(aFlavor))) {
+ return SetTransferData(aFlavor, aData);
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+//
+// AddDataFlavor
+//
+// Adds a data flavor to our list with no data. Error if it already exists.
+//
+NS_IMETHODIMP
+nsTransferable::AddDataFlavor(const char* aDataFlavor) {
+ MOZ_ASSERT(mInitialized);
+
+ if (FindDataFlavor(aDataFlavor).isSome()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Create a new "slot" for the data
+ mDataArray.AppendElement(DataStruct(aDataFlavor));
+ return NS_OK;
+}
+
+//
+// RemoveDataFlavor
+//
+// Removes a data flavor (and causes the data to be destroyed). Error if
+// the requested flavor is not present.
+//
+NS_IMETHODIMP
+nsTransferable::RemoveDataFlavor(const char* aDataFlavor) {
+ MOZ_ASSERT(mInitialized);
+
+ if (Maybe<size_t> index = FindDataFlavor(aDataFlavor)) {
+ mDataArray.RemoveElementAt(index.value());
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsTransferable::SetConverter(nsIFormatConverter* aConverter) {
+ MOZ_ASSERT(mInitialized);
+
+ mFormatConv = aConverter;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsTransferable::GetConverter(nsIFormatConverter** aConverter) {
+ MOZ_ASSERT(mInitialized);
+
+ nsCOMPtr<nsIFormatConverter> converter = mFormatConv;
+ converter.forget(aConverter);
+ return NS_OK;
+}
+
+//
+// FlavorsTransferableCanImport
+//
+// Computes a list of flavors that the transferable can accept into it, either
+// through intrinsic knowledge or input data converters.
+//
+NS_IMETHODIMP
+nsTransferable::FlavorsTransferableCanImport(nsTArray<nsCString>& aFlavors) {
+ MOZ_ASSERT(mInitialized);
+
+ // Get the flavor list, and on to the end of it, append the list of flavors we
+ // can also get to through a converter. This is so that we can just walk the
+ // list in one go, looking for the desired flavor.
+ GetTransferDataFlavors(aFlavors);
+
+ if (mFormatConv) {
+ nsTArray<nsCString> convertedList;
+ mFormatConv->GetInputDataFlavors(convertedList);
+
+ for (uint32_t i = 0; i < convertedList.Length(); ++i) {
+ nsCString& flavorStr = convertedList[i];
+
+ // Don't append if already in intrinsic list
+ if (!aFlavors.Contains(flavorStr)) {
+ aFlavors.AppendElement(flavorStr);
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+//
+// FlavorsTransferableCanExport
+//
+// Computes a list of flavors that the transferable can export, either through
+// intrinsic knowledge or output data converters.
+//
+NS_IMETHODIMP
+nsTransferable::FlavorsTransferableCanExport(nsTArray<nsCString>& aFlavors) {
+ MOZ_ASSERT(mInitialized);
+
+ // Get the flavor list, and on to the end of it, append the list of flavors we
+ // can also get to through a converter. This is so that we can just walk the
+ // list in one go, looking for the desired flavor.
+ GetTransferDataFlavors(aFlavors);
+
+ if (mFormatConv) {
+ nsTArray<nsCString> convertedList;
+ mFormatConv->GetOutputDataFlavors(convertedList);
+
+ for (uint32_t i = 0; i < convertedList.Length(); ++i) {
+ nsCString& flavorStr = convertedList[i];
+
+ // Don't append if already in intrinsic list
+ if (!aFlavors.Contains(flavorStr)) {
+ aFlavors.AppendElement(flavorStr);
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+bool nsTransferable::GetIsPrivateData() {
+ MOZ_ASSERT(mInitialized);
+
+ return mPrivateData;
+}
+
+void nsTransferable::SetIsPrivateData(bool aIsPrivateData) {
+ MOZ_ASSERT(mInitialized);
+
+ mPrivateData = aIsPrivateData;
+}
+
+nsIPrincipal* nsTransferable::GetRequestingPrincipal() {
+ MOZ_ASSERT(mInitialized);
+
+ return mRequestingPrincipal;
+}
+
+void nsTransferable::SetRequestingPrincipal(
+ nsIPrincipal* aRequestingPrincipal) {
+ MOZ_ASSERT(mInitialized);
+
+ mRequestingPrincipal = aRequestingPrincipal;
+}
+
+nsContentPolicyType nsTransferable::GetContentPolicyType() {
+ MOZ_ASSERT(mInitialized);
+
+ return mContentPolicyType;
+}
+
+void nsTransferable::SetContentPolicyType(
+ nsContentPolicyType aContentPolicyType) {
+ MOZ_ASSERT(mInitialized);
+
+ mContentPolicyType = aContentPolicyType;
+}
+
+nsICookieJarSettings* nsTransferable::GetCookieJarSettings() {
+ MOZ_ASSERT(mInitialized);
+
+ return mCookieJarSettings;
+}
+
+void nsTransferable::SetCookieJarSettings(
+ nsICookieJarSettings* aCookieJarSettings) {
+ MOZ_ASSERT(mInitialized);
+
+ mCookieJarSettings = aCookieJarSettings;
+}