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 210 return adoptPtr(new Pasteboard(data, true)); 211 } 212 213 #if ENABLE(DRAG_SUPPORT) 214 PassOwnPtr<Pasteboard> Pasteboard::createForDragAndDrop() 215 { 216 return create(DataObjectJava::create()); 217 } 218 219 PassOwnPtr<Pasteboard> Pasteboard::createForDragAndDrop(const DragData& dragData) 220 { 221 return create(dragData.platformData()); 222 } 223 224 void Pasteboard::setDragImage(DragImageRef image, const IntPoint& hotSpot) 225 { 226 } 227 #endif 228 229 void Pasteboard::writeSelection( 230 Range& selectedRange, 231 bool canSmartCopyOrDelete, 232 Frame& frame, 233 ShouldSerializeSelectedTextForClipboard shouldSerializeSelectedTextForClipboard) 234 { 235 String markup = createMarkup(selectedRange, 0, AnnotateForInterchange, false, ResolveNonLocalURLs); 236 String plainText = shouldSerializeSelectedTextForClipboard == IncludeImageAltTextForClipboard 237 ? frame.editor().selectedTextForClipboard() 238 : frame.editor().selectedText(); 239 240 #if OS(WINDOWS) 241 replaceNewlinesWithWindowsStyleNewlines(plainText); 242 #endif 243 replaceNBSPWithSpace(plainText); 244 245 m_dataObject->clear(); 246 m_dataObject->setPlainText(plainText); 247 m_dataObject->setHTML(markup, frame.document()->url()); 248 249 if (m_copyPasteMode) { 250 jWriteSelection(canSmartCopyOrDelete, plainText, markup); 251 } 252 } 253 254 void Pasteboard::writePlainText(const String& text, SmartReplaceOption) 255 { 256 String plainText(text); 257 #if OS(WINDOWS) 258 replaceNewlinesWithWindowsStyleNewlines(plainText); 259 #endif 260 261 if (m_dataObject) { 262 m_dataObject->clear(); 263 m_dataObject->setPlainText(plainText); 264 } 265 if (m_copyPasteMode) { 266 jWritePlainText(plainText); 267 } 268 } 269 270 void Pasteboard::write(const PasteboardURL& pasteboardURL) 271 { 272 ASSERT(!pasteboardURL.url.isEmpty()); 273 274 String title(pasteboardURL.title); 275 if (title.isEmpty()) { 276 title = pasteboardURL.url.lastPathComponent(); 277 if (title.isEmpty()) { 278 title = pasteboardURL.url.host(); 279 } 280 } 281 String markup(urlToMarkup(pasteboardURL.url, title)); 282 283 m_dataObject->clear(); 284 m_dataObject->setURL(pasteboardURL.url, title); 285 m_dataObject->setPlainText(pasteboardURL.url.string()); 286 m_dataObject->setHTML(markup, pasteboardURL.url); 287 288 if (m_copyPasteMode) { 289 jWriteURL(pasteboardURL.url.string(), markup); 290 } 291 } 292 293 void Pasteboard::writeImage(Element& node, const URL& url, const String& title) 294 { 295 m_dataObject->setURL(url, title); 296 297 // Write the bytes of the image to the file format 298 writeImageToDataObject(m_dataObject, node, url); 299 300 AtomicString imageURL = node.getAttribute(HTMLNames::srcAttr); 301 if (!imageURL.isEmpty()) { 302 String fullURL = node.document().completeURL(stripLeadingAndTrailingHTMLSpaces(imageURL)); 303 if (!fullURL.isEmpty()) { 304 m_dataObject->setHTML( 305 imageToMarkup(fullURL, node), 306 node.document().url()); 307 } 308 } 309 if (m_copyPasteMode) { 310 Image* image = getCachedImage(node)->image(); 311 jWriteImage(*image); 312 } 313 } 314 315 bool Pasteboard::writeString(const String& type, const String& data) 316 { 317 // DnD only mode 318 if (m_dataObject) { 319 return m_dataObject->setData(type, data); 320 } 321 return false; 322 } 323 324 String Pasteboard::readString(const String& type) 325 { 326 // DnD only mode 327 if (m_dataObject) { 328 return m_dataObject->getData(type); 329 } 330 return String(); 331 } 332 333 void Pasteboard::clear(const String& type) 334 { 335 if (m_dataObject) { 336 m_dataObject->clearData(type); 337 } 338 if (m_copyPasteMode) { 339 String canonicalMimeType = DataObjectJava::normalizeMIMEType(type); 340 if (canonicalMimeType == DataObjectJava::mimeURIList()) 341 jWriteURL(DataObjectJava::emptyString(), DataObjectJava::emptyString()); 342 else if (canonicalMimeType == DataObjectJava::mimeHTML()) 343 jWriteSelection(false, DataObjectJava::emptyString(), DataObjectJava::emptyString()); 344 else if (canonicalMimeType == DataObjectJava::mimePlainText()) 345 jWritePlainText(DataObjectJava::emptyString()); 346 } 347 } 348 349 void Pasteboard::clear() 350 { 351 if (m_dataObject) { 352 m_dataObject->clear(); 353 } 354 if (m_copyPasteMode) { 355 jWriteURL(DataObjectJava::emptyString(), DataObjectJava::emptyString()); 356 jWriteSelection(false, DataObjectJava::emptyString(), DataObjectJava::emptyString()); 357 jWritePlainText(DataObjectJava::emptyString()); 358 } 359 } 360 361 Vector<String> Pasteboard::types() 362 { 363 if (m_dataObject) { 364 return m_dataObject->types(); 365 } 366 return Vector<String>(); 367 } 368 369 bool Pasteboard::hasData() 370 { 371 return m_dataObject && m_dataObject->hasData(); 372 } 373 374 Vector<String> Pasteboard::readFilenames() 375 { 376 if (m_dataObject) { 377 Vector<String> fn; 378 m_dataObject->asFilenames(fn); 379 return fn; 380 } 381 return Vector<String>(); 382 } 383 384 void Pasteboard::read(PasteboardPlainText& text) 385 { 386 if (m_copyPasteMode) { 387 text.text = jGetPlainText(); 388 if (m_dataObject) { 389 m_dataObject->setPlainText(text.text); 390 } 391 return; 392 } 393 if (m_dataObject) { 394 text.text = m_dataObject->asPlainText(); 395 } 396 } 397 398 bool Pasteboard::canSmartReplace() 399 { 400 // we do not provide smart replace for now 401 return false; 402 } 403 404 PassRefPtr<DocumentFragment> Pasteboard::documentFragment( 405 Frame& frame, 406 Range& range, 407 bool allowPlainText, 408 bool &chosePlainText) 409 { 410 chosePlainText = false; 411 412 String htmlString = m_copyPasteMode ? 413 jGetHtml() : 414 m_dataObject ? m_dataObject->asHTML() : String(); 415 416 if (!htmlString.isNull()) { 417 if (PassRefPtr<DocumentFragment> fragment = createFragmentFromMarkup( 418 *frame.document(), 419 htmlString, 420 String(), 421 DisallowScriptingContent)) 422 { 423 return fragment; 424 } 425 } 426 427 if (!allowPlainText) { 428 return 0; 429 } 430 431 String plainTextString = m_copyPasteMode ? 432 jGetPlainText() : 433 m_dataObject ? m_dataObject->asPlainText() : String(); 434 435 if (!plainTextString.isNull()) { 436 chosePlainText = true; 437 if (PassRefPtr<DocumentFragment> fragment = createFragmentFromText( 438 range, 439 plainTextString)) 440 { 441 return fragment; 442 } 443 } 444 return 0; 445 } 446 447 void Pasteboard::writePasteboard(const Pasteboard& sourcePasteboard) 448 { 449 if (m_dataObject) { 450 m_dataObject = sourcePasteboard.dataObject()->copy(); 451 } 452 if (m_copyPasteMode) { 453 RefPtr<DataObjectJava> data = sourcePasteboard.dataObject(); 454 if (data->containsURL()) jWriteURL(data->asURL(), data->asHTML()); 455 if (data->containsHTML()) jWriteSelection(false, data->asPlainText(), data->asHTML()); 456 if (data->containsPlainText()) jWritePlainText(data->asPlainText()); 457 } 458 } 459 460 } // namespace WebCore