Home | History | Annotate | Download | only in clipboard
      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