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