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 "ClipboardChromium.h"
     29 
     30 #include "CachedImage.h"
     31 #include "ChromiumBridge.h"
     32 #include "ChromiumDataObject.h"
     33 #include "ClipboardUtilitiesChromium.h"
     34 #include "Document.h"
     35 #include "Element.h"
     36 #include "FileList.h"
     37 #include "Frame.h"
     38 #include "HTMLNames.h"
     39 #include "NamedAttrMap.h"
     40 #include "MIMETypeRegistry.h"
     41 #include "markup.h"
     42 #include "NamedNodeMap.h"
     43 #include "Pasteboard.h"
     44 #include "PlatformString.h"
     45 #include "Range.h"
     46 #include "RenderImage.h"
     47 #include "StringBuilder.h"
     48 
     49 namespace WebCore {
     50 
     51 using namespace HTMLNames;
     52 
     53 // We provide the IE clipboard types (URL and Text), and the clipboard types specified in the WHATWG Web Applications 1.0 draft
     54 // see http://www.whatwg.org/specs/web-apps/current-work/ Section 6.3.5.3
     55 
     56 enum ClipboardDataType { ClipboardDataTypeNone, ClipboardDataTypeURL, ClipboardDataTypeText, ClipboardDataTypeDownloadURL };
     57 
     58 static ClipboardDataType clipboardTypeFromMIMEType(const String& type)
     59 {
     60     String cleanType = type.stripWhiteSpace().lower();
     61 
     62     // two special cases for IE compatibility
     63     if (cleanType == "text" || cleanType == "text/plain" || cleanType.startsWith("text/plain;"))
     64         return ClipboardDataTypeText;
     65     if (cleanType == "url" || cleanType == "text/uri-list")
     66         return ClipboardDataTypeURL;
     67     if (cleanType == "downloadurl")
     68         return ClipboardDataTypeDownloadURL;
     69 
     70     return ClipboardDataTypeNone;
     71 }
     72 
     73 ClipboardChromium::ClipboardChromium(bool isForDragging,
     74                                      PassRefPtr<ChromiumDataObject> dataObject,
     75                                      ClipboardAccessPolicy policy)
     76     : Clipboard(policy, isForDragging)
     77     , m_dataObject(dataObject)
     78 {
     79 }
     80 
     81 PassRefPtr<ClipboardChromium> ClipboardChromium::create(bool isForDragging,
     82     PassRefPtr<ChromiumDataObject> dataObject, ClipboardAccessPolicy policy)
     83 {
     84     return adoptRef(new ClipboardChromium(isForDragging, dataObject, policy));
     85 }
     86 
     87 void ClipboardChromium::clearData(const String& type)
     88 {
     89     if (policy() != ClipboardWritable || !m_dataObject)
     90         return;
     91 
     92     ClipboardDataType dataType = clipboardTypeFromMIMEType(type);
     93 
     94     if (dataType == ClipboardDataTypeURL) {
     95         m_dataObject->url = KURL();
     96         m_dataObject->urlTitle = "";
     97     }
     98 
     99     if (dataType == ClipboardDataTypeText)
    100         m_dataObject->plainText = "";
    101 }
    102 
    103 void ClipboardChromium::clearAllData()
    104 {
    105     if (policy() != ClipboardWritable)
    106         return;
    107 
    108     m_dataObject->clear();
    109 }
    110 
    111 String ClipboardChromium::getData(const String& type, bool& success) const
    112 {
    113     success = false;
    114     if (policy() != ClipboardReadable || !m_dataObject)
    115         return String();
    116 
    117     ClipboardDataType dataType = clipboardTypeFromMIMEType(type);
    118     String text;
    119     if (dataType == ClipboardDataTypeText) {
    120         if (!isForDragging()) {
    121             // If this isn't for a drag, it's for a cut/paste event handler.
    122             // In this case, we need to check the clipboard.
    123             PasteboardPrivate::ClipboardBuffer buffer =
    124                 Pasteboard::generalPasteboard()->isSelectionMode() ?
    125                 PasteboardPrivate::SelectionBuffer :
    126                 PasteboardPrivate::StandardBuffer;
    127             text = ChromiumBridge::clipboardReadPlainText(buffer);
    128             success = !text.isEmpty();
    129         } else if (!m_dataObject->plainText.isEmpty()) {
    130             success = true;
    131             text = m_dataObject->plainText;
    132         }
    133     } else if (dataType == ClipboardDataTypeURL) {
    134         // FIXME: Handle the cut/paste event.  This requires adding a new IPC
    135         // message to get the URL from the clipboard directly.
    136         if (!m_dataObject->url.isEmpty()) {
    137             success = true;
    138             text = m_dataObject->url.string();
    139         }
    140     }
    141 
    142     return text;
    143 }
    144 
    145 bool ClipboardChromium::setData(const String& type, const String& data)
    146 {
    147     if (policy() != ClipboardWritable)
    148         return false;
    149 
    150     ClipboardDataType winType = clipboardTypeFromMIMEType(type);
    151 
    152     if (winType == ClipboardDataTypeURL) {
    153         m_dataObject->url = KURL(ParsedURLString, data);
    154         return m_dataObject->url.isValid();
    155     }
    156 
    157     if (winType == ClipboardDataTypeText) {
    158         m_dataObject->plainText = data;
    159         return true;
    160     }
    161 
    162     if (winType == ClipboardDataTypeDownloadURL) {
    163         m_dataObject->downloadMetadata = data;
    164         KURL url = KURL(ParsedURLString, data);
    165         if (url.isValid())
    166             m_dataObject->downloadURL = url;
    167         return true;
    168     }
    169 
    170     return false;
    171 }
    172 
    173 // extensions beyond IE's API
    174 HashSet<String> ClipboardChromium::types() const
    175 {
    176     HashSet<String> results;
    177     if (policy() != ClipboardReadable && policy() != ClipboardTypesReadable)
    178         return results;
    179 
    180     if (!m_dataObject)
    181         return results;
    182 
    183     if (!m_dataObject->filenames.isEmpty())
    184         results.add("Files");
    185 
    186     if (m_dataObject->url.isValid()) {
    187         results.add("URL");
    188         results.add("text/uri-list");
    189     }
    190 
    191     if (!m_dataObject->plainText.isEmpty()) {
    192         results.add("Text");
    193         results.add("text/plain");
    194     }
    195 
    196     return results;
    197 }
    198 
    199 PassRefPtr<FileList> ClipboardChromium::files() const
    200 {
    201     if (policy() != ClipboardReadable)
    202         return FileList::create();
    203 
    204     if (!m_dataObject || m_dataObject->filenames.isEmpty())
    205         return FileList::create();
    206 
    207     RefPtr<FileList> fileList = FileList::create();
    208     for (size_t i = 0; i < m_dataObject->filenames.size(); ++i)
    209         fileList->append(File::create(m_dataObject->filenames.at(i)));
    210 
    211     return fileList.release();
    212 }
    213 
    214 void ClipboardChromium::setDragImage(CachedImage* image, Node* node, const IntPoint& loc)
    215 {
    216     if (policy() != ClipboardImageWritable && policy() != ClipboardWritable)
    217         return;
    218 
    219     if (m_dragImage)
    220         m_dragImage->removeClient(this);
    221     m_dragImage = image;
    222     if (m_dragImage)
    223         m_dragImage->addClient(this);
    224 
    225     m_dragLoc = loc;
    226     m_dragImageElement = node;
    227 }
    228 
    229 void ClipboardChromium::setDragImage(CachedImage* img, const IntPoint& loc)
    230 {
    231     setDragImage(img, 0, loc);
    232 }
    233 
    234 void ClipboardChromium::setDragImageElement(Node* node, const IntPoint& loc)
    235 {
    236     setDragImage(0, node, loc);
    237 }
    238 
    239 DragImageRef ClipboardChromium::createDragImage(IntPoint& loc) const
    240 {
    241     DragImageRef result = 0;
    242     if (m_dragImage) {
    243         result = createDragImageFromImage(m_dragImage->image());
    244         loc = m_dragLoc;
    245     }
    246     return result;
    247 }
    248 
    249 static String imageToMarkup(const String& url, Element* element)
    250 {
    251     StringBuilder markup;
    252     markup.append("<img src=\"");
    253     markup.append(url);
    254     markup.append("\"");
    255     // Copy over attributes.  If we are dragging an image, we expect things like
    256     // the id to be copied as well.
    257     NamedNodeMap* attrs = element->attributes();
    258     unsigned length = attrs->length();
    259     for (unsigned i = 0; i < length; ++i) {
    260         Attribute* attr = attrs->attributeItem(i);
    261         if (attr->localName() == "src")
    262             continue;
    263         markup.append(" ");
    264         markup.append(attr->localName());
    265         markup.append("=\"");
    266         String escapedAttr = attr->value();
    267         escapedAttr.replace("\"", "&quot;");
    268         markup.append(escapedAttr);
    269         markup.append("\"");
    270     }
    271 
    272     markup.append("/>");
    273     return markup.toString();
    274 }
    275 
    276 static CachedImage* getCachedImage(Element* element)
    277 {
    278     // Attempt to pull CachedImage from element
    279     ASSERT(element);
    280     RenderObject* renderer = element->renderer();
    281     if (!renderer || !renderer->isImage())
    282         return 0;
    283 
    284     RenderImage* image = toRenderImage(renderer);
    285     if (image->cachedImage() && !image->cachedImage()->errorOccurred())
    286         return image->cachedImage();
    287 
    288     return 0;
    289 }
    290 
    291 static void writeImageToDataObject(ChromiumDataObject* dataObject, Element* element,
    292                                    const KURL& url)
    293 {
    294     // Shove image data into a DataObject for use as a file
    295     CachedImage* cachedImage = getCachedImage(element);
    296     if (!cachedImage || !cachedImage->image() || !cachedImage->isLoaded())
    297         return;
    298 
    299     SharedBuffer* imageBuffer = cachedImage->image()->data();
    300     if (!imageBuffer || !imageBuffer->size())
    301         return;
    302 
    303     dataObject->fileContent = imageBuffer;
    304 
    305     // Determine the filename for the file contents of the image.  We try to
    306     // use the alt tag if one exists, otherwise we fall back on the suggested
    307     // filename in the http header, and finally we resort to using the filename
    308     // in the URL.
    309     String extension = MIMETypeRegistry::getPreferredExtensionForMIMEType(
    310         cachedImage->response().mimeType());
    311     dataObject->fileExtension = extension.isEmpty() ? "" : "." + extension;
    312     String title = element->getAttribute(altAttr);
    313     if (title.isEmpty())
    314         title = cachedImage->response().suggestedFilename();
    315 
    316     title = ClipboardChromium::validateFileName(title, dataObject);
    317     dataObject->fileContentFilename = title + dataObject->fileExtension;
    318 }
    319 
    320 void ClipboardChromium::declareAndWriteDragImage(Element* element, const KURL& url, const String& title, Frame* frame)
    321 {
    322     if (!m_dataObject)
    323         return;
    324 
    325     m_dataObject->url = url;
    326     m_dataObject->urlTitle = title;
    327 
    328     // Write the bytes in the image to the file format.
    329     writeImageToDataObject(m_dataObject.get(), element, url);
    330 
    331     AtomicString imageURL = element->getAttribute(srcAttr);
    332     if (imageURL.isEmpty())
    333         return;
    334 
    335     String fullURL = frame->document()->completeURL(deprecatedParseURL(imageURL));
    336     if (fullURL.isEmpty())
    337         return;
    338 
    339     // Put img tag on the clipboard referencing the image
    340     m_dataObject->textHtml = imageToMarkup(fullURL, element);
    341 }
    342 
    343 void ClipboardChromium::writeURL(const KURL& url, const String& title, Frame*)
    344 {
    345     if (!m_dataObject)
    346         return;
    347     m_dataObject->url = url;
    348     m_dataObject->urlTitle = title;
    349 
    350     // The URL can also be used as plain text.
    351     m_dataObject->plainText = url.string();
    352 
    353     // The URL can also be used as an HTML fragment.
    354     m_dataObject->textHtml = urlToMarkup(url, title);
    355     m_dataObject->htmlBaseUrl = url;
    356 }
    357 
    358 void ClipboardChromium::writeRange(Range* selectedRange, Frame* frame)
    359 {
    360     ASSERT(selectedRange);
    361     if (!m_dataObject)
    362          return;
    363 
    364     m_dataObject->textHtml = createMarkup(selectedRange, 0,
    365         AnnotateForInterchange);
    366     m_dataObject->htmlBaseUrl = frame->document()->url();
    367 
    368     String str = frame->selectedText();
    369 #if OS(WINDOWS)
    370     replaceNewlinesWithWindowsStyleNewlines(str);
    371 #endif
    372     replaceNBSPWithSpace(str);
    373     m_dataObject->plainText = str;
    374 }
    375 
    376 void ClipboardChromium::writePlainText(const String& text)
    377 {
    378     if (!m_dataObject)
    379         return;
    380 
    381     String str = text;
    382 #if OS(WINDOWS)
    383     replaceNewlinesWithWindowsStyleNewlines(str);
    384 #endif
    385     replaceNBSPWithSpace(str);
    386     m_dataObject->plainText = str;
    387 }
    388 
    389 bool ClipboardChromium::hasData()
    390 {
    391     if (!m_dataObject)
    392         return false;
    393 
    394     return m_dataObject->hasData();
    395 }
    396 
    397 } // namespace WebCore
    398