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