/* 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 "nsClipboard.h" #include "gfxDrawable.h" #include "gfxUtils.h" #include "ImageOps.h" #include "imgIContainer.h" #include "imgTools.h" #include "mozilla/dom/ContentChild.h" #include "mozilla/Preferences.h" #include "nsClipboardProxy.h" #include "nsISupportsPrimitives.h" #include "nsComponentManagerUtils.h" #include "nsCOMPtr.h" #include "nsServiceManagerUtils.h" #include "nsStringStream.h" #include "nsXULAppAPI.h" using namespace mozilla; using mozilla::dom::ContentChild; #define LOG_TAG "Clipboard" #define LOGI(args...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, ## args) #define LOGE(args...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, ## args) NS_IMPL_ISUPPORTS(nsClipboard, nsIClipboard) nsClipboard::nsClipboard() : mClipboard(mozilla::MakeUnique()) { } NS_IMETHODIMP nsClipboard::SetData(nsITransferable *aTransferable, nsIClipboardOwner *anOwner, int32_t aWhichClipboard) { if (aWhichClipboard != kGlobalClipboard) { return NS_ERROR_NOT_IMPLEMENTED; } if (!XRE_IsParentProcess()) { // Re-direct to the clipboard proxy. RefPtr clipboardProxy = new nsClipboardProxy(); return clipboardProxy->SetData(aTransferable, anOwner, aWhichClipboard); } // Clear out the clipboard in order to set the new data. EmptyClipboard(aWhichClipboard); // Use a pref to toggle rich text/non-text support. if (Preferences::GetBool("clipboard.plainTextOnly")) { nsCOMPtr clip; uint32_t len; nsresult rv = aTransferable->GetTransferData(kUnicodeMime, getter_AddRefs(clip), &len); if (NS_FAILED(rv)) { return rv; } nsCOMPtr wideString = do_QueryInterface(clip); if (!wideString) { return NS_ERROR_NOT_IMPLEMENTED; } nsAutoString utf16string; wideString->GetData(utf16string); mClipboard->SetText(utf16string); return NS_OK; } // Get the types of supported flavors. nsCOMPtr flavorList; nsresult rv = aTransferable->FlavorsTransferableCanExport(getter_AddRefs(flavorList)); if (!flavorList || NS_FAILED(rv)) { return NS_ERROR_FAILURE; } uint32_t flavorCount = 0; flavorList->Count(&flavorCount); bool imageAdded = false; for (uint32_t i = 0; i < flavorCount; ++i) { nsCOMPtr currentFlavor = do_QueryElementAt(flavorList, i); if (currentFlavor) { // MIME type nsXPIDLCString flavorStr; currentFlavor->ToString(getter_Copies(flavorStr)); // Clip is the data which will be sent to the clipboard. nsCOMPtr clip; uint32_t len; if (flavorStr.EqualsLiteral(kUnicodeMime)) { // text/plain rv = aTransferable->GetTransferData(flavorStr, getter_AddRefs(clip), &len); nsCOMPtr wideString = do_QueryInterface(clip); if (!wideString || NS_FAILED(rv)) { continue; } nsAutoString utf16string; wideString->GetData(utf16string); mClipboard->SetText(utf16string); } else if (flavorStr.EqualsLiteral(kHTMLMime)) { // text/html rv = aTransferable->GetTransferData(flavorStr, getter_AddRefs(clip), &len); nsCOMPtr wideString = do_QueryInterface(clip); if (!wideString || NS_FAILED(rv)) { continue; } nsAutoString utf16string; wideString->GetData(utf16string); mClipboard->SetHTML(utf16string); } else if (!imageAdded && // image is added only once to the clipboard. (flavorStr.EqualsLiteral(kNativeImageMime) || flavorStr.EqualsLiteral(kPNGImageMime) || flavorStr.EqualsLiteral(kJPEGImageMime) || flavorStr.EqualsLiteral(kJPGImageMime))) { // image/[png|jpeg|jpg] or application/x-moz-nativeimage // Look through our transfer data for the image. static const char* const imageMimeTypes[] = { kNativeImageMime, kPNGImageMime, kJPEGImageMime, kJPGImageMime }; nsCOMPtr imgPtr; for (uint32_t i = 0; !imgPtr && i < ArrayLength(imageMimeTypes); ++i) { aTransferable->GetTransferData(imageMimeTypes[i], getter_AddRefs(clip), &len); imgPtr = do_QueryInterface(clip); } if (!imgPtr) { continue; } nsCOMPtr imageData; imgPtr->GetData(getter_AddRefs(imageData)); nsCOMPtr image(do_QueryInterface(imageData)); if (!image) { continue; } RefPtr surface = image->GetFrame(imgIContainer::FRAME_CURRENT, imgIContainer::FLAG_SYNC_DECODE); if (!surface) { continue; } RefPtr dataSurface; if (surface->GetFormat() == gfx::SurfaceFormat::B8G8R8A8) { dataSurface = surface->GetDataSurface(); } else { // Convert format to SurfaceFormat::B8G8R8A8. dataSurface = gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat(surface, gfx::SurfaceFormat::B8G8R8A8); } mClipboard->SetImage(dataSurface); imageAdded = true; } } } return NS_OK; } NS_IMETHODIMP nsClipboard::GetData(nsITransferable *aTransferable, int32_t aWhichClipboard) { if (aWhichClipboard != kGlobalClipboard) { return NS_ERROR_NOT_IMPLEMENTED; } if (!XRE_IsParentProcess()) { // Re-direct to the clipboard proxy. RefPtr clipboardProxy = new nsClipboardProxy(); return clipboardProxy->GetData(aTransferable, aWhichClipboard); } // Use a pref to toggle rich text/non-text support. if (Preferences::GetBool("clipboard.plainTextOnly")) { nsresult rv; nsCOMPtr dataWrapper = do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv); rv = dataWrapper->SetData(mClipboard->GetText()); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } nsCOMPtr genericDataWrapper = do_QueryInterface(dataWrapper); uint32_t len = mClipboard->GetText().Length() * sizeof(char16_t); rv = aTransferable->SetTransferData(kUnicodeMime, genericDataWrapper, len); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } return NS_OK; } // Get flavor list that includes all acceptable flavors (including // ones obtained through conversion). // Note: We don't need to call nsITransferable::AddDataFlavor here // because ContentParent already did. nsCOMPtr flavorList; nsresult rv = aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavorList)); if (!flavorList || NS_FAILED(rv)) { return NS_ERROR_FAILURE; } // Walk through flavors and see which flavor matches the one being pasted. uint32_t flavorCount; flavorList->Count(&flavorCount); for (uint32_t i = 0; i < flavorCount; ++i) { nsCOMPtr currentFlavor = do_QueryElementAt(flavorList, i); if (currentFlavor) { // flavorStr is the mime type. nsXPIDLCString flavorStr; currentFlavor->ToString(getter_Copies(flavorStr)); // text/plain, text/Unicode if (flavorStr.EqualsLiteral(kUnicodeMime) && mClipboard->HasText()) { nsresult rv; nsCOMPtr dataWrapper = do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv); rv = dataWrapper->SetData(mClipboard->GetText()); if (NS_WARN_IF(NS_FAILED(rv))) { continue; } nsCOMPtr genericDataWrapper = do_QueryInterface(dataWrapper); uint32_t len = mClipboard->GetText().Length() * sizeof(char16_t); rv = aTransferable->SetTransferData(flavorStr, genericDataWrapper, len); if (NS_WARN_IF(NS_FAILED(rv))) { continue; } break; } // text/html if (flavorStr.EqualsLiteral(kHTMLMime) && mClipboard->HasHTML()) { nsresult rv; nsCOMPtr dataWrapper = do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv); rv = dataWrapper->SetData(mClipboard->GetHTML()); if (NS_WARN_IF(NS_FAILED(rv))) { continue; } nsCOMPtr genericDataWrapper = do_QueryInterface(dataWrapper); uint32_t len = mClipboard->GetHTML().Length() * sizeof(char16_t); rv = aTransferable->SetTransferData(flavorStr, genericDataWrapper, len); if (NS_WARN_IF(NS_FAILED(rv))) { continue; } break; } // image/[png|jpeg|jpg] if ((flavorStr.EqualsLiteral(kPNGImageMime) || flavorStr.EqualsLiteral(kJPEGImageMime) || flavorStr.EqualsLiteral(kJPGImageMime)) && mClipboard->HasImage() ) { // Get image buffer from clipboard. RefPtr image = mClipboard->GetImage(); // Encode according to MIME type. RefPtr drawable = new gfxSurfaceDrawable(image, image->GetSize()); nsCOMPtr imageContainer(image::ImageOps::CreateFromDrawable(drawable)); nsCOMPtr imgTool = do_GetService(NS_IMGTOOLS_CID); nsCOMPtr byteStream; imgTool->EncodeImage(imageContainer, flavorStr, EmptyString(), getter_AddRefs(byteStream)); // Set transferable. nsresult rv = aTransferable->SetTransferData(flavorStr, byteStream, sizeof(nsIInputStream*)); if (NS_WARN_IF(NS_FAILED(rv))) { continue; } break; } } } return NS_OK; } NS_IMETHODIMP nsClipboard::EmptyClipboard(int32_t aWhichClipboard) { if (aWhichClipboard != kGlobalClipboard) { return NS_ERROR_NOT_IMPLEMENTED; } if (XRE_IsParentProcess()) { mClipboard->Clear(); } else { ContentChild::GetSingleton()->SendEmptyClipboard(aWhichClipboard); } return NS_OK; } NS_IMETHODIMP nsClipboard::HasDataMatchingFlavors(const char **aFlavorList, uint32_t aLength, int32_t aWhichClipboard, bool *aHasType) { *aHasType = false; if (aWhichClipboard != kGlobalClipboard) { return NS_ERROR_NOT_IMPLEMENTED; } if (XRE_IsParentProcess()) { // Retrieve the union of all aHasType in aFlavorList for (uint32_t i = 0; i < aLength; ++i) { const char *flavor = aFlavorList[i]; if (!flavor) { continue; } if (!strcmp(flavor, kUnicodeMime) && mClipboard->HasText()) { *aHasType = true; } else if (!strcmp(flavor, kHTMLMime) && mClipboard->HasHTML()) { *aHasType = true; } else if (!strcmp(flavor, kJPEGImageMime) || !strcmp(flavor, kJPGImageMime) || !strcmp(flavor, kPNGImageMime)) { // We will encode the image into any format you want, so we don't // need to check each specific format if (mClipboard->HasImage()) { *aHasType = true; } } } } else { RefPtr clipboardProxy = new nsClipboardProxy(); return clipboardProxy->HasDataMatchingFlavors(aFlavorList, aLength, aWhichClipboard, aHasType); } return NS_OK; } NS_IMETHODIMP nsClipboard::SupportsSelectionClipboard(bool *aIsSupported) { *aIsSupported = false; return NS_OK; } NS_IMETHODIMP nsClipboard::SupportsFindClipboard(bool* _retval) { NS_ENSURE_ARG_POINTER(_retval); *_retval = false; return NS_OK; }