/* * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ #include "config.h" #include "PasteboardUtilitiesJava.h" #include "CachedImage.h" #include "DocumentFragment.h" #include "Image.h" #include "Editor.h" #include "Frame.h" #include "FrameView.h" #include "markup.h" #include "Pasteboard.h" #include "RenderImage.h" #include "Range.h" #include "NotImplemented.h" #include "DataObjectJava.h" #include "DragData.h" #include #include #include #include #include "NamedNodeMap.h" #include "Attr.h" #include "HTMLNames.h" #include "HTMLParserIdioms.h" #include "wtf/Ref.h" namespace WebCore { /////////////////// // WCPasteboard JNI /////////////////// namespace { #define PB_CLASS jPBClass() #define DEFINE_PB_CLASS(_name) \ JNIEnv* env = WebCore_GetJavaEnv(); \ static JGClass cls(env->FindClass(_name)); \ ASSERT(cls); #define DEFINE_PB_STATIC_METHOD(_name, _params) \ JNIEnv* env = WebCore_GetJavaEnv(); \ static jmethodID mid = env->GetStaticMethodID(PB_CLASS, _name, _params); \ ASSERT(mid); #define CALL_PB_STATIC_VOID_METHOD(...) \ env->CallStaticVoidMethod(PB_CLASS, mid, __VA_ARGS__); \ CheckAndClearException(env); #define CALL_PB_STATIC_JSTROBJ_METHOD(_jstrobj) \ JLString _jstrobj(static_cast(env->CallStaticObjectMethod(PB_CLASS, mid))); \ CheckAndClearException(env); jclass jPBClass() { DEFINE_PB_CLASS("com/sun/webkit/WCPasteboard"); return cls; } String jGetPlainText() { DEFINE_PB_STATIC_METHOD("getPlainText", "()Ljava/lang/String;"); CALL_PB_STATIC_JSTROBJ_METHOD(jstr); return jstr ? String(env, jstr) : String(); } void jWritePlainText(const String& plainText) { DEFINE_PB_STATIC_METHOD("writePlainText", "(Ljava/lang/String;)V"); CALL_PB_STATIC_VOID_METHOD((jstring)plainText.toJavaString(env)); } void jWriteSelection(bool canSmartCopyOrDelete, const String& plainText, const String& markup) { DEFINE_PB_STATIC_METHOD("writeSelection", "(ZLjava/lang/String;Ljava/lang/String;)V"); CALL_PB_STATIC_VOID_METHOD( bool_to_jbool(canSmartCopyOrDelete), (jstring)plainText.toJavaString(env), (jstring)markup.toJavaString(env)); } void jWriteImage(const Image& image, const String& fileType) { DEFINE_PB_STATIC_METHOD("writeImage", "(Lcom/sun/webkit/graphics/WCImageFrame;Ljava/lang/String;)V"); CALL_PB_STATIC_VOID_METHOD( jobject(*const_cast(image).javaImage()), (jstring)fileType.toJavaString(env)); } void jWriteURL(const String& url, const String& markup) { DEFINE_PB_STATIC_METHOD("writeUrl", "(Ljava/lang/String;Ljava/lang/String;)V"); CALL_PB_STATIC_VOID_METHOD( (jstring)url.toJavaString(env), (jstring)markup.toJavaString(env)); } String jGetHtml() { DEFINE_PB_STATIC_METHOD("getHtml", "()Ljava/lang/String;"); CALL_PB_STATIC_JSTROBJ_METHOD(jstr); return jstr ? String(env, jstr) : String(); } /////////////////// // Helper functions /////////////////// CachedImage* getCachedImage(const Element& element) { // Attempt to pull CachedImage from element RenderObject* renderer = element.renderer(); if (!renderer || !renderer->isImage()) { return 0; } RenderImage* image = static_cast(renderer); if (image->cachedImage() && !image->cachedImage()->errorOccurred()) { return image->cachedImage(); } return 0; } void writeImageToDataObject(RefPtr dataObject, const Element& element, const URL&) { if (!dataObject) { return; } // Shove image data into a DataObject for use as a file CachedImage* cachedImage = getCachedImage(element); if (!cachedImage || !cachedImage->image() || !cachedImage->isLoaded()) { return; } SharedBuffer* imageBuffer = cachedImage->image()->data(); if (!imageBuffer || !imageBuffer->size()) { return; } dataObject->m_fileContent = imageBuffer; // Determine the filename for the file contents of the image. We try to // use the alt tag if one exists, otherwise we fall back on the suggested // filename in the http header, and finally we resort to using the filename // in the URL. //String title = element->getAttribute(altAttr); //if (title.isEmpty()) // title = cachedImage->response().suggestedFilename(); //TODO: do we need it? dataObject->m_fileContentFilename = cachedImage->response().suggestedFilename(); } String imageToMarkup(const String& url, const Element& element) { StringBuilder markup; markup.append("length(); for (unsigned i = 0; i < length; ++i) { RefPtr attr(static_cast(attrs->item(i).get())); if (attr->name() == "src") continue; markup.append(" "); markup.append(attr->name()); markup.append("=\""); String escapedAttr = attr->value(); escapedAttr.replace("\"", """); markup.append(escapedAttr); markup.append("\""); } markup.append("/>"); return markup.toString(); } } // anonymouse namespace /////////////////////////// // WebCore::Pasteboard impl /////////////////////////// Pasteboard::Pasteboard(RefPtr dataObject, bool copyPasteMode = false) : m_dataObject(dataObject), m_copyPasteMode(copyPasteMode) { ASSERT(m_dataObject); } Pasteboard::Pasteboard() : Pasteboard(DataObjectJava::create()) { } std::unique_ptr Pasteboard::create(RefPtr dataObject) { return std::unique_ptr(new Pasteboard(dataObject)); } std::unique_ptr Pasteboard::createPrivate() { return std::unique_ptr(new Pasteboard(DataObjectJava::create())); } std::unique_ptr Pasteboard::createForCopyAndPaste() { // Use single shared data instance for all copy'n'paste pasteboards. static RefPtr data = DataObjectJava::create(); // TODO: setURL, setFiles, setData, setHtml (needs URL) data->setPlainText(jGetPlainText()); return std::unique_ptr(new Pasteboard(data, true)); } #if ENABLE(DRAG_SUPPORT) std::unique_ptr Pasteboard::createForDragAndDrop() { return create(DataObjectJava::create()); } std::unique_ptr Pasteboard::createForDragAndDrop(const DragData& dragData) { return create(dragData.platformData()); } void Pasteboard::setDragImage(DragImage, const IntPoint&) { } #endif void Pasteboard::writeSelection( Range& selectedRange, bool canSmartCopyOrDelete, Frame& frame, ShouldSerializeSelectedTextForDataTransfer shouldSerializeSelectedTextForDataTransfer) { String markup = createMarkup(selectedRange, 0, AnnotateForInterchange, false, ResolveNonLocalURLs); String plainText = shouldSerializeSelectedTextForDataTransfer == IncludeImageAltTextForDataTransfer ? frame.editor().selectedTextForDataTransfer() : frame.editor().selectedText(); #if OS(WINDOWS) replaceNewlinesWithWindowsStyleNewlines(plainText); #endif replaceNBSPWithSpace(plainText); m_dataObject->clear(); m_dataObject->setPlainText(plainText); m_dataObject->setHTML(markup, frame.document()->url()); if (m_copyPasteMode) { jWriteSelection(canSmartCopyOrDelete, plainText, markup); } } void Pasteboard::writePlainText(const String& text, SmartReplaceOption) { String plainText(text); #if OS(WINDOWS) replaceNewlinesWithWindowsStyleNewlines(plainText); #endif if (m_dataObject) { m_dataObject->clear(); m_dataObject->setPlainText(plainText); } if (m_copyPasteMode) { jWritePlainText(plainText); } } void Pasteboard::write(const PasteboardURL& pasteboardURL) { ASSERT(!pasteboardURL.url.isEmpty()); String title(pasteboardURL.title); if (title.isEmpty()) { title = pasteboardURL.url.lastPathComponent(); if (title.isEmpty()) { title = pasteboardURL.url.host(); } } String markup(urlToMarkup(pasteboardURL.url, title)); m_dataObject->clear(); m_dataObject->setURL(pasteboardURL.url, title); m_dataObject->setPlainText(pasteboardURL.url.string()); m_dataObject->setHTML(markup, pasteboardURL.url); if (m_copyPasteMode) { jWriteURL(pasteboardURL.url.string(), markup); } } void Pasteboard::writeImage(Element& node, const URL& url, const String& title) { m_dataObject->setURL(url, title); // Write the bytes of the image to the file format writeImageToDataObject(m_dataObject, node, url); AtomicString imageURL = node.getAttribute(HTMLNames::srcAttr); URL fullURL; if (!imageURL.isEmpty()) { fullURL = node.document().completeURL(stripLeadingAndTrailingHTMLSpaces(imageURL)); if (!fullURL.isEmpty()) { m_dataObject->setHTML( imageToMarkup(fullURL.string(), node), node.document().url()); } } if (m_copyPasteMode) { CachedImage* cachedImage = getCachedImage(node); // CachedImage not exist if (!cachedImage) { return; } Image* image = cachedImage->image(); // Image data not exist if (!image) { return; } // SVGImage are not Bitmap backed, Let the receiving end decode the svg image // based on url and its markup if (image->isSVGImage()) { jWriteURL(url.string(), createMarkup(node)); } else { String lastPathComponent = fullURL.lastPathComponent(); String extension; size_t dotOffset = lastPathComponent.reverseFind('.'); if (dotOffset != notFound) extension = lastPathComponent.substring(dotOffset + 1); else extension = "png"; jWriteImage(*image, extension); } } } void Pasteboard::writeString(const String& type, const String& data) { // DnD only mode if (m_dataObject) { m_dataObject->setData(type, data); } } String Pasteboard::readString(const String& type) { // DnD only mode if (m_dataObject) { return m_dataObject->getData(type); } return String(); } void Pasteboard::clear(const String& type) { if (m_dataObject) { m_dataObject->clearData(type); } if (m_copyPasteMode) { String canonicalMimeType = DataObjectJava::normalizeMIMEType(type); if (canonicalMimeType == DataObjectJava::mimeURIList()) jWriteURL(DataObjectJava::emptyString(), DataObjectJava::emptyString()); else if (canonicalMimeType == DataObjectJava::mimeHTML()) jWriteSelection(false, DataObjectJava::emptyString(), DataObjectJava::emptyString()); else if (canonicalMimeType == DataObjectJava::mimePlainText()) jWritePlainText(DataObjectJava::emptyString()); } } void Pasteboard::clear() { if (m_dataObject) { m_dataObject->clear(); } if (m_copyPasteMode) { jWriteURL(DataObjectJava::emptyString(), DataObjectJava::emptyString()); jWriteSelection(false, DataObjectJava::emptyString(), DataObjectJava::emptyString()); jWritePlainText(DataObjectJava::emptyString()); } } Vector Pasteboard::types() { if (m_dataObject) { return m_dataObject->types(); } return Vector(); } bool Pasteboard::hasData() { return m_dataObject && m_dataObject->hasData(); } Vector Pasteboard::readFilenames() { if (m_dataObject) { Vector fn; m_dataObject->asFilenames(fn); return fn; } return Vector(); } void Pasteboard::read(PasteboardPlainText& text) { if (m_copyPasteMode) { text.text = jGetPlainText(); if (m_dataObject) { m_dataObject->setPlainText(text.text); } return; } if (m_dataObject) { text.text = m_dataObject->asPlainText(); } } bool Pasteboard::canSmartReplace() { // we do not provide smart replace for now return false; } RefPtr Pasteboard::documentFragment( Frame& frame, Range& range, bool allowPlainText, bool &chosePlainText) { chosePlainText = false; String htmlString = m_copyPasteMode ? jGetHtml() : m_dataObject ? m_dataObject->asHTML() : String(); if (!htmlString.isNull()) { if (RefPtr fragment = createFragmentFromMarkup( *frame.document(), htmlString, String(), DisallowScriptingContent)) { return fragment; } } if (!allowPlainText) { return nullptr; } String plainTextString = m_copyPasteMode ? jGetPlainText() : m_dataObject ? m_dataObject->asPlainText() : String(); if (!plainTextString.isNull()) { chosePlainText = true; if (RefPtr fragment = createFragmentFromText( range, plainTextString)) { return fragment; } } return nullptr; } void Pasteboard::writePasteboard(const Pasteboard& sourcePasteboard) { if (m_dataObject) { m_dataObject = sourcePasteboard.dataObject()->copy(); } if (m_copyPasteMode) { RefPtr data = sourcePasteboard.dataObject(); if (data->containsURL()) jWriteURL(data->asURL(), data->asHTML()); if (data->containsHTML()) jWriteSelection(false, data->asPlainText(), data->asHTML()); if (data->containsPlainText()) jWritePlainText(data->asPlainText()); } } void Pasteboard::read(PasteboardWebContentReader&) { } void Pasteboard::write(const PasteboardImage&) { } void Pasteboard::write(const PasteboardWebContent&) { } void Pasteboard::writeMarkup(const String&) { } void Pasteboard::writeTrustworthyWebURLsPboardType(const PasteboardURL&) { notImplemented(); } } // namespace WebCore