1 /* 2 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 #include "core/clipboard/DataTransfer.h" 28 29 #include "core/HTMLNames.h" 30 #include "core/clipboard/DataObject.h" 31 #include "core/clipboard/DataTransferItem.h" 32 #include "core/clipboard/DataTransferItemList.h" 33 #include "core/editing/markup.h" 34 #include "core/fetch/ImageResource.h" 35 #include "core/fileapi/FileList.h" 36 #include "core/frame/LocalFrame.h" 37 #include "core/html/HTMLImageElement.h" 38 #include "core/rendering/RenderImage.h" 39 #include "core/rendering/RenderLayer.h" 40 #include "core/rendering/RenderObject.h" 41 #include "platform/DragImage.h" 42 #include "platform/MIMETypeRegistry.h" 43 #include "platform/clipboard/ClipboardMimeTypes.h" 44 #include "platform/clipboard/ClipboardUtilities.h" 45 46 namespace blink { 47 48 static DragOperation convertEffectAllowedToDragOperation(const String& op) 49 { 50 // Values specified in http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#dom-datatransfer-effectallowed 51 if (op == "uninitialized") 52 return DragOperationEvery; 53 if (op == "none") 54 return DragOperationNone; 55 if (op == "copy") 56 return DragOperationCopy; 57 if (op == "link") 58 return DragOperationLink; 59 if (op == "move") 60 return (DragOperation)(DragOperationGeneric | DragOperationMove); 61 if (op == "copyLink") 62 return (DragOperation)(DragOperationCopy | DragOperationLink); 63 if (op == "copyMove") 64 return (DragOperation)(DragOperationCopy | DragOperationGeneric | DragOperationMove); 65 if (op == "linkMove") 66 return (DragOperation)(DragOperationLink | DragOperationGeneric | DragOperationMove); 67 if (op == "all") 68 return DragOperationEvery; 69 return DragOperationPrivate; // really a marker for "no conversion" 70 } 71 72 static String convertDragOperationToEffectAllowed(DragOperation op) 73 { 74 bool moveSet = !!((DragOperationGeneric | DragOperationMove) & op); 75 76 if ((moveSet && (op & DragOperationCopy) && (op & DragOperationLink)) 77 || (op == DragOperationEvery)) 78 return "all"; 79 if (moveSet && (op & DragOperationCopy)) 80 return "copyMove"; 81 if (moveSet && (op & DragOperationLink)) 82 return "linkMove"; 83 if ((op & DragOperationCopy) && (op & DragOperationLink)) 84 return "copyLink"; 85 if (moveSet) 86 return "move"; 87 if (op & DragOperationCopy) 88 return "copy"; 89 if (op & DragOperationLink) 90 return "link"; 91 return "none"; 92 } 93 94 // We provide the IE clipboard types (URL and Text), and the clipboard types specified in the WHATWG Web Applications 1.0 draft 95 // see http://www.whatwg.org/specs/web-apps/current-work/ Section 6.3.5.3 96 static String normalizeType(const String& type, bool* convertToURL = 0) 97 { 98 String cleanType = type.stripWhiteSpace().lower(); 99 if (cleanType == mimeTypeText || cleanType.startsWith(mimeTypeTextPlainEtc)) 100 return mimeTypeTextPlain; 101 if (cleanType == mimeTypeURL) { 102 if (convertToURL) 103 *convertToURL = true; 104 return mimeTypeTextURIList; 105 } 106 return cleanType; 107 } 108 109 PassRefPtrWillBeRawPtr<DataTransfer> DataTransfer::create(DataTransferType type, DataTransferAccessPolicy policy, PassRefPtrWillBeRawPtr<DataObject> dataObject) 110 { 111 return adoptRefWillBeNoop(new DataTransfer(type, policy, dataObject)); 112 } 113 114 DataTransfer::~DataTransfer() 115 { 116 } 117 118 void DataTransfer::setDropEffect(const String &effect) 119 { 120 if (!isForDragAndDrop()) 121 return; 122 123 // The attribute must ignore any attempts to set it to a value other than none, copy, link, and move. 124 if (effect != "none" && effect != "copy" && effect != "link" && effect != "move") 125 return; 126 127 // FIXME: The spec actually allows this in all circumstances, even though there's no point in 128 // setting the drop effect when this condition is not true. 129 if (canReadTypes()) 130 m_dropEffect = effect; 131 } 132 133 void DataTransfer::setEffectAllowed(const String &effect) 134 { 135 if (!isForDragAndDrop()) 136 return; 137 138 if (convertEffectAllowedToDragOperation(effect) == DragOperationPrivate) { 139 // This means that there was no conversion, and the effectAllowed that 140 // we are passed isn't a valid effectAllowed, so we should ignore it, 141 // and not set m_effectAllowed. 142 143 // The attribute must ignore any attempts to set it to a value other than 144 // none, copy, copyLink, copyMove, link, linkMove, move, all, and uninitialized. 145 return; 146 } 147 148 149 if (canWriteData()) 150 m_effectAllowed = effect; 151 } 152 153 void DataTransfer::clearData(const String& type) 154 { 155 if (!canWriteData()) 156 return; 157 158 if (type.isNull()) 159 m_dataObject->clearAll(); 160 else 161 m_dataObject->clearData(normalizeType(type)); 162 } 163 164 String DataTransfer::getData(const String& type) const 165 { 166 if (!canReadData()) 167 return String(); 168 169 bool convertToURL = false; 170 String data = m_dataObject->getData(normalizeType(type, &convertToURL)); 171 if (!convertToURL) 172 return data; 173 return convertURIListToURL(data); 174 } 175 176 void DataTransfer::setData(const String& type, const String& data) 177 { 178 if (!canWriteData()) 179 return; 180 181 m_dataObject->setData(normalizeType(type), data); 182 } 183 184 // extensions beyond IE's API 185 Vector<String> DataTransfer::types() const 186 { 187 Vector<String> types; 188 if (!canReadTypes()) 189 return types; 190 191 ListHashSet<String> typesSet = m_dataObject->types(); 192 types.appendRange(typesSet.begin(), typesSet.end()); 193 return types; 194 } 195 196 PassRefPtrWillBeRawPtr<FileList> DataTransfer::files() const 197 { 198 RefPtrWillBeRawPtr<FileList> files = FileList::create(); 199 if (!canReadData()) 200 return files.release(); 201 202 for (size_t i = 0; i < m_dataObject->length(); ++i) { 203 if (m_dataObject->item(i)->kind() == DataObjectItem::FileKind) { 204 RefPtrWillBeRawPtr<Blob> blob = m_dataObject->item(i)->getAsFile(); 205 if (blob && blob->isFile()) 206 files->append(toFile(blob.get())); 207 } 208 } 209 210 return files.release(); 211 } 212 213 void DataTransfer::setDragImage(Element* image, int x, int y, ExceptionState& exceptionState) 214 { 215 if (!isForDragAndDrop()) 216 return; 217 218 if (!image) { 219 exceptionState.throwTypeError("setDragImage: Invalid first argument"); 220 return; 221 } 222 IntPoint location(x, y); 223 if (isHTMLImageElement(*image) && !image->inDocument()) 224 setDragImageResource(toHTMLImageElement(*image).cachedImage(), location); 225 else 226 setDragImageElement(image, location); 227 } 228 229 void DataTransfer::clearDragImage() 230 { 231 if (!canSetDragImage()) 232 return; 233 234 m_dragImage = 0; 235 m_dragLoc = IntPoint(); 236 m_dragImageElement = nullptr; 237 } 238 239 void DataTransfer::setDragImageResource(ImageResource* img, const IntPoint& loc) 240 { 241 setDragImage(img, 0, loc); 242 } 243 244 void DataTransfer::setDragImageElement(Node* node, const IntPoint& loc) 245 { 246 setDragImage(0, node, loc); 247 } 248 249 PassOwnPtr<DragImage> DataTransfer::createDragImage(IntPoint& loc, LocalFrame* frame) const 250 { 251 if (m_dragImageElement) { 252 loc = m_dragLoc; 253 254 return frame->nodeImage(*m_dragImageElement); 255 } 256 if (m_dragImage) { 257 loc = m_dragLoc; 258 return DragImage::create(m_dragImage->image()); 259 } 260 return nullptr; 261 } 262 263 static ImageResource* getImageResource(Element* element) 264 { 265 // Attempt to pull ImageResource from element 266 ASSERT(element); 267 RenderObject* renderer = element->renderer(); 268 if (!renderer || !renderer->isImage()) 269 return 0; 270 271 RenderImage* image = toRenderImage(renderer); 272 if (image->cachedImage() && !image->cachedImage()->errorOccurred()) 273 return image->cachedImage(); 274 275 return 0; 276 } 277 278 static void writeImageToDataObject(DataObject* dataObject, Element* element, const KURL& url) 279 { 280 // Shove image data into a DataObject for use as a file 281 ImageResource* cachedImage = getImageResource(element); 282 if (!cachedImage || !cachedImage->imageForRenderer(element->renderer()) || !cachedImage->isLoaded()) 283 return; 284 285 SharedBuffer* imageBuffer = cachedImage->imageForRenderer(element->renderer())->data(); 286 if (!imageBuffer || !imageBuffer->size()) 287 return; 288 289 String imageExtension = cachedImage->image()->filenameExtension(); 290 ASSERT(!imageExtension.isEmpty()); 291 292 // Determine the filename for the file contents of the image. 293 String filename = cachedImage->response().suggestedFilename(); 294 if (filename.isEmpty()) 295 filename = url.lastPathComponent(); 296 297 String fileExtension; 298 if (filename.isEmpty()) { 299 filename = element->getAttribute(HTMLNames::altAttr); 300 } else { 301 // Strip any existing extension. Assume that alt text is usually not a filename. 302 int extensionIndex = filename.reverseFind('.'); 303 if (extensionIndex != -1) { 304 fileExtension = filename.substring(extensionIndex + 1); 305 filename.truncate(extensionIndex); 306 } 307 } 308 309 if (!fileExtension.isEmpty() && fileExtension != imageExtension) { 310 String imageMimeType = MIMETypeRegistry::getMIMETypeForExtension(imageExtension); 311 ASSERT(imageMimeType.startsWith("image/")); 312 // Use the file extension only if it has imageMimeType: it's untrustworthy otherwise. 313 if (imageMimeType == MIMETypeRegistry::getMIMETypeForExtension(fileExtension)) 314 imageExtension = fileExtension; 315 } 316 317 imageExtension = "." + imageExtension; 318 validateFilename(filename, imageExtension); 319 320 dataObject->addSharedBuffer(filename + imageExtension, imageBuffer); 321 } 322 323 void DataTransfer::declareAndWriteDragImage(Element* element, const KURL& url, const String& title) 324 { 325 if (!m_dataObject) 326 return; 327 328 m_dataObject->setURLAndTitle(url, title); 329 330 // Write the bytes in the image to the file format. 331 writeImageToDataObject(m_dataObject.get(), element, url); 332 333 // Put img tag on the clipboard referencing the image 334 m_dataObject->setData(mimeTypeTextHTML, createMarkup(element, IncludeNode, 0, ResolveAllURLs)); 335 } 336 337 void DataTransfer::writeURL(const KURL& url, const String& title) 338 { 339 if (!m_dataObject) 340 return; 341 ASSERT(!url.isEmpty()); 342 343 m_dataObject->setURLAndTitle(url, title); 344 345 // The URL can also be used as plain text. 346 m_dataObject->setData(mimeTypeTextPlain, url.string()); 347 348 // The URL can also be used as an HTML fragment. 349 m_dataObject->setHTMLAndBaseURL(urlToMarkup(url, title), url); 350 } 351 352 void DataTransfer::writeRange(Range* selectedRange, LocalFrame* frame) 353 { 354 ASSERT(selectedRange); 355 if (!m_dataObject) 356 return; 357 358 m_dataObject->setHTMLAndBaseURL(createMarkup(selectedRange, 0, AnnotateForInterchange, false, ResolveNonLocalURLs), frame->document()->url()); 359 360 String str = frame->selectedTextForClipboard(); 361 #if OS(WIN) 362 replaceNewlinesWithWindowsStyleNewlines(str); 363 #endif 364 replaceNBSPWithSpace(str); 365 m_dataObject->setData(mimeTypeTextPlain, str); 366 } 367 368 void DataTransfer::writePlainText(const String& text) 369 { 370 if (!m_dataObject) 371 return; 372 373 String str = text; 374 #if OS(WIN) 375 replaceNewlinesWithWindowsStyleNewlines(str); 376 #endif 377 replaceNBSPWithSpace(str); 378 379 m_dataObject->setData(mimeTypeTextPlain, str); 380 } 381 382 bool DataTransfer::hasData() 383 { 384 ASSERT(isForDragAndDrop()); 385 386 return m_dataObject->length() > 0; 387 } 388 389 void DataTransfer::setAccessPolicy(DataTransferAccessPolicy policy) 390 { 391 // once you go numb, can never go back 392 ASSERT(m_policy != DataTransferNumb || policy == DataTransferNumb); 393 m_policy = policy; 394 } 395 396 bool DataTransfer::canReadTypes() const 397 { 398 return m_policy == DataTransferReadable || m_policy == DataTransferTypesReadable || m_policy == DataTransferWritable; 399 } 400 401 bool DataTransfer::canReadData() const 402 { 403 return m_policy == DataTransferReadable || m_policy == DataTransferWritable; 404 } 405 406 bool DataTransfer::canWriteData() const 407 { 408 return m_policy == DataTransferWritable; 409 } 410 411 bool DataTransfer::canSetDragImage() const 412 { 413 return m_policy == DataTransferImageWritable || m_policy == DataTransferWritable; 414 } 415 416 DragOperation DataTransfer::sourceOperation() const 417 { 418 DragOperation op = convertEffectAllowedToDragOperation(m_effectAllowed); 419 ASSERT(op != DragOperationPrivate); 420 return op; 421 } 422 423 DragOperation DataTransfer::destinationOperation() const 424 { 425 DragOperation op = convertEffectAllowedToDragOperation(m_dropEffect); 426 ASSERT(op == DragOperationCopy || op == DragOperationNone || op == DragOperationLink || op == (DragOperation)(DragOperationGeneric | DragOperationMove) || op == DragOperationEvery); 427 return op; 428 } 429 430 void DataTransfer::setSourceOperation(DragOperation op) 431 { 432 ASSERT_ARG(op, op != DragOperationPrivate); 433 m_effectAllowed = convertDragOperationToEffectAllowed(op); 434 } 435 436 void DataTransfer::setDestinationOperation(DragOperation op) 437 { 438 ASSERT_ARG(op, op == DragOperationCopy || op == DragOperationNone || op == DragOperationLink || op == DragOperationGeneric || op == DragOperationMove || op == (DragOperation)(DragOperationGeneric | DragOperationMove)); 439 m_dropEffect = convertDragOperationToEffectAllowed(op); 440 } 441 442 bool DataTransfer::hasDropZoneType(const String& keyword) 443 { 444 if (keyword.startsWith("file:")) 445 return hasFileOfType(keyword.substring(5)); 446 447 if (keyword.startsWith("string:")) 448 return hasStringOfType(keyword.substring(7)); 449 450 return false; 451 } 452 453 PassRefPtrWillBeRawPtr<DataTransferItemList> DataTransfer::items() 454 { 455 // FIXME: According to the spec, we are supposed to return the same collection of items each 456 // time. We now return a wrapper that always wraps the *same* set of items, so JS shouldn't be 457 // able to tell, but we probably still want to fix this. 458 return DataTransferItemList::create(this, m_dataObject); 459 } 460 461 PassRefPtrWillBeRawPtr<DataObject> DataTransfer::dataObject() const 462 { 463 return m_dataObject; 464 } 465 466 DataTransfer::DataTransfer(DataTransferType type, DataTransferAccessPolicy policy, PassRefPtrWillBeRawPtr<DataObject> dataObject) 467 : m_policy(policy) 468 , m_dropEffect("uninitialized") 469 , m_effectAllowed("uninitialized") 470 , m_transferType(type) 471 , m_dataObject(dataObject) 472 { 473 } 474 475 void DataTransfer::setDragImage(ImageResource* image, Node* node, const IntPoint& loc) 476 { 477 if (!canSetDragImage()) 478 return; 479 480 m_dragImage = image; 481 m_dragLoc = loc; 482 m_dragImageElement = node; 483 } 484 485 bool DataTransfer::hasFileOfType(const String& type) const 486 { 487 if (!canReadTypes()) 488 return false; 489 490 RefPtrWillBeRawPtr<FileList> fileList = files(); 491 if (fileList->isEmpty()) 492 return false; 493 494 for (unsigned f = 0; f < fileList->length(); f++) { 495 if (equalIgnoringCase(fileList->item(f)->type(), type)) 496 return true; 497 } 498 return false; 499 } 500 501 bool DataTransfer::hasStringOfType(const String& type) const 502 { 503 if (!canReadTypes()) 504 return false; 505 506 return types().contains(type); 507 } 508 509 DragOperation convertDropZoneOperationToDragOperation(const String& dragOperation) 510 { 511 if (dragOperation == "copy") 512 return DragOperationCopy; 513 if (dragOperation == "move") 514 return DragOperationMove; 515 if (dragOperation == "link") 516 return DragOperationLink; 517 return DragOperationNone; 518 } 519 520 String convertDragOperationToDropZoneOperation(DragOperation operation) 521 { 522 switch (operation) { 523 case DragOperationCopy: 524 return String("copy"); 525 case DragOperationMove: 526 return String("move"); 527 case DragOperationLink: 528 return String("link"); 529 default: 530 return String("copy"); 531 } 532 } 533 534 void DataTransfer::trace(Visitor* visitor) 535 { 536 visitor->trace(m_dataObject); 537 visitor->trace(m_dragImageElement); 538 } 539 540 } // namespace blink 541