3121ea80916f4b6d345488dd45f39c00ecd41811
[WebKit-https.git] / Source / WebCore / platform / chromium / ClipboardChromium.cpp
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 "DataTransferItem.h"
35 #include "DataTransferItemChromium.h"
36 #include "DataTransferItemList.h"
37 #include "DataTransferItemListChromium.h"
38 #include "Document.h"
39 #include "DragData.h"
40 #include "Element.h"
41 #include "ExceptionCode.h"
42 #include "File.h"
43 #include "FileList.h"
44 #include "Frame.h"
45 #include "HTMLNames.h"
46 #include "HTMLParserIdioms.h"
47 #include "Image.h"
48 #include "MIMETypeRegistry.h"
49 #include "NamedNodeMap.h"
50 #include "PlatformSupport.h"
51 #include "Range.h"
52 #include "RenderImage.h"
53 #include "StringCallback.h"
54 #include "markup.h"
55
56 #include <wtf/text/WTFString.h>
57
58 namespace WebCore {
59
60 namespace {
61
62 // These wrapper classes invalidate a DataTransferItem/DataTransferItemList when the associated
63 // Clipboard object goes out of scope.
64 class DataTransferItemListPolicyWrapper : public DataTransferItemList {
65 public:
66     static PassRefPtr<DataTransferItemListPolicyWrapper> create(
67         PassRefPtr<ClipboardChromium>, PassRefPtr<DataTransferItemListChromium>);
68
69     virtual size_t length() const;
70     virtual PassRefPtr<DataTransferItem> item(unsigned long index);
71     virtual void deleteItem(unsigned long index, ExceptionCode&);
72     virtual void clear();
73     virtual void add(const String& data, const String& type, ExceptionCode&);
74     virtual void add(PassRefPtr<File>);
75
76 private:
77     DataTransferItemListPolicyWrapper(PassRefPtr<ClipboardChromium>, PassRefPtr<DataTransferItemListChromium>);
78
79     RefPtr<ClipboardChromium> m_clipboard;
80     RefPtr<DataTransferItemListChromium> m_list;
81 };
82
83 class DataTransferItemPolicyWrapper : public DataTransferItem {
84 public:
85     static PassRefPtr<DataTransferItemPolicyWrapper> create(
86         PassRefPtr<ClipboardChromium>, PassRefPtr<DataTransferItemChromium>);
87
88     virtual String kind() const;
89     virtual String type() const;
90
91     virtual void getAsString(PassRefPtr<StringCallback>) const;
92     virtual PassRefPtr<Blob> getAsFile() const;
93
94 private:
95     DataTransferItemPolicyWrapper(PassRefPtr<ClipboardChromium>, PassRefPtr<DataTransferItemChromium>);
96
97     RefPtr<ClipboardChromium> m_clipboard;
98     RefPtr<DataTransferItemChromium> m_item;
99 };
100
101 PassRefPtr<DataTransferItemListPolicyWrapper> DataTransferItemListPolicyWrapper::create(
102     PassRefPtr<ClipboardChromium> clipboard, PassRefPtr<DataTransferItemListChromium> list)
103 {
104     return adoptRef(new DataTransferItemListPolicyWrapper(clipboard, list));
105 }
106
107 size_t DataTransferItemListPolicyWrapper::length() const
108 {
109     if (m_clipboard->policy() == ClipboardNumb)
110         return 0;
111     return m_list->length();
112 }
113
114 PassRefPtr<DataTransferItem> DataTransferItemListPolicyWrapper::item(unsigned long index)
115 {
116     if (m_clipboard->policy() == ClipboardNumb)
117         return 0;
118     RefPtr<DataTransferItemChromium> item = m_list->item(index);
119     if (!item)
120         return 0;
121     return DataTransferItemPolicyWrapper::create(m_clipboard, item);
122 }
123
124 void DataTransferItemListPolicyWrapper::deleteItem(unsigned long index, ExceptionCode& ec)
125 {
126     if (m_clipboard->policy() != ClipboardWritable) {
127         ec = INVALID_STATE_ERR;
128         return;
129     }
130     m_list->deleteItem(index);
131 }
132
133 void DataTransferItemListPolicyWrapper::clear()
134 {
135     if (m_clipboard->policy() != ClipboardWritable)
136         return;
137     m_list->clear();
138 }
139
140 void DataTransferItemListPolicyWrapper::add(const String& data, const String& type, ExceptionCode& ec)
141 {
142     if (m_clipboard->policy() != ClipboardWritable)
143         return;
144     m_list->add(data, type, ec);
145 }
146
147 void DataTransferItemListPolicyWrapper::add(PassRefPtr<File> file)
148 {
149     if (m_clipboard->policy() != ClipboardWritable)
150         return;
151     m_list->add(file, m_clipboard->frame()->document()->scriptExecutionContext());
152 }
153
154 DataTransferItemListPolicyWrapper::DataTransferItemListPolicyWrapper(
155     PassRefPtr<ClipboardChromium> clipboard, PassRefPtr<DataTransferItemListChromium> list)
156     : m_clipboard(clipboard)
157     , m_list(list)
158 {
159 }
160
161 PassRefPtr<DataTransferItemPolicyWrapper> DataTransferItemPolicyWrapper::create(
162     PassRefPtr<ClipboardChromium> clipboard, PassRefPtr<DataTransferItemChromium> item)
163 {
164     return adoptRef(new DataTransferItemPolicyWrapper(clipboard, item));
165 }
166
167 String DataTransferItemPolicyWrapper::kind() const
168 {
169     if (m_clipboard->policy() == ClipboardNumb)
170         return String();
171     return m_item->kind();
172 }
173
174 String DataTransferItemPolicyWrapper::type() const
175 {
176     if (m_clipboard->policy() == ClipboardNumb)
177         return String();
178     return m_item->type();
179 }
180
181 void DataTransferItemPolicyWrapper::getAsString(PassRefPtr<StringCallback> callback) const
182 {
183     if (m_clipboard->policy() != ClipboardReadable && m_clipboard->policy() != ClipboardWritable)
184         return;
185
186     m_item->getAsString(callback, m_clipboard->frame()->document()->scriptExecutionContext());
187 }
188
189 PassRefPtr<Blob> DataTransferItemPolicyWrapper::getAsFile() const
190 {
191     if (m_clipboard->policy() != ClipboardReadable && m_clipboard->policy() != ClipboardWritable)
192         return 0;
193
194     return m_item->getAsFile();
195 }
196
197 DataTransferItemPolicyWrapper::DataTransferItemPolicyWrapper(
198     PassRefPtr<ClipboardChromium> clipboard, PassRefPtr<DataTransferItemChromium> item)
199     : m_clipboard(clipboard)
200     , m_item(item)
201 {
202 }
203
204 } // namespace
205
206 using namespace HTMLNames;
207
208 // We provide the IE clipboard types (URL and Text), and the clipboard types specified in the WHATWG Web Applications 1.0 draft
209 // see http://www.whatwg.org/specs/web-apps/current-work/ Section 6.3.5.3
210
211 static String normalizeType(const String& type, bool* convertToURL = 0)
212 {
213     String cleanType = type.stripWhiteSpace().lower();
214     if (cleanType == mimeTypeText || cleanType.startsWith(mimeTypeTextPlainEtc))
215         return mimeTypeTextPlain;
216     if (cleanType == mimeTypeURL) {
217         if (convertToURL)
218           *convertToURL = true;
219         return mimeTypeTextURIList;
220     }
221     return cleanType;
222 }
223
224 PassRefPtr<Clipboard> Clipboard::create(ClipboardAccessPolicy policy, DragData* dragData, Frame* frame)
225 {
226     return ClipboardChromium::create(DragAndDrop, dragData->platformData(), policy, frame);
227 }
228
229 ClipboardChromium::ClipboardChromium(ClipboardType clipboardType,
230                                      PassRefPtr<ChromiumDataObject> dataObject,
231                                      ClipboardAccessPolicy policy,
232                                      Frame* frame)
233     : Clipboard(policy, clipboardType)
234     , m_dataObject(dataObject)
235     , m_frame(frame)
236 {
237 }
238
239 ClipboardChromium::~ClipboardChromium()
240 {
241 }
242
243 PassRefPtr<ClipboardChromium> ClipboardChromium::create(ClipboardType clipboardType,
244     PassRefPtr<ChromiumDataObject> dataObject, ClipboardAccessPolicy policy, Frame* frame)
245 {
246     return adoptRef(new ClipboardChromium(clipboardType, dataObject, policy, frame));
247 }
248
249 void ClipboardChromium::clearData(const String& type)
250 {
251     if (policy() != ClipboardWritable)
252         return;
253
254     m_dataObject->clearData(normalizeType(type));
255
256     ASSERT_NOT_REACHED();
257 }
258
259 void ClipboardChromium::clearAllData()
260 {
261     if (policy() != ClipboardWritable)
262         return;
263
264     m_dataObject->clearAll();
265 }
266
267 String ClipboardChromium::getData(const String& type) const
268 {
269     if (policy() != ClipboardReadable)
270         return String();
271
272     bool convertToURL = false;
273     String data = m_dataObject->getData(normalizeType(type, &convertToURL));
274     if (!convertToURL)
275         return data;
276     return convertURIListToURL(data);
277
278     Vector<String> uriList;
279     // Line separator is \r\n per RFC 2483 - howver, for compatiblity
280     // reasons we also allow just \n here.
281     data.split('\n', uriList);
282     // Process the input and return the first valid RUL. In case no URLs can
283     // be found, return an empty string. This is in line with the HTML5
284     // spec (see "The DragEvent and DataTransfer interfaces").
285     for (size_t i = 0; i < uriList.size(); ++i) {
286         String& line = uriList[i];
287         line = line.stripWhiteSpace();
288         if (line.isEmpty())
289             continue;
290         if (line[0] == '#')
291             continue;
292         KURL url = KURL(ParsedURLString, line);
293         if (url.isValid())
294             return url;
295     }
296
297     return String();
298 }
299
300 bool ClipboardChromium::setData(const String& type, const String& data)
301 {
302     if (policy() != ClipboardWritable)
303         return false;
304
305     return m_dataObject->setData(normalizeType(type), data);
306 }
307
308 // extensions beyond IE's API
309 HashSet<String> ClipboardChromium::types() const
310 {
311     if (policy() != ClipboardReadable && policy() != ClipboardTypesReadable)
312         return HashSet<String>();
313
314     return m_dataObject->types();
315 }
316
317 PassRefPtr<FileList> ClipboardChromium::files() const
318 {
319     RefPtr<FileList> files = FileList::create();
320     if (policy() != ClipboardReadable)
321         return files.release();
322
323     RefPtr<DataTransferItemListChromium> list = m_dataObject->items();
324     for (size_t i = 0; i < list->length(); ++i) {
325         if (list->item(i)->kind() == DataTransferItem::kindFile) {
326             RefPtr<Blob> blob = list->item(i)->getAsFile();
327             if (blob && blob->isFile())
328                 files->append(static_cast<File*>(blob.get()));
329         }
330     }
331
332     return files.release();
333 }
334
335 void ClipboardChromium::setDragImage(CachedImage* image, Node* node, const IntPoint& loc)
336 {
337     if (policy() != ClipboardImageWritable && policy() != ClipboardWritable)
338         return;
339
340     if (m_dragImage)
341         m_dragImage->removeClient(this);
342     m_dragImage = image;
343     if (m_dragImage)
344         m_dragImage->addClient(this);
345
346     m_dragLoc = loc;
347     m_dragImageElement = node;
348 }
349
350 void ClipboardChromium::setDragImage(CachedImage* img, const IntPoint& loc)
351 {
352     setDragImage(img, 0, loc);
353 }
354
355 void ClipboardChromium::setDragImageElement(Node* node, const IntPoint& loc)
356 {
357     setDragImage(0, node, loc);
358 }
359
360 DragImageRef ClipboardChromium::createDragImage(IntPoint& loc) const
361 {
362     DragImageRef result = 0;
363     if (m_dragImageElement) {
364         if (m_frame) {
365             result = m_frame->nodeImage(m_dragImageElement.get());
366             loc = m_dragLoc;
367         }
368     } else if (m_dragImage) {
369         result = createDragImageFromImage(m_dragImage->image());
370         loc = m_dragLoc;
371     }
372     return result;
373 }
374
375 static CachedImage* getCachedImage(Element* element)
376 {
377     // Attempt to pull CachedImage from element
378     ASSERT(element);
379     RenderObject* renderer = element->renderer();
380     if (!renderer || !renderer->isImage())
381         return 0;
382
383     RenderImage* image = toRenderImage(renderer);
384     if (image->cachedImage() && !image->cachedImage()->errorOccurred())
385         return image->cachedImage();
386
387     return 0;
388 }
389
390 static void writeImageToDataObject(ChromiumDataObject* dataObject, Element* element,
391                                    const KURL& url)
392 {
393     // Shove image data into a DataObject for use as a file
394     CachedImage* cachedImage = getCachedImage(element);
395     if (!cachedImage || !cachedImage->imageForRenderer(element->renderer()) || !cachedImage->isLoaded())
396         return;
397
398     SharedBuffer* imageBuffer = cachedImage->imageForRenderer(element->renderer())->data();
399     if (!imageBuffer || !imageBuffer->size())
400         return;
401
402     // Determine the filename for the file contents of the image.
403     String filename = cachedImage->response().suggestedFilename();
404     if (filename.isEmpty())
405         filename = url.lastPathComponent();
406     if (filename.isEmpty())
407         filename = element->getAttribute(altAttr);
408     else {
409         // Strip any existing extension. Assume that alt text is usually not a filename.
410         int extensionIndex = filename.reverseFind('.');
411         if (extensionIndex != -1)
412             filename.truncate(extensionIndex);
413     }
414
415     String extension = MIMETypeRegistry::getPreferredExtensionForMIMEType(
416         cachedImage->response().mimeType());
417     extension = extension.isEmpty() ? emptyString() : "." + extension;
418
419     ClipboardChromium::validateFilename(filename, extension);
420
421     dataObject->addSharedBuffer(filename + extension, imageBuffer);
422 }
423
424 void ClipboardChromium::declareAndWriteDragImage(Element* element, const KURL& url, const String& title, Frame* frame)
425 {
426     if (!m_dataObject)
427         return;
428
429     m_dataObject->setURLAndTitle(url, title);
430
431     // Write the bytes in the image to the file format.
432     writeImageToDataObject(m_dataObject.get(), element, url);
433
434     // Put img tag on the clipboard referencing the image
435     m_dataObject->setData(mimeTypeTextHTML, createMarkup(element, IncludeNode, 0, ResolveAllURLs));
436 }
437
438 void ClipboardChromium::writeURL(const KURL& url, const String& title, Frame*)
439 {
440     if (!m_dataObject)
441         return;
442     ASSERT(!url.isEmpty());
443
444     m_dataObject->setURLAndTitle(url, title);
445
446     // The URL can also be used as plain text.
447     m_dataObject->setData(mimeTypeTextPlain, url.string());
448
449     // The URL can also be used as an HTML fragment.
450     m_dataObject->setHTMLAndBaseURL(urlToMarkup(url, title), url);
451 }
452
453 void ClipboardChromium::writeRange(Range* selectedRange, Frame* frame)
454 {
455     ASSERT(selectedRange);
456     if (!m_dataObject)
457          return;
458
459     m_dataObject->setHTMLAndBaseURL(createMarkup(selectedRange, 0, AnnotateForInterchange, false, ResolveNonLocalURLs), frame->document()->url());
460
461     String str = frame->editor()->selectedText();
462 #if OS(WINDOWS)
463     replaceNewlinesWithWindowsStyleNewlines(str);
464 #endif
465     replaceNBSPWithSpace(str);
466     m_dataObject->setData(mimeTypeTextPlain, str);
467 }
468
469 void ClipboardChromium::writePlainText(const String& text)
470 {
471     if (!m_dataObject)
472         return;
473
474     String str = text;
475 #if OS(WINDOWS)
476     replaceNewlinesWithWindowsStyleNewlines(str);
477 #endif
478     replaceNBSPWithSpace(str);
479
480     m_dataObject->setData(mimeTypeTextPlain, str);
481 }
482
483 bool ClipboardChromium::hasData()
484 {
485     ASSERT(isForDragAndDrop());
486
487     return m_dataObject->items()->length() > 0;
488 }
489
490 #if ENABLE(DATA_TRANSFER_ITEMS)
491 PassRefPtr<DataTransferItemList> ClipboardChromium::items()
492 {
493     // FIXME: According to the spec, we are supposed to return the same collection of items each
494     // time. We now return a wrapper that always wraps the *same* set of items, so JS shouldn't be
495     // able to tell, but we probably still want to fix this.
496     return DataTransferItemListPolicyWrapper::create(this, m_dataObject->items());
497 }
498 #endif
499
500 } // namespace WebCore