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