Data store should be readable in dragstart/copy/cut events
[WebKit-https.git] / Source / WebCore / platform / gtk / ClipboardGtk.cpp
1 /*
2  *  This library is free software; you can redistribute it and/or
3  *  modify it under the terms of the GNU Lesser General Public
4  *  License as published by the Free Software Foundation; either
5  *  version 2 of the License, or (at your option) any later version.
6  *
7  *  This library is distributed in the hope that it will be useful,
8  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10  *  Lesser General Public License for more details.
11  *
12  *  You should have received a copy of the GNU Lesser General Public
13  *  License along with this library; if not, write to the Free Software
14  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
15  */
16
17 #include "config.h"
18 #include "ClipboardGtk.h"
19
20 #include "CachedImage.h"
21 #include "DragData.h"
22 #include "Editor.h"
23 #include "Element.h"
24 #include "FileList.h"
25 #include "Frame.h"
26 #include "HTMLNames.h"
27 #include "Image.h"
28 #include "NotImplemented.h"
29 #include "Pasteboard.h"
30 #include "PasteboardHelper.h"
31 #include "RenderImage.h"
32 #include "ScriptExecutionContext.h"
33 #include "markup.h"
34 #include <wtf/text/CString.h>
35 #include <wtf/text/StringHash.h>
36 #include <gtk/gtk.h>
37
38 namespace WebCore {
39
40 enum ClipboardDataType {
41     ClipboardDataTypeText,
42     ClipboardDataTypeMarkup,
43     ClipboardDataTypeURIList,
44     ClipboardDataTypeURL,
45     ClipboardDataTypeImage,
46     ClipboardDataTypeUnknown
47 };
48
49 PassRefPtr<Clipboard> Editor::newGeneralClipboard(ClipboardAccessPolicy policy, Frame* frame)
50 {
51     return ClipboardGtk::create(policy, gtk_clipboard_get_for_display(gdk_display_get_default(), GDK_SELECTION_CLIPBOARD), frame);
52 }
53
54 PassRefPtr<Clipboard> Clipboard::create(ClipboardAccessPolicy policy, DragData* dragData, Frame* frame)
55 {
56     return ClipboardGtk::create(policy, dragData->platformData(), DragAndDrop, frame);
57 }
58
59 ClipboardGtk::ClipboardGtk(ClipboardAccessPolicy policy, GtkClipboard* clipboard, Frame* frame)
60     : Clipboard(policy, CopyAndPaste)
61     , m_dataObject(DataObjectGtk::forClipboard(clipboard))
62     , m_clipboard(clipboard)
63     , m_frame(frame)
64 {
65 }
66
67 ClipboardGtk::ClipboardGtk(ClipboardAccessPolicy policy, PassRefPtr<DataObjectGtk> dataObject, ClipboardType clipboardType, Frame* frame)
68     : Clipboard(policy, clipboardType)
69     , m_dataObject(dataObject)
70     , m_clipboard(0)
71     , m_frame(frame)
72 {
73 }
74
75 ClipboardGtk::~ClipboardGtk()
76 {
77     if (m_dragImage)
78         m_dragImage->removeClient(this);
79 }
80
81 static ClipboardDataType dataObjectTypeFromHTMLClipboardType(const String& rawType)
82 {
83     String type(rawType.stripWhiteSpace());
84
85     // Two special cases for IE compatibility
86     if (type == "Text" || type == "text")
87         return ClipboardDataTypeText;
88     if (type == "URL")
89         return ClipboardDataTypeURL;
90
91     // From the Mac port: Ignore any trailing charset - JS strings are
92     // Unicode, which encapsulates the charset issue.
93     if (type == "text/plain" || type.startsWith("text/plain;"))
94         return ClipboardDataTypeText;
95     if (type == "text/html" || type.startsWith("text/html;"))
96         return ClipboardDataTypeMarkup;
97     if (type == "Files" || type == "text/uri-list" || type.startsWith("text/uri-list;"))
98         return ClipboardDataTypeURIList;
99
100     // Not a known type, so just default to using the text portion.
101     return ClipboardDataTypeUnknown;
102 }
103
104 void ClipboardGtk::clearData(const String& typeString)
105 {
106     if (!canWriteData())
107         return;
108
109     ClipboardDataType type = dataObjectTypeFromHTMLClipboardType(typeString);
110     switch (type) {
111     case ClipboardDataTypeURIList:
112     case ClipboardDataTypeURL:
113         m_dataObject->clearURIList();
114         break;
115     case ClipboardDataTypeMarkup:
116         m_dataObject->clearMarkup();
117         break;
118     case ClipboardDataTypeText:
119         m_dataObject->clearText();
120         break;
121     case ClipboardDataTypeImage:
122         m_dataObject->clearImage();
123         break;
124     case ClipboardDataTypeUnknown:
125         m_dataObject->clearAll();
126         break;
127     }
128
129     if (m_clipboard)
130         PasteboardHelper::defaultPasteboardHelper()->writeClipboardContents(m_clipboard);
131 }
132
133
134 void ClipboardGtk::clearAllData()
135 {
136     if (!canWriteData())
137         return;
138
139     // We do not clear filenames. According to the spec: "The clearData() method
140     // does not affect whether any files were included in the drag, so the types
141     // attribute's list might still not be empty after calling clearData() (it would 
142     // still contain the "Files" string if any files were included in the drag)."
143     m_dataObject->clearAllExceptFilenames();
144
145     if (m_clipboard)
146         PasteboardHelper::defaultPasteboardHelper()->writeClipboardContents(m_clipboard);
147 }
148
149 String ClipboardGtk::getData(const String& typeString) const
150 {
151     if (!canReadData() || !m_dataObject)
152         return String();
153
154     if (m_clipboard)
155         PasteboardHelper::defaultPasteboardHelper()->getClipboardContents(m_clipboard);
156
157     ClipboardDataType type = dataObjectTypeFromHTMLClipboardType(typeString);
158     if (type == ClipboardDataTypeURIList)
159         return m_dataObject->uriList();
160     if (type == ClipboardDataTypeURL)
161         return m_dataObject->url();
162     if (type == ClipboardDataTypeMarkup)
163         return m_dataObject->markup();
164     if (type == ClipboardDataTypeText)
165         return m_dataObject->text();
166
167     return String();
168 }
169
170 bool ClipboardGtk::setData(const String& typeString, const String& data)
171 {
172     if (!canWriteData())
173         return false;
174
175     bool success = false;
176     ClipboardDataType type = dataObjectTypeFromHTMLClipboardType(typeString);
177     if (type == ClipboardDataTypeURIList || type == ClipboardDataTypeURL) {
178         m_dataObject->setURIList(data);
179         success = true;
180     } else if (type == ClipboardDataTypeMarkup) {
181         m_dataObject->setMarkup(data);
182         success = true;
183     } else if (type == ClipboardDataTypeText) {
184         m_dataObject->setText(data);
185         success = true;
186     }
187
188     return success;
189 }
190
191 ListHashSet<String> ClipboardGtk::types() const
192 {
193     if (!canReadTypes())
194         return ListHashSet<String>();
195
196     if (m_clipboard)
197         PasteboardHelper::defaultPasteboardHelper()->getClipboardContents(m_clipboard);
198
199     ListHashSet<String> types;
200     if (m_dataObject->hasText()) {
201         types.add("text/plain");
202         types.add("Text");
203         types.add("text");
204     }
205
206     if (m_dataObject->hasMarkup())
207         types.add("text/html");
208
209     if (m_dataObject->hasURIList()) {
210         types.add("text/uri-list");
211         types.add("URL");
212     }
213
214     if (m_dataObject->hasFilenames())
215         types.add("Files");
216
217     return types;
218 }
219
220 PassRefPtr<FileList> ClipboardGtk::files() const
221 {
222     if (!canReadData())
223         return FileList::create();
224
225     if (m_clipboard)
226         PasteboardHelper::defaultPasteboardHelper()->getClipboardContents(m_clipboard);
227
228     RefPtr<FileList> fileList = FileList::create();
229     const Vector<String>& filenames = m_dataObject->filenames();
230     for (size_t i = 0; i < filenames.size(); i++)
231         fileList->append(File::create(filenames[i], File::AllContentTypes));
232     return fileList.release();
233 }
234
235 void ClipboardGtk::setDragImage(CachedImage* image, const IntPoint& location)
236 {
237     setDragImage(image, 0, location);
238 }
239
240 void ClipboardGtk::setDragImageElement(Node* element, const IntPoint& location)
241 {
242     setDragImage(0, element, location);
243 }
244
245 void ClipboardGtk::setDragImage(CachedImage* image, Node* element, const IntPoint& location)
246 {
247     if (!canSetDragImage())
248         return;
249
250     if (m_dragImage)
251         m_dragImage->removeClient(this);
252     m_dragImage = image;
253     if (m_dragImage)
254         m_dragImage->addClient(this);
255
256     m_dragLoc = location;
257     m_dragImageElement = element;
258 }
259
260 DragImageRef ClipboardGtk::createDragImage(IntPoint& location) const
261 {
262     location = m_dragLoc;
263
264     if (m_dragImage)
265         return createDragImageFromImage(m_dragImage->image());
266     if (m_dragImageElement && m_frame)
267         return m_frame->nodeImage(m_dragImageElement.get());
268
269     return 0; // We do not have enough information to create a drag image, use the default icon.
270 }
271
272 static CachedImage* getCachedImage(Element* element)
273 {
274     // Attempt to pull CachedImage from element
275     ASSERT(element);
276     RenderObject* renderer = element->renderer();
277     if (!renderer || !renderer->isImage())
278         return 0;
279
280     RenderImage* image = static_cast<RenderImage*>(renderer);
281     if (image->cachedImage() && !image->cachedImage()->errorOccurred())
282         return image->cachedImage();
283
284     return 0;
285 }
286
287 void ClipboardGtk::declareAndWriteDragImage(Element* element, const KURL& url, const String& label, Frame* frame)
288 {
289     m_dataObject->setURL(url, label);
290     m_dataObject->setMarkup(createMarkup(element, IncludeNode, 0, ResolveAllURLs));
291
292     CachedImage* image = getCachedImage(element);
293     if (!image || !image->isLoaded())
294         return;
295
296     GRefPtr<GdkPixbuf> pixbuf = adoptGRef(image->imageForRenderer(element->renderer())->getGdkPixbuf());
297     if (!pixbuf)
298         return;
299
300     m_dataObject->setImage(pixbuf.get());
301 }
302
303 void ClipboardGtk::writeURL(const KURL& url, const String& label, Frame*)
304 {
305     m_dataObject->setURL(url, label);
306     if (m_clipboard)
307         PasteboardHelper::defaultPasteboardHelper()->writeClipboardContents(m_clipboard);
308 }
309
310 void ClipboardGtk::writeRange(Range* range, Frame* frame)
311 {
312     ASSERT(range);
313
314     m_dataObject->setText(frame->editor()->selectedText());
315     m_dataObject->setMarkup(createMarkup(range, 0, AnnotateForInterchange, false, ResolveNonLocalURLs));
316
317     if (m_clipboard)
318         PasteboardHelper::defaultPasteboardHelper()->writeClipboardContents(m_clipboard);
319 }
320
321 void ClipboardGtk::writePlainText(const String& text)
322 {
323     m_dataObject->setText(text);
324
325     if (m_clipboard)
326         PasteboardHelper::defaultPasteboardHelper()->writeClipboardContents(m_clipboard);
327 }
328
329 bool ClipboardGtk::hasData()
330 {
331     if (m_clipboard)
332         PasteboardHelper::defaultPasteboardHelper()->getClipboardContents(m_clipboard);
333
334     return m_dataObject->hasText() || m_dataObject->hasMarkup()
335         || m_dataObject->hasURIList() || m_dataObject->hasImage();
336 }
337
338 }