1 /*
   2  * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24 */
  25 
  26 #include "config.h"
  27 
  28 #include "PasteboardUtilitiesJava.h"
  29 #include "CachedImage.h"
  30 #include "DocumentFragment.h"
  31 #include "Image.h"
  32 #include "Editor.h"
  33 #include "Frame.h"
  34 #include "FrameView.h"
  35 #include "markup.h"
  36 #include "Pasteboard.h"
  37 #include "RenderImage.h"
  38 #include "Range.h"
  39 #include "NotImplemented.h"
  40 #include "DataObjectJava.h"
  41 #include "DragData.h"
  42 #include <wtf/java/JavaEnv.h>
  43 #include <wtf/java/JavaRef.h>
  44 #include <wtf/text/WTFString.h>
  45 #include <wtf/text/StringBuilder.h>
  46 #include "NamedNodeMap.h"
  47 #include "Attr.h"
  48 #include "HTMLNames.h"
  49 #include "HTMLParserIdioms.h"
  50 
  51 #include "wtf/Ref.h"
  52 
  53 namespace WebCore {
  54 
  55 ///////////////////
  56 // WCPasteboard JNI
  57 ///////////////////
  58 
  59 namespace {
  60 
  61 #define PB_CLASS jPBClass()
  62 
  63 #define DEFINE_PB_CLASS(_name) \
  64     JNIEnv* env = WebCore_GetJavaEnv(); \
  65     static JGClass cls(env->FindClass(_name)); \
  66     ASSERT(cls);
  67 
  68 #define DEFINE_PB_STATIC_METHOD(_name, _params) \
  69     JNIEnv* env = WebCore_GetJavaEnv(); \
  70     static jmethodID mid = env->GetStaticMethodID(PB_CLASS, _name, _params); \
  71     ASSERT(mid);
  72 
  73 #define CALL_PB_STATIC_VOID_METHOD(...) \
  74     env->CallStaticVoidMethod(PB_CLASS, mid, __VA_ARGS__); \
  75     CheckAndClearException(env);
  76 
  77 #define CALL_PB_STATIC_JSTROBJ_METHOD(_jstrobj) \
  78     JLString _jstrobj(static_cast<jstring>(env->CallStaticObjectMethod(PB_CLASS, mid))); \
  79     CheckAndClearException(env);
  80 
  81 jclass jPBClass()
  82 {
  83     DEFINE_PB_CLASS("com/sun/webkit/WCPasteboard");
  84     return cls;
  85 }
  86 
  87 String jGetPlainText()
  88 {
  89     DEFINE_PB_STATIC_METHOD("getPlainText", "()Ljava/lang/String;");
  90     CALL_PB_STATIC_JSTROBJ_METHOD(jstr);
  91 
  92     return jstr ? String(env, jstr) : String();
  93 }
  94 
  95 void jWritePlainText(const String& plainText)
  96 {
  97     DEFINE_PB_STATIC_METHOD("writePlainText", "(Ljava/lang/String;)V");
  98     CALL_PB_STATIC_VOID_METHOD((jstring)plainText.toJavaString(env));
  99 }
 100 
 101 void jWriteSelection(bool canSmartCopyOrDelete, const String& plainText, const String& markup)
 102 {
 103     DEFINE_PB_STATIC_METHOD("writeSelection", "(ZLjava/lang/String;Ljava/lang/String;)V");
 104     CALL_PB_STATIC_VOID_METHOD(
 105         bool_to_jbool(canSmartCopyOrDelete),
 106         (jstring)plainText.toJavaString(env),
 107         (jstring)markup.toJavaString(env));
 108 }
 109 
 110 void jWriteImage(const Image& image, const String& fileType)
 111 {
 112     DEFINE_PB_STATIC_METHOD("writeImage", "(Lcom/sun/webkit/graphics/WCImageFrame;Ljava/lang/String;)V");
 113     CALL_PB_STATIC_VOID_METHOD(
 114         jobject(*const_cast<Image&>(image).javaImage()),
 115         (jstring)fileType.toJavaString(env));
 116 }
 117 
 118 void jWriteURL(const String& url, const String& markup)
 119 {
 120     DEFINE_PB_STATIC_METHOD("writeUrl", "(Ljava/lang/String;Ljava/lang/String;)V");
 121     CALL_PB_STATIC_VOID_METHOD(
 122         (jstring)url.toJavaString(env),
 123         (jstring)markup.toJavaString(env));
 124 }
 125 
 126 String jGetHtml()
 127 {
 128     DEFINE_PB_STATIC_METHOD("getHtml", "()Ljava/lang/String;");
 129     CALL_PB_STATIC_JSTROBJ_METHOD(jstr);
 130 
 131     return jstr ? String(env, jstr) : String();
 132 }
 133 
 134 ///////////////////
 135 // Helper functions
 136 ///////////////////
 137 
 138 CachedImage* getCachedImage(const Element& element)
 139 {
 140     // Attempt to pull CachedImage from element
 141     RenderObject* renderer = element.renderer();
 142     if (!renderer || !renderer->isImage()) {
 143         return 0;
 144     }
 145     RenderImage* image = static_cast<RenderImage*>(renderer);
 146     if (image->cachedImage() && !image->cachedImage()->errorOccurred()) {
 147         return image->cachedImage();
 148     }
 149     return 0;
 150 }
 151 
 152 void writeImageToDataObject(RefPtr<DataObjectJava> dataObject, const Element& element, const URL&)
 153 {
 154     if (!dataObject) {
 155         return;
 156     }
 157     // Shove image data into a DataObject for use as a file
 158     CachedImage* cachedImage = getCachedImage(element);
 159     if (!cachedImage || !cachedImage->image() || !cachedImage->isLoaded()) {
 160         return;
 161     }
 162     SharedBuffer* imageBuffer = cachedImage->image()->data();
 163     if (!imageBuffer || !imageBuffer->size()) {
 164         return;
 165     }
 166     dataObject->m_fileContent = imageBuffer;
 167 
 168     // Determine the filename for the file contents of the image.  We try to
 169     // use the alt tag if one exists, otherwise we fall back on the suggested
 170     // filename in the http header, and finally we resort to using the filename
 171     // in the URL.
 172     //String title = element->getAttribute(altAttr);
 173     //if (title.isEmpty())
 174     //  title = cachedImage->response().suggestedFilename();
 175 
 176     //TODO: do we need it?
 177     dataObject->m_fileContentFilename = cachedImage->response().suggestedFilename();
 178 }
 179 
 180 String imageToMarkup(const String& url, const Element& element)
 181 {
 182     StringBuilder markup;
 183     markup.append("<img src=\"");
 184     markup.append(url);
 185     markup.append("\"");
 186     // Copy over attributes.  If we are dragging an image, we expect things like
 187     // the id to be copied as well.
 188     NamedNodeMap* attrs = &element.attributes();
 189     unsigned length = attrs->length();
 190     for (unsigned i = 0; i < length; ++i) {
 191         RefPtr<Attr> attr(static_cast<Attr*>(attrs->item(i).get()));
 192         if (attr->name() == "src")
 193             continue;
 194         markup.append(" ");
 195         markup.append(attr->name());
 196         markup.append("=\"");
 197         String escapedAttr = attr->value();
 198         escapedAttr.replace("\"", "&quot;");
 199         markup.append(escapedAttr);
 200         markup.append("\"");
 201     }
 202 
 203     markup.append("/>");
 204     return markup.toString();
 205 }
 206 
 207 } // anonymouse namespace
 208 
 209 ///////////////////////////
 210 // WebCore::Pasteboard impl
 211 ///////////////////////////
 212 
 213 Pasteboard::Pasteboard(RefPtr<DataObjectJava> dataObject, bool copyPasteMode = false)
 214   : m_dataObject(dataObject),
 215     m_copyPasteMode(copyPasteMode)
 216 {
 217     ASSERT(m_dataObject);
 218 }
 219 
 220 Pasteboard::Pasteboard() : Pasteboard(DataObjectJava::create())
 221 {
 222 }
 223 
 224 std::unique_ptr<Pasteboard> Pasteboard::create(RefPtr<DataObjectJava> dataObject)
 225 {
 226     return std::unique_ptr<Pasteboard>(new Pasteboard(dataObject));
 227 }
 228 
 229 std::unique_ptr<Pasteboard> Pasteboard::createPrivate()
 230 {
 231     return std::unique_ptr<Pasteboard>(new Pasteboard(DataObjectJava::create()));
 232 }
 233 
 234 std::unique_ptr<Pasteboard> Pasteboard::createForCopyAndPaste()
 235 {
 236     // Use single shared data instance for all copy'n'paste pasteboards.
 237     static RefPtr<DataObjectJava> data = DataObjectJava::create();
 238     // TODO: setURL, setFiles, setData, setHtml (needs URL)
 239     data->setPlainText(jGetPlainText());
 240     return std::unique_ptr<Pasteboard>(new Pasteboard(data, true));
 241 }
 242 
 243 #if ENABLE(DRAG_SUPPORT)
 244 std::unique_ptr<Pasteboard> Pasteboard::createForDragAndDrop()
 245 {
 246     return create(DataObjectJava::create());
 247 }
 248 
 249 std::unique_ptr<Pasteboard> Pasteboard::createForDragAndDrop(const DragData& dragData)
 250 {
 251     return create(dragData.platformData());
 252 }
 253 
 254 void Pasteboard::setDragImage(DragImage, const IntPoint&)
 255 {
 256 }
 257 #endif
 258 
 259 void Pasteboard::writeSelection(
 260     Range& selectedRange,
 261     bool canSmartCopyOrDelete,
 262     Frame& frame,
 263     ShouldSerializeSelectedTextForDataTransfer shouldSerializeSelectedTextForDataTransfer)
 264 {
 265     String markup = createMarkup(selectedRange, 0, AnnotateForInterchange, false, ResolveNonLocalURLs);
 266     String plainText = shouldSerializeSelectedTextForDataTransfer == IncludeImageAltTextForDataTransfer
 267         ? frame.editor().selectedTextForDataTransfer()
 268         : frame.editor().selectedText();
 269 
 270 #if OS(WINDOWS)
 271     replaceNewlinesWithWindowsStyleNewlines(plainText);
 272 #endif
 273     replaceNBSPWithSpace(plainText);
 274 
 275     m_dataObject->clear();
 276     m_dataObject->setPlainText(plainText);
 277     m_dataObject->setHTML(markup, frame.document()->url());
 278 
 279     if (m_copyPasteMode) {
 280         jWriteSelection(canSmartCopyOrDelete, plainText, markup);
 281     }
 282 }
 283 
 284 void Pasteboard::writePlainText(const String& text, SmartReplaceOption)
 285 {
 286     String plainText(text);
 287 #if OS(WINDOWS)
 288     replaceNewlinesWithWindowsStyleNewlines(plainText);
 289 #endif
 290 
 291     if (m_dataObject) {
 292         m_dataObject->clear();
 293         m_dataObject->setPlainText(plainText);
 294     }
 295     if (m_copyPasteMode) {
 296         jWritePlainText(plainText);
 297     }
 298 }
 299 
 300 void Pasteboard::write(const PasteboardURL& pasteboardURL)
 301 {
 302     ASSERT(!pasteboardURL.url.isEmpty());
 303 
 304     String title(pasteboardURL.title);
 305     if (title.isEmpty()) {
 306         title = pasteboardURL.url.lastPathComponent();
 307         if (title.isEmpty()) {
 308             title = pasteboardURL.url.host();
 309         }
 310     }
 311     String markup(urlToMarkup(pasteboardURL.url, title));
 312 
 313     m_dataObject->clear();
 314     m_dataObject->setURL(pasteboardURL.url, title);
 315     m_dataObject->setPlainText(pasteboardURL.url.string());
 316     m_dataObject->setHTML(markup, pasteboardURL.url);
 317 
 318     if (m_copyPasteMode) {
 319         jWriteURL(pasteboardURL.url.string(), markup);
 320     }
 321 }
 322 
 323 void Pasteboard::writeImage(Element& node, const URL& url, const String& title)
 324 {
 325     m_dataObject->setURL(url, title);
 326 
 327     // Write the bytes of the image to the file format
 328     writeImageToDataObject(m_dataObject,    node, url);
 329     
 330     AtomicString imageURL = node.getAttribute(HTMLNames::srcAttr);
 331     URL fullURL;
 332     if (!imageURL.isEmpty()) {
 333         fullURL = node.document().completeURL(stripLeadingAndTrailingHTMLSpaces(imageURL));
 334         if (!fullURL.isEmpty()) {
 335             m_dataObject->setHTML(
 336                 imageToMarkup(fullURL.string(), node),
 337                 node.document().url());
 338         }
 339     }
 340     if (m_copyPasteMode) {
 341         CachedImage* cachedImage = getCachedImage(node);
 342         // CachedImage not exist
 343         if (!cachedImage) {
 344             return;
 345         }
 346 
 347         Image* image = cachedImage->image();
 348         // Image data not exist
 349         if (!image) {
 350             return;
 351         }
 352 
 353         // SVGImage are not Bitmap backed, Let the receiving end decode the svg image
 354         // based on url and its markup
 355         if (image->isSVGImage()) {
 356             jWriteURL(url.string(), createMarkup(node));
 357         }
 358         else {
 359             String lastPathComponent = fullURL.lastPathComponent();
 360             String extension;
 361             size_t dotOffset = lastPathComponent.reverseFind('.');
 362             if (dotOffset != notFound)
 363                 extension = lastPathComponent.substring(dotOffset + 1);            
 364             else
 365                 extension = "png";
 366 
 367             jWriteImage(*image, extension);
 368         }
 369     }
 370 }
 371 
 372 void Pasteboard::writeString(const String& type, const String& data)
 373 {
 374     // DnD only mode
 375     if (m_dataObject) {
 376         m_dataObject->setData(type, data);
 377     }
 378 }
 379 
 380 String Pasteboard::readString(const String& type)
 381 {
 382     // DnD only mode
 383     if (m_dataObject) {
 384         return m_dataObject->getData(type);
 385     }
 386     return String();
 387 }
 388 
 389 void Pasteboard::clear(const String& type)
 390 {
 391     if (m_dataObject) {
 392         m_dataObject->clearData(type);
 393     }
 394     if (m_copyPasteMode) {
 395         String canonicalMimeType = DataObjectJava::normalizeMIMEType(type);
 396         if (canonicalMimeType == DataObjectJava::mimeURIList())
 397             jWriteURL(DataObjectJava::emptyString(), DataObjectJava::emptyString());
 398         else if (canonicalMimeType == DataObjectJava::mimeHTML())
 399             jWriteSelection(false, DataObjectJava::emptyString(), DataObjectJava::emptyString());
 400         else if (canonicalMimeType == DataObjectJava::mimePlainText())
 401             jWritePlainText(DataObjectJava::emptyString());
 402     }
 403 }
 404 
 405 void Pasteboard::clear()
 406 {
 407     if (m_dataObject) {
 408         m_dataObject->clear();
 409     }
 410     if (m_copyPasteMode) {
 411         jWriteURL(DataObjectJava::emptyString(), DataObjectJava::emptyString());
 412         jWriteSelection(false, DataObjectJava::emptyString(), DataObjectJava::emptyString());
 413         jWritePlainText(DataObjectJava::emptyString());
 414     }
 415 }
 416 
 417 Vector<String> Pasteboard::types()
 418 {
 419     if (m_dataObject) {
 420         return m_dataObject->types();
 421     }
 422     return Vector<String>();
 423 }
 424 
 425 bool Pasteboard::hasData()
 426 {
 427     return m_dataObject && m_dataObject->hasData();
 428 }
 429 
 430 Vector<String> Pasteboard::readFilenames()
 431 {
 432     if (m_dataObject) {
 433         Vector<String> fn;
 434         m_dataObject->asFilenames(fn);
 435         return fn;
 436     }
 437     return Vector<String>();
 438 }
 439 
 440 void Pasteboard::read(PasteboardPlainText& text)
 441 {
 442     if (m_copyPasteMode) {
 443         text.text = jGetPlainText();
 444         if (m_dataObject) {
 445             m_dataObject->setPlainText(text.text);
 446         }
 447         return;
 448     }
 449     if (m_dataObject) {
 450         text.text = m_dataObject->asPlainText();
 451     }
 452 }
 453 
 454 bool Pasteboard::canSmartReplace()
 455 {
 456     // we do not provide smart replace for now
 457     return false;
 458 }
 459 
 460 RefPtr<DocumentFragment> Pasteboard::documentFragment(
 461     Frame& frame,
 462     Range& range,
 463     bool allowPlainText,
 464     bool &chosePlainText)
 465 {
 466     chosePlainText = false;
 467 
 468     String htmlString = m_copyPasteMode ?
 469         jGetHtml() :
 470         m_dataObject ? m_dataObject->asHTML() : String();
 471 
 472     if (!htmlString.isNull()) {
 473         if (RefPtr<DocumentFragment> fragment = createFragmentFromMarkup(
 474                 *frame.document(),
 475                 htmlString,
 476                 String(),
 477                 DisallowScriptingContent))
 478         {
 479             return fragment;
 480         }
 481     }
 482 
 483     if (!allowPlainText) {
 484         return nullptr;
 485     }
 486 
 487     String plainTextString = m_copyPasteMode ?
 488         jGetPlainText() :
 489         m_dataObject ? m_dataObject->asPlainText() : String();
 490 
 491     if (!plainTextString.isNull()) {
 492         chosePlainText = true;
 493         if (RefPtr<DocumentFragment> fragment = createFragmentFromText(
 494                 range,
 495                 plainTextString))
 496         {
 497             return fragment;
 498         }
 499     }
 500     return nullptr;
 501 }
 502 
 503 void Pasteboard::writePasteboard(const Pasteboard& sourcePasteboard)
 504 {
 505     if (m_dataObject) {
 506         m_dataObject = sourcePasteboard.dataObject()->copy();
 507     }
 508     if (m_copyPasteMode) {
 509         RefPtr<DataObjectJava> data = sourcePasteboard.dataObject();
 510         if (data->containsURL()) jWriteURL(data->asURL(), data->asHTML());
 511         if (data->containsHTML()) jWriteSelection(false, data->asPlainText(), data->asHTML());
 512         if (data->containsPlainText()) jWritePlainText(data->asPlainText());
 513     }
 514 }
 515 
 516 void Pasteboard::read(PasteboardWebContentReader&)
 517 {
 518 }
 519 
 520 void Pasteboard::write(const PasteboardImage&)
 521 {
 522 }
 523 
 524 void Pasteboard::write(const PasteboardWebContent&)
 525 {
 526 }
 527 
 528 void Pasteboard::writeMarkup(const String&)
 529 {
 530 }
 531 
 532 void Pasteboard::writeTrustworthyWebURLsPboardType(const PasteboardURL&)
 533 {
 534     notImplemented();
 535 }
 536 
 537 } // namespace WebCore