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