Home | History | Annotate | Download | only in chromium
      1 /*
      2  * Copyright (C) 2006, 2007 Apple Inc.  All rights reserved.
      3  * Copyright (C) 2008, 2009 Google Inc.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  */
     26 
     27 #include "config.h"
     28 #include "core/platform/chromium/ClipboardChromium.h"
     29 
     30 #include "HTMLNames.h"
     31 #include "bindings/v8/ExceptionState.h"
     32 #include "core/dom/DataTransferItemList.h"
     33 #include "core/dom/Document.h"
     34 #include "core/dom/Element.h"
     35 #include "core/dom/ExceptionCode.h"
     36 #include "core/dom/StringCallback.h"
     37 #include "core/editing/markup.h"
     38 #include "core/fileapi/File.h"
     39 #include "core/fileapi/FileList.h"
     40 #include "core/loader/cache/ImageResource.h"
     41 #include "core/page/Frame.h"
     42 #include "core/platform/DragData.h"
     43 #include "core/platform/MIMETypeRegistry.h"
     44 #include "core/platform/chromium/ChromiumDataObject.h"
     45 #include "core/platform/chromium/ChromiumDataObjectItem.h"
     46 #include "core/platform/chromium/ClipboardMimeTypes.h"
     47 #include "core/platform/chromium/ClipboardUtilitiesChromium.h"
     48 #include "core/platform/graphics/Image.h"
     49 #include "core/rendering/RenderImage.h"
     50 
     51 #include "wtf/text/WTFString.h"
     52 
     53 namespace WebCore {
     54 
     55 namespace {
     56 
     57 // A wrapper class that invalidates a DataTransferItemList when the associated Clipboard object goes out of scope.
     58 class DataTransferItemListPolicyWrapper : public DataTransferItemList {
     59 public:
     60     static PassRefPtr<DataTransferItemListPolicyWrapper> create(PassRefPtr<ClipboardChromium>, PassRefPtr<ChromiumDataObject>);
     61     virtual ~DataTransferItemListPolicyWrapper();
     62 
     63     virtual size_t length() const;
     64     virtual PassRefPtr<DataTransferItem> item(unsigned long index) OVERRIDE;
     65     virtual void deleteItem(unsigned long index, ExceptionState&) OVERRIDE;
     66     virtual void clear() OVERRIDE;
     67     virtual void add(const String& data, const String& type, ExceptionState&) OVERRIDE;
     68     virtual void add(PassRefPtr<File>) OVERRIDE;
     69 
     70 private:
     71     DataTransferItemListPolicyWrapper(PassRefPtr<ClipboardChromium>, PassRefPtr<ChromiumDataObject>);
     72 
     73     RefPtr<ClipboardChromium> m_clipboard;
     74     RefPtr<ChromiumDataObject> m_dataObject;
     75 };
     76 
     77 
     78 PassRefPtr<DataTransferItemListPolicyWrapper> DataTransferItemListPolicyWrapper::create(
     79     PassRefPtr<ClipboardChromium> clipboard, PassRefPtr<ChromiumDataObject> list)
     80 {
     81     return adoptRef(new DataTransferItemListPolicyWrapper(clipboard, list));
     82 }
     83 
     84 DataTransferItemListPolicyWrapper::~DataTransferItemListPolicyWrapper()
     85 {
     86 }
     87 
     88 size_t DataTransferItemListPolicyWrapper::length() const
     89 {
     90     if (!m_clipboard->canReadTypes())
     91         return 0;
     92     return m_dataObject->length();
     93 }
     94 
     95 PassRefPtr<DataTransferItem> DataTransferItemListPolicyWrapper::item(unsigned long index)
     96 {
     97     if (!m_clipboard->canReadTypes())
     98         return 0;
     99     RefPtr<ChromiumDataObjectItem> item = m_dataObject->item(index);
    100     if (!item)
    101         return 0;
    102 
    103     return DataTransferItemPolicyWrapper::create(m_clipboard, item);
    104 }
    105 
    106 void DataTransferItemListPolicyWrapper::deleteItem(unsigned long index, ExceptionState& es)
    107 {
    108     if (!m_clipboard->canWriteData()) {
    109         es.throwDOMException(InvalidStateError);
    110         return;
    111     }
    112     m_dataObject->deleteItem(index);
    113 }
    114 
    115 void DataTransferItemListPolicyWrapper::clear()
    116 {
    117     if (!m_clipboard->canWriteData())
    118         return;
    119     m_dataObject->clearAll();
    120 }
    121 
    122 void DataTransferItemListPolicyWrapper::add(const String& data, const String& type, ExceptionState& es)
    123 {
    124     if (!m_clipboard->canWriteData())
    125         return;
    126     m_dataObject->add(data, type, es);
    127 }
    128 
    129 void DataTransferItemListPolicyWrapper::add(PassRefPtr<File> file)
    130 {
    131     if (!m_clipboard->canWriteData())
    132         return;
    133     m_dataObject->add(file, m_clipboard->frame()->document()->scriptExecutionContext());
    134 }
    135 
    136 DataTransferItemListPolicyWrapper::DataTransferItemListPolicyWrapper(
    137     PassRefPtr<ClipboardChromium> clipboard, PassRefPtr<ChromiumDataObject> dataObject)
    138     : m_clipboard(clipboard)
    139     , m_dataObject(dataObject)
    140 {
    141 }
    142 
    143 } // namespace
    144 
    145 PassRefPtr<DataTransferItemPolicyWrapper> DataTransferItemPolicyWrapper::create(
    146     PassRefPtr<ClipboardChromium> clipboard, PassRefPtr<ChromiumDataObjectItem> item)
    147 {
    148     return adoptRef(new DataTransferItemPolicyWrapper(clipboard, item));
    149 }
    150 
    151 DataTransferItemPolicyWrapper::~DataTransferItemPolicyWrapper()
    152 {
    153 }
    154 
    155 String DataTransferItemPolicyWrapper::kind() const
    156 {
    157     if (!m_clipboard->canReadTypes())
    158         return String();
    159     return m_item->kind();
    160 }
    161 
    162 String DataTransferItemPolicyWrapper::type() const
    163 {
    164     if (!m_clipboard->canReadTypes())
    165         return String();
    166     return m_item->type();
    167 }
    168 
    169 void DataTransferItemPolicyWrapper::getAsString(PassRefPtr<StringCallback> callback) const
    170 {
    171     if (!m_clipboard->canReadData())
    172         return;
    173 
    174     m_item->getAsString(callback, m_clipboard->frame()->document()->scriptExecutionContext());
    175 }
    176 
    177 PassRefPtr<Blob> DataTransferItemPolicyWrapper::getAsFile() const
    178 {
    179     if (!m_clipboard->canReadData())
    180         return 0;
    181 
    182     return m_item->getAsFile();
    183 }
    184 
    185 DataTransferItemPolicyWrapper::DataTransferItemPolicyWrapper(
    186     PassRefPtr<ClipboardChromium> clipboard, PassRefPtr<ChromiumDataObjectItem> item)
    187     : m_clipboard(clipboard)
    188     , m_item(item)
    189 {
    190 }
    191 
    192 using namespace HTMLNames;
    193 
    194 // We provide the IE clipboard types (URL and Text), and the clipboard types specified in the WHATWG Web Applications 1.0 draft
    195 // see http://www.whatwg.org/specs/web-apps/current-work/ Section 6.3.5.3
    196 
    197 static String normalizeType(const String& type, bool* convertToURL = 0)
    198 {
    199     String cleanType = type.stripWhiteSpace().lower();
    200     if (cleanType == mimeTypeText || cleanType.startsWith(mimeTypeTextPlainEtc))
    201         return mimeTypeTextPlain;
    202     if (cleanType == mimeTypeURL) {
    203         if (convertToURL)
    204           *convertToURL = true;
    205         return mimeTypeTextURIList;
    206     }
    207     return cleanType;
    208 }
    209 
    210 PassRefPtr<Clipboard> Clipboard::create(ClipboardAccessPolicy policy, DragData* dragData, Frame* frame)
    211 {
    212     return ClipboardChromium::create(DragAndDrop, dragData->platformData(), policy, frame);
    213 }
    214 
    215 ClipboardChromium::ClipboardChromium(ClipboardType clipboardType,
    216                                      PassRefPtr<ChromiumDataObject> dataObject,
    217                                      ClipboardAccessPolicy policy,
    218                                      Frame* frame)
    219     : Clipboard(policy, clipboardType)
    220     , m_dataObject(dataObject)
    221     , m_frame(frame)
    222 {
    223 }
    224 
    225 ClipboardChromium::~ClipboardChromium()
    226 {
    227     if (m_dragImage)
    228         m_dragImage->removeClient(this);
    229 }
    230 
    231 PassRefPtr<ClipboardChromium> ClipboardChromium::create(ClipboardType clipboardType,
    232     PassRefPtr<ChromiumDataObject> dataObject, ClipboardAccessPolicy policy, Frame* frame)
    233 {
    234     return adoptRef(new ClipboardChromium(clipboardType, dataObject, policy, frame));
    235 }
    236 
    237 void ClipboardChromium::clearData(const String& type)
    238 {
    239     if (!canWriteData())
    240         return;
    241 
    242     m_dataObject->clearData(normalizeType(type));
    243 
    244     ASSERT_NOT_REACHED();
    245 }
    246 
    247 void ClipboardChromium::clearAllData()
    248 {
    249     if (!canWriteData())
    250         return;
    251 
    252     m_dataObject->clearAll();
    253 }
    254 
    255 String ClipboardChromium::getData(const String& type) const
    256 {
    257     if (!canReadData())
    258         return String();
    259 
    260     bool convertToURL = false;
    261     String data = m_dataObject->getData(normalizeType(type, &convertToURL));
    262     if (!convertToURL)
    263         return data;
    264     return convertURIListToURL(data);
    265 }
    266 
    267 bool ClipboardChromium::setData(const String& type, const String& data)
    268 {
    269     if (!canWriteData())
    270         return false;
    271 
    272     return m_dataObject->setData(normalizeType(type), data);
    273 }
    274 
    275 // extensions beyond IE's API
    276 ListHashSet<String> ClipboardChromium::types() const
    277 {
    278     if (!canReadTypes())
    279         return ListHashSet<String>();
    280 
    281     return m_dataObject->types();
    282 }
    283 
    284 PassRefPtr<FileList> ClipboardChromium::files() const
    285 {
    286     RefPtr<FileList> files = FileList::create();
    287     if (!canReadData())
    288         return files.release();
    289 
    290     for (size_t i = 0; i < m_dataObject->length(); ++i) {
    291         if (m_dataObject->item(i)->kind() == DataTransferItem::kindFile) {
    292             RefPtr<Blob> blob = m_dataObject->item(i)->getAsFile();
    293             if (blob && blob->isFile())
    294                 files->append(toFile(blob.get()));
    295         }
    296     }
    297 
    298     return files.release();
    299 }
    300 
    301 void ClipboardChromium::setDragImage(ImageResource* image, Node* node, const IntPoint& loc)
    302 {
    303     if (!canSetDragImage())
    304         return;
    305 
    306     if (m_dragImage)
    307         m_dragImage->removeClient(this);
    308     m_dragImage = image;
    309     if (m_dragImage)
    310         m_dragImage->addClient(this);
    311 
    312     m_dragLoc = loc;
    313     m_dragImageElement = node;
    314 }
    315 
    316 void ClipboardChromium::setDragImage(ImageResource* img, const IntPoint& loc)
    317 {
    318     setDragImage(img, 0, loc);
    319 }
    320 
    321 void ClipboardChromium::setDragImageElement(Node* node, const IntPoint& loc)
    322 {
    323     setDragImage(0, node, loc);
    324 }
    325 
    326 PassOwnPtr<DragImage> ClipboardChromium::createDragImage(IntPoint& loc) const
    327 {
    328     if (m_dragImageElement) {
    329         if (m_frame) {
    330             loc = m_dragLoc;
    331             return m_frame->nodeImage(m_dragImageElement.get());
    332         }
    333     } else if (m_dragImage) {
    334         loc = m_dragLoc;
    335         return DragImage::create(m_dragImage->image());
    336     }
    337     return nullptr;
    338 }
    339 
    340 static ImageResource* getImageResource(Element* element)
    341 {
    342     // Attempt to pull ImageResource from element
    343     ASSERT(element);
    344     RenderObject* renderer = element->renderer();
    345     if (!renderer || !renderer->isImage())
    346         return 0;
    347 
    348     RenderImage* image = toRenderImage(renderer);
    349     if (image->cachedImage() && !image->cachedImage()->errorOccurred())
    350         return image->cachedImage();
    351 
    352     return 0;
    353 }
    354 
    355 static void writeImageToDataObject(ChromiumDataObject* dataObject, Element* element,
    356                                    const KURL& url)
    357 {
    358     // Shove image data into a DataObject for use as a file
    359     ImageResource* cachedImage = getImageResource(element);
    360     if (!cachedImage || !cachedImage->imageForRenderer(element->renderer()) || !cachedImage->isLoaded())
    361         return;
    362 
    363     SharedBuffer* imageBuffer = cachedImage->imageForRenderer(element->renderer())->data();
    364     if (!imageBuffer || !imageBuffer->size())
    365         return;
    366 
    367     String imageExtension = cachedImage->image()->filenameExtension();
    368     ASSERT(!imageExtension.isEmpty());
    369 
    370     // Determine the filename for the file contents of the image.
    371     String filename = cachedImage->response().suggestedFilename();
    372     if (filename.isEmpty())
    373         filename = url.lastPathComponent();
    374 
    375     String fileExtension;
    376     if (filename.isEmpty()) {
    377         filename = element->getAttribute(altAttr);
    378     } else {
    379         // Strip any existing extension. Assume that alt text is usually not a filename.
    380         int extensionIndex = filename.reverseFind('.');
    381         if (extensionIndex != -1) {
    382             fileExtension = filename.substring(extensionIndex + 1);
    383             filename.truncate(extensionIndex);
    384         }
    385     }
    386 
    387     if (!fileExtension.isEmpty() && fileExtension != imageExtension) {
    388         String imageMimeType = MIMETypeRegistry::getMIMETypeForExtension(imageExtension);
    389         ASSERT(imageMimeType.startsWith("image/"));
    390         // Use the file extension only if it has imageMimeType: it's untrustworthy otherwise.
    391         if (imageMimeType == MIMETypeRegistry::getMIMETypeForExtension(fileExtension))
    392             imageExtension = fileExtension;
    393     }
    394 
    395     imageExtension = "." + imageExtension;
    396     ClipboardChromium::validateFilename(filename, imageExtension);
    397 
    398     dataObject->addSharedBuffer(filename + imageExtension, imageBuffer);
    399 }
    400 
    401 void ClipboardChromium::declareAndWriteDragImage(Element* element, const KURL& url, const String& title, Frame* frame)
    402 {
    403     if (!m_dataObject)
    404         return;
    405 
    406     m_dataObject->setURLAndTitle(url, title);
    407 
    408     // Write the bytes in the image to the file format.
    409     writeImageToDataObject(m_dataObject.get(), element, url);
    410 
    411     // Put img tag on the clipboard referencing the image
    412     m_dataObject->setData(mimeTypeTextHTML, createMarkup(element, IncludeNode, 0, ResolveAllURLs));
    413 }
    414 
    415 void ClipboardChromium::writeURL(const KURL& url, const String& title, Frame*)
    416 {
    417     if (!m_dataObject)
    418         return;
    419     ASSERT(!url.isEmpty());
    420 
    421     m_dataObject->setURLAndTitle(url, title);
    422 
    423     // The URL can also be used as plain text.
    424     m_dataObject->setData(mimeTypeTextPlain, url.string());
    425 
    426     // The URL can also be used as an HTML fragment.
    427     m_dataObject->setHTMLAndBaseURL(urlToMarkup(url, title), url);
    428 }
    429 
    430 void ClipboardChromium::writeRange(Range* selectedRange, Frame* frame)
    431 {
    432     ASSERT(selectedRange);
    433     if (!m_dataObject)
    434          return;
    435 
    436     m_dataObject->setHTMLAndBaseURL(createMarkup(selectedRange, 0, AnnotateForInterchange, false, ResolveNonLocalURLs), frame->document()->url());
    437 
    438     String str = frame->selectedTextForClipboard();
    439 #if OS(WINDOWS)
    440     replaceNewlinesWithWindowsStyleNewlines(str);
    441 #endif
    442     replaceNBSPWithSpace(str);
    443     m_dataObject->setData(mimeTypeTextPlain, str);
    444 }
    445 
    446 void ClipboardChromium::writePlainText(const String& text)
    447 {
    448     if (!m_dataObject)
    449         return;
    450 
    451     String str = text;
    452 #if OS(WINDOWS)
    453     replaceNewlinesWithWindowsStyleNewlines(str);
    454 #endif
    455     replaceNBSPWithSpace(str);
    456 
    457     m_dataObject->setData(mimeTypeTextPlain, str);
    458 }
    459 
    460 bool ClipboardChromium::hasData()
    461 {
    462     ASSERT(isForDragAndDrop());
    463 
    464     return m_dataObject->length() > 0;
    465 }
    466 
    467 PassRefPtr<DataTransferItemList> ClipboardChromium::items()
    468 {
    469     // FIXME: According to the spec, we are supposed to return the same collection of items each
    470     // time. We now return a wrapper that always wraps the *same* set of items, so JS shouldn't be
    471     // able to tell, but we probably still want to fix this.
    472     return DataTransferItemListPolicyWrapper::create(this, m_dataObject);
    473 }
    474 
    475 } // namespace WebCore
    476