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)
 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 Pasteboard::Pasteboard(RefPtr<DataObjectJava> dataObject, bool copyPasteMode = false)
 212   : m_dataObject(dataObject),
 213     m_copyPasteMode(copyPasteMode)
 214 {
 215     ASSERT(m_dataObject);
 216 }
 217 
 218 Pasteboard::Pasteboard() : Pasteboard(DataObjectJava::create())
 219 {
 220 }
 221 
 222 std::unique_ptr<Pasteboard> Pasteboard::create(RefPtr<DataObjectJava> dataObject)
 223 {
 224     return std::unique_ptr<Pasteboard>(new Pasteboard(dataObject));
 225 }
 226 
 227 std::unique_ptr<Pasteboard> Pasteboard::createPrivate()
 228 {
 229     return std::unique_ptr<Pasteboard>(new Pasteboard(DataObjectJava::create()));
 230 }
 231 
 232 std::unique_ptr<Pasteboard> Pasteboard::createForCopyAndPaste()
 233 {
 234     // Use single shared data instance for all copy'n'paste pasteboards.
 235     static RefPtr<DataObjectJava> data = DataObjectJava::create();
 236     // TODO: setURL, setFiles, setData, setHtml (needs URL)
 237     data->setPlainText(jGetPlainText());
 238     return std::unique_ptr<Pasteboard>(new Pasteboard(data, true));
 239 }
 240 
 241 #if ENABLE(DRAG_SUPPORT)
 242 std::unique_ptr<Pasteboard> Pasteboard::createForDragAndDrop()
 243 {
 244     return create(DataObjectJava::create());
 245 }
 246 
 247 std::unique_ptr<Pasteboard> Pasteboard::createForDragAndDrop(const DragData& dragData)
 248 {
 249     return create(dragData.platformData());
 250 }
 251 
 252 void Pasteboard::setDragImage(DragImage, const IntPoint&)
 253 {
 254 }
 255 #endif
 256 
 257 void Pasteboard::writeSelection(
 258     Range& selectedRange,
 259     bool canSmartCopyOrDelete,
 260     Frame& frame,
 261     ShouldSerializeSelectedTextForDataTransfer shouldSerializeSelectedTextForDataTransfer)
 262 {
 263     String markup = createMarkup(selectedRange, 0, AnnotateForInterchange, false, ResolveNonLocalURLs);
 264     String plainText = shouldSerializeSelectedTextForDataTransfer == IncludeImageAltTextForDataTransfer
 265         ? frame.editor().selectedTextForDataTransfer()
 266         : frame.editor().selectedText();
 267 
 268 #if OS(WINDOWS)
 269     replaceNewlinesWithWindowsStyleNewlines(plainText);
 270 #endif
 271     replaceNBSPWithSpace(plainText);
 272 
 273     m_dataObject->clear();
 274     m_dataObject->setPlainText(plainText);
 275     m_dataObject->setHTML(markup, frame.document()->url());
 276 
 277     if (m_copyPasteMode) {
 278         jWriteSelection(canSmartCopyOrDelete, plainText, markup);
 279     }
 280 }
 281 
 282 void Pasteboard::writePlainText(const String& text, SmartReplaceOption)
 283 {
 284     String plainText(text);
 285 #if OS(WINDOWS)
 286     replaceNewlinesWithWindowsStyleNewlines(plainText);
 287 #endif
 288 
 289     if (m_dataObject) {
 290         m_dataObject->clear();
 291         m_dataObject->setPlainText(plainText);
 292     }
 293     if (m_copyPasteMode) {
 294         jWritePlainText(plainText);
 295     }
 296 }
 297 
 298 void Pasteboard::write(const PasteboardURL& pasteboardURL)
 299 {
 300     ASSERT(!pasteboardURL.url.isEmpty());
 301 
 302     String title(pasteboardURL.title);
 303     if (title.isEmpty()) {
 304         title = pasteboardURL.url.lastPathComponent();
 305         if (title.isEmpty()) {
 306             title = pasteboardURL.url.host();
 307         }
 308     }
 309     String markup(urlToMarkup(pasteboardURL.url, title));
 310 
 311     m_dataObject->clear();
 312     m_dataObject->setURL(pasteboardURL.url, title);
 313     m_dataObject->setPlainText(pasteboardURL.url.string());
 314     m_dataObject->setHTML(markup, pasteboardURL.url);
 315 
 316     if (m_copyPasteMode) {
 317         jWriteURL(pasteboardURL.url.string(), markup);
 318     }
 319 }
 320 
 321 void Pasteboard::writeImage(Element& node, const URL& url, const String& title)
 322 {
 323     m_dataObject->setURL(url, title);
 324 
 325     // Write the bytes of the image to the file format
 326     writeImageToDataObject(m_dataObject,    node, url);
 327 
 328     AtomicString imageURL = node.getAttribute(HTMLNames::srcAttr);
 329     if (!imageURL.isEmpty()) {
 330         String fullURL = node.document().completeURL(stripLeadingAndTrailingHTMLSpaces(imageURL));
 331         if (!fullURL.isEmpty()) {
 332             m_dataObject->setHTML(
 333                 imageToMarkup(fullURL, node),
 334                 node.document().url());
 335         }
 336     }
 337     if (m_copyPasteMode) {
 338         CachedImage* cachedImage = getCachedImage(node);
 339         // CachedImage not exist
 340         if (!cachedImage) {
 341             return;
 342         }
 343 
 344         Image* image = cachedImage->image();
 345         // Image data not exist
 346         if (!image) {
 347             return;
 348         }
 349 
 350         // SVGImage are not Bitmap backed, Let the receiving end decode the svg image
 351         // based on url and its markup
 352         if (image->isSVGImage()) {
 353             jWriteURL(url.string(), createMarkup(node));
 354         }
 355         else {
 356             jWriteImage(*image);
 357         }
 358     }
 359 }
 360 
 361 void Pasteboard::writeString(const String& type, const String& data)
 362 {
 363     // DnD only mode
 364     if (m_dataObject) {
 365         m_dataObject->setData(type, data);
 366     }
 367 }
 368 
 369 String Pasteboard::readString(const String& type)
 370 {
 371     // DnD only mode
 372     if (m_dataObject) {
 373         return m_dataObject->getData(type);
 374     }
 375     return String();
 376 }
 377 
 378 void Pasteboard::clear(const String& type)
 379 {
 380     if (m_dataObject) {
 381         m_dataObject->clearData(type);
 382     }
 383     if (m_copyPasteMode) {
 384         String canonicalMimeType = DataObjectJava::normalizeMIMEType(type);
 385         if (canonicalMimeType == DataObjectJava::mimeURIList())
 386             jWriteURL(DataObjectJava::emptyString(), DataObjectJava::emptyString());
 387         else if (canonicalMimeType == DataObjectJava::mimeHTML())
 388             jWriteSelection(false, DataObjectJava::emptyString(), DataObjectJava::emptyString());
 389         else if (canonicalMimeType == DataObjectJava::mimePlainText())
 390             jWritePlainText(DataObjectJava::emptyString());
 391     }
 392 }
 393 
 394 void Pasteboard::clear()
 395 {
 396     if (m_dataObject) {
 397         m_dataObject->clear();
 398     }
 399     if (m_copyPasteMode) {
 400         jWriteURL(DataObjectJava::emptyString(), DataObjectJava::emptyString());
 401         jWriteSelection(false, DataObjectJava::emptyString(), DataObjectJava::emptyString());
 402         jWritePlainText(DataObjectJava::emptyString());
 403     }
 404 }
 405 
 406 Vector<String> Pasteboard::types()
 407 {
 408     if (m_dataObject) {
 409         return m_dataObject->types();
 410     }
 411     return Vector<String>();
 412 }
 413 
 414 bool Pasteboard::hasData()
 415 {
 416     return m_dataObject && m_dataObject->hasData();
 417 }
 418 
 419 Vector<String> Pasteboard::readFilenames()
 420 {
 421     if (m_dataObject) {
 422         Vector<String> fn;
 423         m_dataObject->asFilenames(fn);
 424         return fn;
 425     }
 426     return Vector<String>();
 427 }
 428 
 429 void Pasteboard::read(PasteboardPlainText& text)
 430 {
 431     if (m_copyPasteMode) {
 432         text.text = jGetPlainText();
 433         if (m_dataObject) {
 434             m_dataObject->setPlainText(text.text);
 435         }
 436         return;
 437     }
 438     if (m_dataObject) {
 439         text.text = m_dataObject->asPlainText();
 440     }
 441 }
 442 
 443 bool Pasteboard::canSmartReplace()
 444 {
 445     // we do not provide smart replace for now
 446     return false;
 447 }
 448 
 449 RefPtr<DocumentFragment> Pasteboard::documentFragment(
 450     Frame& frame,
 451     Range& range,
 452     bool allowPlainText,
 453     bool &chosePlainText)
 454 {
 455     chosePlainText = false;
 456 
 457     String htmlString = m_copyPasteMode ?
 458         jGetHtml() :
 459         m_dataObject ? m_dataObject->asHTML() : String();
 460 
 461     if (!htmlString.isNull()) {
 462         if (RefPtr<DocumentFragment> fragment = createFragmentFromMarkup(
 463                 *frame.document(),
 464                 htmlString,
 465                 String(),
 466                 DisallowScriptingContent))
 467         {
 468             return fragment;
 469         }
 470     }
 471 
 472     if (!allowPlainText) {
 473         return nullptr;
 474     }
 475 
 476     String plainTextString = m_copyPasteMode ?
 477         jGetPlainText() :
 478         m_dataObject ? m_dataObject->asPlainText() : String();
 479 
 480     if (!plainTextString.isNull()) {
 481         chosePlainText = true;
 482         if (RefPtr<DocumentFragment> fragment = createFragmentFromText(
 483                 range,
 484                 plainTextString))
 485         {
 486             return fragment;
 487         }
 488     }
 489     return nullptr;
 490 }
 491 
 492 void Pasteboard::writePasteboard(const Pasteboard& sourcePasteboard)
 493 {
 494     if (m_dataObject) {
 495         m_dataObject = sourcePasteboard.dataObject()->copy();
 496     }
 497     if (m_copyPasteMode) {
 498         RefPtr<DataObjectJava> data = sourcePasteboard.dataObject();
 499         if (data->containsURL()) jWriteURL(data->asURL(), data->asHTML());
 500         if (data->containsHTML()) jWriteSelection(false, data->asPlainText(), data->asHTML());
 501         if (data->containsPlainText()) jWritePlainText(data->asPlainText());
 502     }
 503 }
 504 
 505 void Pasteboard::read(PasteboardWebContentReader&)
 506 {
 507 }
 508 
 509 void Pasteboard::write(const PasteboardImage&)
 510 {
 511 }
 512 
 513 void Pasteboard::write(const PasteboardWebContent&)
 514 {
 515 }
 516 
 517 void Pasteboard::writeMarkup(const String&)
 518 {
 519 }
 520 
 521 void Pasteboard::writeTrustworthyWebURLsPboardType(const PasteboardURL&)
 522 {
 523     notImplemented();
 524 }
 525 
 526 } // namespace WebCore