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