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