1 /* 2 * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. 3 */ 4 #include "config.h" 5 6 #include "PasteboardUtilitiesJava.h" 7 #include "CachedImage.h" 8 #include "DocumentFragment.h" 9 #include "Image.h" 10 #include "Editor.h" 11 #include "Frame.h" 12 #include "FrameView.h" 13 #include "markup.h" 14 #include "Pasteboard.h" 15 #include "RenderImage.h" 16 #include "Range.h" 17 #include "NotImplemented.h" 18 #include "DataObjectJava.h" 19 #include "DragData.h" 20 #include "JavaEnv.h" 21 #include "JavaRef.h" 22 #include "WTFString.h" 23 #include "StringBuilder.h" 24 #include "NamedNodeMap.h" 25 #include "Attr.h" 26 #include "HTMLNames.h" 27 #include "HTMLParserIdioms.h" 28 #include "com_sun_webkit_WCPasteboard.h" 29 30 namespace WebCore { 31 32 /////////////////// 33 // WCPasteboard JNI 34 /////////////////// 35 36 namespace { 37 38 #define PB_CLASS jPBClass() 39 40 #define DEFINE_PB_CLASS(_name) \ 41 JNIEnv* env = WebCore_GetJavaEnv(); \ 42 static JGClass cls(env->FindClass(_name)); \ 43 ASSERT(cls); 44 45 #define DEFINE_PB_STATIC_METHOD(_name, _params) \ 46 JNIEnv* env = WebCore_GetJavaEnv(); \ 47 static jmethodID mid = env->GetStaticMethodID(PB_CLASS, _name, _params); \ 48 ASSERT(mid); 49 50 #define CALL_PB_STATIC_VOID_METHOD(...) \ 51 env->CallStaticVoidMethod(PB_CLASS, mid, __VA_ARGS__); \ 52 CheckAndClearException(env); 53 54 #define CALL_PB_STATIC_JSTROBJ_METHOD(_jstrobj) \ 55 JLString _jstrobj(static_cast<jstring>(env->CallStaticObjectMethod(PB_CLASS, mid))); \ 56 CheckAndClearException(env); 57 58 jclass jPBClass() 59 { 60 DEFINE_PB_CLASS("com/sun/webkit/WCPasteboard"); 61 return cls; 62 } 63 64 String jGetPlainText() 65 { 66 DEFINE_PB_STATIC_METHOD("getPlainText", "()Ljava/lang/String;"); 67 CALL_PB_STATIC_JSTROBJ_METHOD(jstr); 68 69 return jstr ? String(env, jstr) : String(); 70 } 71 72 void jWritePlainText(const String& plainText) 73 { 74 DEFINE_PB_STATIC_METHOD("writePlainText", "(Ljava/lang/String;)V"); 75 CALL_PB_STATIC_VOID_METHOD((jstring)plainText.toJavaString(env)); 76 } 77 78 void jWriteSelection(bool canSmartCopyOrDelete, const String& plainText, const String& markup) 79 { 80 DEFINE_PB_STATIC_METHOD("writeSelection", "(ZLjava/lang/String;Ljava/lang/String;)V"); 81 CALL_PB_STATIC_VOID_METHOD( 82 bool_to_jbool(canSmartCopyOrDelete), 83 (jstring)plainText.toJavaString(env), 84 (jstring)markup.toJavaString(env)); 85 } 86 87 void jWriteImage(const Image& image) 88 { 89 DEFINE_PB_STATIC_METHOD("writeImage", "(Lcom/sun/webkit/graphics/WCImageFrame;)V"); 90 CALL_PB_STATIC_VOID_METHOD(jobject(*const_cast<Image&>(image).javaImage())); 91 } 92 93 void jWriteURL(const String& url, const String& markup) 94 { 95 DEFINE_PB_STATIC_METHOD("writeUrl", "(Ljava/lang/String;Ljava/lang/String;)V"); 96 CALL_PB_STATIC_VOID_METHOD( 97 (jstring)url.toJavaString(env), 98 (jstring)markup.toJavaString(env)); 99 } 100 101 String jGetHtml() 102 { 103 DEFINE_PB_STATIC_METHOD("getHtml", "()Ljava/lang/String;"); 104 CALL_PB_STATIC_JSTROBJ_METHOD(jstr); 105 106 return jstr ? String(env, jstr) : String(); 107 } 108 109 /////////////////// 110 // Helper functions 111 /////////////////// 112 113 CachedImage* getCachedImage(const Element& element) 114 { 115 // Attempt to pull CachedImage from element 116 RenderObject* renderer = element.renderer(); 117 if (!renderer || !renderer->isImage()) { 118 return 0; 119 } 120 RenderImage* image = static_cast<RenderImage*>(renderer); 121 if (image->cachedImage() && !image->cachedImage()->errorOccurred()) { 122 return image->cachedImage(); 123 } 124 return 0; 125 } 126 127 void writeImageToDataObject(PassRefPtr<DataObjectJava> dataObject, const Element& element, const URL& url) 128 { 129 if (!dataObject) { 130 return; 131 } 132 // Shove image data into a DataObject for use as a file 133 CachedImage* cachedImage = getCachedImage(element); 134 if (!cachedImage || !cachedImage->image() || !cachedImage->isLoaded()) { 135 return; 136 } 137 SharedBuffer* imageBuffer = cachedImage->image()->data(); 138 if (!imageBuffer || !imageBuffer->size()) { 139 return; 140 } 141 dataObject->m_fileContent = imageBuffer; 142 143 // Determine the filename for the file contents of the image. We try to 144 // use the alt tag if one exists, otherwise we fall back on the suggested 145 // filename in the http header, and finally we resort to using the filename 146 // in the URL. 147 //String title = element->getAttribute(altAttr); 148 //if (title.isEmpty()) 149 // title = cachedImage->response().suggestedFilename(); 150 151 //TODO: do we need it? 152 dataObject->m_fileContentFilename = cachedImage->response().suggestedFilename(); 153 } 154 155 String imageToMarkup(const String& url, const Element& element) 156 { 157 StringBuilder markup; 158 markup.append("<img src=\""); 159 markup.append(url); 160 markup.append("\""); 161 // Copy over attributes. If we are dragging an image, we expect things like 162 // the id to be copied as well. 163 NamedNodeMap* attrs = element.attributes(); 164 unsigned length = attrs->length(); 165 for (unsigned i = 0; i < length; ++i) { 166 RefPtr<Attr> attr(static_cast<Attr*>(attrs->item(i).get())); 167 if (attr->name() == "src") 168 continue; 169 markup.append(" "); 170 markup.append(attr->name()); 171 markup.append("=\""); 172 String escapedAttr = attr->value(); 173 escapedAttr.replace("\"", """); 174 markup.append(escapedAttr); 175 markup.append("\""); 176 } 177 178 markup.append("/>"); 179 return markup.toString(); 180 } 181 182 } // anonymouse namespace 183 184 /////////////////////////// 185 // WebCore::Pasteboard impl 186 /////////////////////////// 187 188 Pasteboard::Pasteboard(PassRefPtr<DataObjectJava> dataObject, bool copyPasteMode = false) : 189 m_dataObject(dataObject), 190 m_copyPasteMode(copyPasteMode) 191 { 192 ASSERT(m_dataObject); 193 } 194 195 PassOwnPtr<Pasteboard> Pasteboard::create(PassRefPtr<DataObjectJava> dataObject) 196 { 197 return adoptPtr(new Pasteboard(dataObject)); 198 } 199 200 PassOwnPtr<Pasteboard> Pasteboard::createPrivate() 201 { 202 return adoptPtr(new Pasteboard(DataObjectJava::create())); 203 } 204 205 PassOwnPtr<Pasteboard> Pasteboard::createForCopyAndPaste() 206 { 207 // Use single shared data instance for all copy'n'paste pasteboards. 208 static RefPtr<DataObjectJava> data = DataObjectJava::create(); 209 //Todo : setURL, setFiles, setData, setHtml (needs URL) 210 data->setPlainText(jGetPlainText()); 211 return adoptPtr(new Pasteboard(data, true)); 212 } 213 214 #if ENABLE(DRAG_SUPPORT) 215 PassOwnPtr<Pasteboard> Pasteboard::createForDragAndDrop() 216 { 217 return create(DataObjectJava::create()); 218 } 219 220 PassOwnPtr<Pasteboard> Pasteboard::createForDragAndDrop(const DragData& dragData) 221 { 222 return create(dragData.platformData()); 223 } 224 225 void Pasteboard::setDragImage(DragImageRef image, const IntPoint& hotSpot) 226 { 227 } 228 #endif 229 230 void Pasteboard::writeSelection( 231 Range& selectedRange, 232 bool canSmartCopyOrDelete, 233 Frame& frame, 234 ShouldSerializeSelectedTextForClipboard shouldSerializeSelectedTextForClipboard) 235 { 236 String markup = createMarkup(selectedRange, 0, AnnotateForInterchange, false, ResolveNonLocalURLs); 237 String plainText = shouldSerializeSelectedTextForClipboard == IncludeImageAltTextForClipboard 238 ? frame.editor().selectedTextForClipboard() 239 : frame.editor().selectedText(); 240 241 #if OS(WINDOWS) 242 replaceNewlinesWithWindowsStyleNewlines(plainText); 243 #endif 244 replaceNBSPWithSpace(plainText); 245 246 m_dataObject->clear(); 247 m_dataObject->setPlainText(plainText); 248 m_dataObject->setHTML(markup, frame.document()->url()); 249 250 if (m_copyPasteMode) { 251 jWriteSelection(canSmartCopyOrDelete, plainText, markup); 252 } 253 } 254 255 void Pasteboard::writePlainText(const String& text, SmartReplaceOption) 256 { 257 String plainText(text); 258 #if OS(WINDOWS) 259 replaceNewlinesWithWindowsStyleNewlines(plainText); 260 #endif 261 262 if (m_dataObject) { 263 m_dataObject->clear(); 264 m_dataObject->setPlainText(plainText); 265 } 266 if (m_copyPasteMode) { 267 jWritePlainText(plainText); 268 } 269 } 270 271 void Pasteboard::write(const PasteboardURL& pasteboardURL) 272 { 273 ASSERT(!pasteboardURL.url.isEmpty()); 274 275 String title(pasteboardURL.title); 276 if (title.isEmpty()) { 277 title = pasteboardURL.url.lastPathComponent(); 278 if (title.isEmpty()) { 279 title = pasteboardURL.url.host(); 280 } 281 } 282 String markup(urlToMarkup(pasteboardURL.url, title)); 283 284 m_dataObject->clear(); 285 m_dataObject->setURL(pasteboardURL.url, title); 286 m_dataObject->setPlainText(pasteboardURL.url.string()); 287 m_dataObject->setHTML(markup, pasteboardURL.url); 288 289 if (m_copyPasteMode) { 290 jWriteURL(pasteboardURL.url.string(), markup); 291 } 292 } 293 294 void Pasteboard::writeImage(Element& node, const URL& url, const String& title) 295 { 296 m_dataObject->setURL(url, title); 297 298 // Write the bytes of the image to the file format 299 writeImageToDataObject(m_dataObject, node, url); 300 301 AtomicString imageURL = node.getAttribute(HTMLNames::srcAttr); 302 if (!imageURL.isEmpty()) { 303 String fullURL = node.document().completeURL(stripLeadingAndTrailingHTMLSpaces(imageURL)); 304 if (!fullURL.isEmpty()) { 305 m_dataObject->setHTML( 306 imageToMarkup(fullURL, node), 307 node.document().url()); 308 } 309 } 310 if (m_copyPasteMode) { 311 Image* image = getCachedImage(node)->image(); 312 jWriteImage(*image); 313 } 314 } 315 316 bool Pasteboard::writeString(const String& type, const String& data) 317 { 318 // DnD only mode 319 if (m_dataObject) { 320 return m_dataObject->setData(type, data); 321 } 322 return false; 323 } 324 325 String Pasteboard::readString(const String& type) 326 { 327 // DnD only mode 328 if (m_dataObject) { 329 return m_dataObject->getData(type); 330 } 331 return String(); 332 } 333 334 void Pasteboard::clear(const String& type) 335 { 336 if (m_dataObject) { 337 m_dataObject->clearData(type); 338 } 339 if (m_copyPasteMode) { 340 String canonicalMimeType = DataObjectJava::normalizeMIMEType(type); 341 if (canonicalMimeType == DataObjectJava::mimeURIList()) 342 jWriteURL(DataObjectJava::emptyString(), DataObjectJava::emptyString()); 343 else if (canonicalMimeType == DataObjectJava::mimeHTML()) 344 jWriteSelection(false, DataObjectJava::emptyString(), DataObjectJava::emptyString()); 345 else if (canonicalMimeType == DataObjectJava::mimePlainText()) 346 jWritePlainText(DataObjectJava::emptyString()); 347 } 348 } 349 350 void Pasteboard::clear() 351 { 352 if (m_dataObject) { 353 m_dataObject->clear(); 354 } 355 if (m_copyPasteMode) { 356 jWriteURL(DataObjectJava::emptyString(), DataObjectJava::emptyString()); 357 jWriteSelection(false, DataObjectJava::emptyString(), DataObjectJava::emptyString()); 358 jWritePlainText(DataObjectJava::emptyString()); 359 } 360 } 361 362 Vector<String> Pasteboard::types() 363 { 364 if (m_dataObject) { 365 return m_dataObject->types(); 366 } 367 return Vector<String>(); 368 } 369 370 bool Pasteboard::hasData() 371 { 372 return m_dataObject && m_dataObject->hasData(); 373 } 374 375 Vector<String> Pasteboard::readFilenames() 376 { 377 if (m_dataObject) { 378 Vector<String> fn; 379 m_dataObject->asFilenames(fn); 380 return fn; 381 } 382 return Vector<String>(); 383 } 384 385 void Pasteboard::read(PasteboardPlainText& text) 386 { 387 if (m_copyPasteMode) { 388 text.text = jGetPlainText(); 389 if (m_dataObject) { 390 m_dataObject->setPlainText(text.text); 391 } 392 return; 393 } 394 if (m_dataObject) { 395 text.text = m_dataObject->asPlainText(); 396 } 397 } 398 399 bool Pasteboard::canSmartReplace() 400 { 401 // we do not provide smart replace for now 402 return false; 403 } 404 405 PassRefPtr<DocumentFragment> Pasteboard::documentFragment( 406 Frame& frame, 407 Range& range, 408 bool allowPlainText, 409 bool &chosePlainText) 410 { 411 chosePlainText = false; 412 413 String htmlString = m_copyPasteMode ? 414 jGetHtml() : 415 m_dataObject ? m_dataObject->asHTML() : String(); 416 417 if (!htmlString.isNull()) { 418 if (PassRefPtr<DocumentFragment> fragment = createFragmentFromMarkup( 419 *frame.document(), 420 htmlString, 421 String(), 422 DisallowScriptingContent)) 423 { 424 return fragment; 425 } 426 } 427 428 if (!allowPlainText) { 429 return 0; 430 } 431 432 String plainTextString = m_copyPasteMode ? 433 jGetPlainText() : 434 m_dataObject ? m_dataObject->asPlainText() : String(); 435 436 if (!plainTextString.isNull()) { 437 chosePlainText = true; 438 if (PassRefPtr<DocumentFragment> fragment = createFragmentFromText( 439 range, 440 plainTextString)) 441 { 442 return fragment; 443 } 444 } 445 return 0; 446 } 447 448 void Pasteboard::writePasteboard(const Pasteboard& sourcePasteboard) 449 { 450 if (m_dataObject) { 451 m_dataObject = sourcePasteboard.dataObject()->copy(); 452 } 453 if (m_copyPasteMode) { 454 RefPtr<DataObjectJava> data = sourcePasteboard.dataObject(); 455 if (data->containsURL()) jWriteURL(data->asURL(), data->asHTML()); 456 if (data->containsHTML()) jWriteSelection(false, data->asPlainText(), data->asHTML()); 457 if (data->containsPlainText()) jWritePlainText(data->asPlainText()); 458 } 459 } 460 461 } // namespace WebCore