1 /*
   2  * Copyright (c) 2011, 2018, 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)
 111 {
 112     DEFINE_PB_STATIC_METHOD("writeImage", "(Lcom/sun/webkit/graphics/WCImageFrame;)V");
 113     CALL_PB_STATIC_VOID_METHOD(jobject(*const_cast<Image&>(image).javaImage()));
 114 }
 115 
 116 void jWriteURL(const String& url, const String& markup)
 117 {
 118     DEFINE_PB_STATIC_METHOD("writeUrl", "(Ljava/lang/String;Ljava/lang/String;)V");
 119     CALL_PB_STATIC_VOID_METHOD(
 120         (jstring)url.toJavaString(env),
 121         (jstring)markup.toJavaString(env));
 122 }
 123 
 124 String jGetHtml()
 125 {
 126     DEFINE_PB_STATIC_METHOD("getHtml", "()Ljava/lang/String;");
 127     CALL_PB_STATIC_JSTROBJ_METHOD(jstr);
 128 
 129     return jstr ? String(env, jstr) : String();
 130 }
 131 
 132 ///////////////////
 133 // Helper functions
 134 ///////////////////
 135 
 136 CachedImage* getCachedImage(const Element& element)
 137 {
 138     // Attempt to pull CachedImage from element
 139     RenderObject* renderer = element.renderer();
 140     if (!renderer || !renderer->isImage()) {
 141         return 0;
 142     }
 143     RenderImage* image = static_cast<RenderImage*>(renderer);
 144     if (image->cachedImage() && !image->cachedImage()->errorOccurred()) {
 145         return image->cachedImage();
 146     }
 147     return 0;
 148 }
 149 
 150 void writeImageToDataObject(RefPtr<DataObjectJava> dataObject, const Element& element, const URL&)
 151 {
 152     if (!dataObject) {
 153         return;
 154     }
 155     // Shove image data into a DataObject for use as a file
 156     CachedImage* cachedImage = getCachedImage(element);
 157     if (!cachedImage || !cachedImage->image() || !cachedImage->isLoaded()) {
 158         return;
 159     }
 160     SharedBuffer* imageBuffer = cachedImage->image()->data();
 161     if (!imageBuffer || !imageBuffer->size()) {
 162         return;
 163     }
 164     dataObject->m_fileContent = imageBuffer;
 165 
 166     // Determine the filename for the file contents of the image.  We try to
 167     // use the alt tag if one exists, otherwise we fall back on the suggested
 168     // filename in the http header, and finally we resort to using the filename
 169     // in the URL.
 170     //String title = element->getAttribute(altAttr);
 171     //if (title.isEmpty())
 172     //  title = cachedImage->response().suggestedFilename();
 173 
 174     //TODO: do we need it?
 175     dataObject->m_fileContentFilename = cachedImage->response().suggestedFilename();
 176 }
 177 
 178 String imageToMarkup(const String& url, const Element& element)
 179 {
 180     StringBuilder markup;
 181     markup.append("<img src=\"");
 182     markup.append(url);
 183     markup.append("\"");
 184     // Copy over attributes.  If we are dragging an image, we expect things like
 185     // the id to be copied as well.
 186     NamedNodeMap* attrs = &element.attributes();
 187     unsigned length = attrs->length();
 188     for (unsigned i = 0; i < length; ++i) {
 189         RefPtr<Attr> attr(static_cast<Attr*>(attrs->item(i).get()));
 190         if (attr->name() == "src")
 191             continue;
 192         markup.append(" ");
 193         markup.append(attr->name());
 194         markup.append("=\"");
 195         String escapedAttr = attr->value();
 196         escapedAttr.replace("\"", "&quot;");
 197         markup.append(escapedAttr);
 198         markup.append("\"");
 199     }
 200 
 201     markup.append("/>");
 202     return markup.toString();
 203 }
 204 
 205 } // anonymouse namespace
 206 
 207 ///////////////////////////
 208 // WebCore::Pasteboard impl
 209 ///////////////////////////
 210 
 211 struct PasteboardFileCounter final : PasteboardFileReader {
 212     void readFilename(const String&) final { ++count; }
 213     void readBuffer(const String&, const String&, Ref<SharedBuffer>&&) final { ++count; }
 214 
 215     unsigned count { 0 };
 216 };
 217 
 218 Pasteboard::Pasteboard(RefPtr<DataObjectJava> dataObject, bool copyPasteMode = false)
 219   : m_dataObject(dataObject),
 220     m_copyPasteMode(copyPasteMode)
 221 {
 222     ASSERT(m_dataObject);
 223 }
 224 
 225 Pasteboard::Pasteboard() : Pasteboard(DataObjectJava::create())
 226 {
 227 }
 228 
 229 std::unique_ptr<Pasteboard> Pasteboard::create(RefPtr<DataObjectJava> dataObject)
 230 {
 231     return std::unique_ptr<Pasteboard>(new Pasteboard(dataObject));
 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     if (!imageURL.isEmpty()) {
 332         String fullURL = node.document().completeURL(stripLeadingAndTrailingHTMLSpaces(imageURL));
 333         if (!fullURL.isEmpty()) {
 334             m_dataObject->setHTML(
 335                 imageToMarkup(fullURL, node),
 336                 node.document().url());
 337         }
 338     }
 339     if (m_copyPasteMode) {
 340         CachedImage* cachedImage = getCachedImage(node);
 341         // CachedImage not exist
 342         if (!cachedImage) {
 343             return;
 344         }
 345 
 346         Image* image = cachedImage->image();
 347         // Image data not exist
 348         if (!image) {
 349             return;
 350         }
 351 
 352         // SVGImage are not Bitmap backed, Let the receiving end decode the svg image
 353         // based on url and its markup
 354         if (image->isSVGImage()) {
 355             jWriteURL(url.string(), createMarkup(node));
 356         }
 357         else {
 358             jWriteImage(*image);
 359         }
 360     }
 361 }
 362 
 363 void Pasteboard::writeString(const String& type, const String& data)
 364 {
 365     // DnD only mode
 366     if (m_dataObject) {
 367         m_dataObject->setData(type, data);
 368     }
 369 }
 370 
 371 String Pasteboard::readString(const String& type)
 372 {
 373     // DnD only mode
 374     if (m_dataObject) {
 375         return m_dataObject->getData(type);
 376     }
 377     return String();
 378 }
 379 
 380 void Pasteboard::clear(const String& type)
 381 {
 382     if (m_dataObject) {
 383         m_dataObject->clearData(type);
 384     }
 385     if (m_copyPasteMode) {
 386         String canonicalMimeType = DataObjectJava::normalizeMIMEType(type);
 387         if (canonicalMimeType == DataObjectJava::mimeURIList())
 388             jWriteURL(DataObjectJava::emptyString(), DataObjectJava::emptyString());
 389         else if (canonicalMimeType == DataObjectJava::mimeHTML())
 390             jWriteSelection(false, DataObjectJava::emptyString(), DataObjectJava::emptyString());
 391         else if (canonicalMimeType == DataObjectJava::mimePlainText())
 392             jWritePlainText(DataObjectJava::emptyString());
 393     }
 394 }
 395 
 396 void Pasteboard::clear()
 397 {
 398     if (m_dataObject) {
 399         m_dataObject->clear();
 400     }
 401     if (m_copyPasteMode) {
 402         jWriteURL(DataObjectJava::emptyString(), DataObjectJava::emptyString());
 403         jWriteSelection(false, DataObjectJava::emptyString(), DataObjectJava::emptyString());
 404         jWritePlainText(DataObjectJava::emptyString());
 405     }
 406 }
 407 
 408 Vector<String> Pasteboard::typesSafeForBindings(const String&)
 409 {
 410     notImplemented();
 411     return { };
 412 }
 413 
 414 String Pasteboard::readOrigin()
 415 {
 416     notImplemented();
 417     return { };
 418 }
 419 
 420 Vector<String> Pasteboard::typesForLegacyUnsafeBindings()
 421 {
 422     if (m_dataObject) {
 423         return m_dataObject->types();
 424     }
 425     return Vector<String>();
 426 }
 427 
 428 bool Pasteboard::hasData()
 429 {
 430     return m_dataObject && m_dataObject->hasData();
 431 }
 432 
 433 void Pasteboard::read(PasteboardFileReader& reader)
 434 {
 435     if (m_dataObject) {
 436         for (const auto& filename : m_dataObject->asFilenames())
 437             reader.readFilename(filename);
 438     }
 439 }
 440 
 441 String Pasteboard::readStringInCustomData(const String&)
 442 {
 443     notImplemented();
 444     return { };
 445 }
 446 
 447 bool Pasteboard::containsFiles()
 448 {
 449     // FIXME: This implementation can be slightly more efficient by avoiding calls to DragQueryFileW.
 450     PasteboardFileCounter reader;
 451     read(reader);
 452     return reader.count;
 453 }
 454 
 455 void Pasteboard::read(PasteboardPlainText& text)
 456 {
 457     if (m_copyPasteMode) {
 458         text.text = jGetPlainText();
 459         if (m_dataObject) {
 460             m_dataObject->setPlainText(text.text);
 461         }
 462         return;
 463     }
 464     if (m_dataObject) {
 465         text.text = m_dataObject->asPlainText();
 466     }
 467 }
 468 
 469 bool Pasteboard::canSmartReplace()
 470 {
 471     // we do not provide smart replace for now
 472     return false;
 473 }
 474 
 475 RefPtr<DocumentFragment> Pasteboard::documentFragment(
 476     Frame& frame,
 477     Range& range,
 478     bool allowPlainText,
 479     bool &chosePlainText)
 480 {
 481     chosePlainText = false;
 482 
 483     String htmlString = m_copyPasteMode ?
 484         jGetHtml() :
 485         m_dataObject ? m_dataObject->asHTML() : String();
 486 
 487     if (!htmlString.isNull()) {
 488         if (RefPtr<DocumentFragment> fragment = createFragmentFromMarkup(
 489                 *frame.document(),
 490                 htmlString,
 491                 String(),
 492                 DisallowScriptingContent))
 493         {
 494             return fragment;
 495         }
 496     }
 497 
 498     if (!allowPlainText) {
 499         return nullptr;
 500     }
 501 
 502     String plainTextString = m_copyPasteMode ?
 503         jGetPlainText() :
 504         m_dataObject ? m_dataObject->asPlainText() : String();
 505 
 506     if (!plainTextString.isNull()) {
 507         chosePlainText = true;
 508         if (RefPtr<DocumentFragment> fragment = createFragmentFromText(
 509                 range,
 510                 plainTextString))
 511         {
 512             return fragment;
 513         }
 514     }
 515     return nullptr;
 516 }
 517 
 518 void Pasteboard::read(PasteboardWebContentReader&)
 519 {
 520 }
 521 
 522 void Pasteboard::write(const PasteboardImage&)
 523 {
 524 }
 525 
 526 void Pasteboard::write(const PasteboardWebContent&)
 527 {
 528 }
 529 
 530 void Pasteboard::writeMarkup(const String&)
 531 {
 532 }
 533 
 534 void Pasteboard::writeCustomData(const PasteboardCustomData&)
 535 {
 536 }
 537 
 538 void Pasteboard::writeTrustworthyWebURLsPboardType(const PasteboardURL&)
 539 {
 540     notImplemented();
 541 }
 542 
 543 } // namespace WebCore