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("\"", """); 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