c392e057f972c0bd928eb69588a724cf289a2901
[WebKit-https.git] / Source / WebCore / platform / gtk / PasteboardGtk.cpp
1 /*
2  *  Copyright (C) 2007 Holger Hans Peter Freyther
3  *  Copyright (C) 2007 Alp Toker <alp@atoker.com>
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19
20 #include "config.h"
21 #include "Pasteboard.h"
22
23 #include "CachedImage.h"
24 #include "DataObjectGtk.h"
25 #include "DocumentFragment.h"
26 #include "DragData.h"
27 #include "Editor.h"
28 #include "Frame.h"
29 #include "HTMLImageElement.h"
30 #include "HTMLInputElement.h"
31 #include "HTMLNames.h"
32 #include "HTMLParserIdioms.h"
33 #include "Image.h"
34 #include "URL.h"
35 #include "PasteboardHelper.h"
36 #include "RenderImage.h"
37 #include "markup.h"
38 #include <gtk/gtk.h>
39
40 #if ENABLE(SVG)
41 #include "SVGNames.h"
42 #include "XLinkNames.h"
43 #endif
44
45 namespace WebCore {
46
47 enum ClipboardDataType {
48     ClipboardDataTypeText,
49     ClipboardDataTypeMarkup,
50     ClipboardDataTypeURIList,
51     ClipboardDataTypeURL,
52     ClipboardDataTypeImage,
53     ClipboardDataTypeUnknown
54 };
55
56 PassOwnPtr<Pasteboard> Pasteboard::create(GtkClipboard* gtkClipboard)
57 {
58     return adoptPtr(new Pasteboard(gtkClipboard));
59 }
60
61 PassOwnPtr<Pasteboard> Pasteboard::create(PassRefPtr<DataObjectGtk> dataObject)
62 {
63     return adoptPtr(new Pasteboard(dataObject));
64 }
65
66 PassOwnPtr<Pasteboard> Pasteboard::createForCopyAndPaste()
67 {
68     return create(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
69 }
70
71 PassOwnPtr<Pasteboard> Pasteboard::createForGlobalSelection()
72 {
73     return create(gtk_clipboard_get(GDK_SELECTION_PRIMARY));
74 }
75
76 PassOwnPtr<Pasteboard> Pasteboard::createPrivate()
77 {
78     return create(DataObjectGtk::create());
79 }
80
81 #if ENABLE(DRAG_SUPPORT)
82 PassOwnPtr<Pasteboard> Pasteboard::createForDragAndDrop()
83 {
84     return create(DataObjectGtk::create());
85 }
86
87 PassOwnPtr<Pasteboard> Pasteboard::createForDragAndDrop(const DragData& dragData)
88 {
89     return create(dragData.platformData());
90 }
91 #endif
92
93 Pasteboard::Pasteboard(PassRefPtr<DataObjectGtk> dataObject)
94     : m_dataObject(dataObject)
95     , m_gtkClipboard(0)
96 {
97     ASSERT(m_dataObject);
98 }
99
100 Pasteboard::Pasteboard(GtkClipboard* gtkClipboard)
101     : m_dataObject(DataObjectGtk::forClipboard(gtkClipboard))
102     , m_gtkClipboard(gtkClipboard)
103 {
104     ASSERT(m_dataObject);
105 }
106
107 Pasteboard::~Pasteboard()
108 {
109 }
110
111 PassRefPtr<DataObjectGtk> Pasteboard::dataObject() const
112 {
113     return m_dataObject;
114 }
115
116 static ClipboardDataType dataObjectTypeFromHTMLClipboardType(const String& rawType)
117 {
118     String type(rawType.stripWhiteSpace());
119
120     // Two special cases for IE compatibility
121     if (type == "Text" || type == "text")
122         return ClipboardDataTypeText;
123     if (type == "URL")
124         return ClipboardDataTypeURL;
125
126     // From the Mac port: Ignore any trailing charset - JS strings are
127     // Unicode, which encapsulates the charset issue.
128     if (type == "text/plain" || type.startsWith("text/plain;"))
129         return ClipboardDataTypeText;
130     if (type == "text/html" || type.startsWith("text/html;"))
131         return ClipboardDataTypeMarkup;
132     if (type == "Files" || type == "text/uri-list" || type.startsWith("text/uri-list;"))
133         return ClipboardDataTypeURIList;
134
135     // Not a known type, so just default to using the text portion.
136     return ClipboardDataTypeUnknown;
137 }
138
139 bool Pasteboard::writeString(const String& type, const String& data)
140 {
141     switch (dataObjectTypeFromHTMLClipboardType(type)) {
142     case ClipboardDataTypeURIList:
143     case ClipboardDataTypeURL:
144         m_dataObject->setURIList(data);
145         return true;
146     case ClipboardDataTypeMarkup:
147         m_dataObject->setMarkup(data);
148         return true;
149     case ClipboardDataTypeText:
150         m_dataObject->setText(data);
151         return true;
152     case ClipboardDataTypeImage:
153     case ClipboardDataTypeUnknown:
154         break;
155     }
156
157     return false;
158 }
159
160 void Pasteboard::writeSelection(Range* selectedRange, bool canSmartCopyOrDelete, Frame* frame, ShouldSerializeSelectedTextForClipboard shouldSerializeSelectedTextForClipboard)
161 {
162     m_dataObject->clearAll();
163     m_dataObject->setText(shouldSerializeSelectedTextForClipboard == IncludeImageAltTextForClipboard ? frame->editor().selectedTextForClipboard() : frame->editor().selectedText());
164     m_dataObject->setMarkup(createMarkup(selectedRange, 0, AnnotateForInterchange, false, ResolveNonLocalURLs));
165
166     if (m_gtkClipboard)
167         PasteboardHelper::defaultPasteboardHelper()->writeClipboardContents(m_gtkClipboard, canSmartCopyOrDelete ? PasteboardHelper::IncludeSmartPaste : PasteboardHelper::DoNotIncludeSmartPaste);
168 }
169
170 void Pasteboard::writePlainText(const String& text, SmartReplaceOption smartReplaceOption)
171 {
172     m_dataObject->clearAll();
173     m_dataObject->setText(text);
174
175     if (m_gtkClipboard)
176         PasteboardHelper::defaultPasteboardHelper()->writeClipboardContents(m_gtkClipboard, (smartReplaceOption == CanSmartReplace) ? PasteboardHelper::IncludeSmartPaste : PasteboardHelper::DoNotIncludeSmartPaste);
177 }
178
179 void Pasteboard::write(const PasteboardURL& pasteboardURL)
180 {
181     ASSERT(!pasteboardURL.url.isEmpty());
182
183     m_dataObject->clearAll();
184     m_dataObject->setURL(pasteboardURL.url, pasteboardURL.title);
185
186     if (m_gtkClipboard)
187         PasteboardHelper::defaultPasteboardHelper()->writeClipboardContents(m_gtkClipboard);
188 }
189
190 static URL getURLForImageNode(Node* node)
191 {
192     // FIXME: Later this code should be shared with Chromium somehow. Chances are all platforms want it.
193     AtomicString urlString;
194     if (isHTMLImageElement(node) || isHTMLInputElement(node))
195         urlString = toElement(node)->getAttribute(HTMLNames::srcAttr);
196 #if ENABLE(SVG)
197     else if (node->hasTagName(SVGNames::imageTag))
198         urlString = toElement(node)->getAttribute(XLinkNames::hrefAttr);
199 #endif
200     else if (node->hasTagName(HTMLNames::embedTag) || isHTMLObjectElement(node)) {
201         Element* element = toElement(node);
202         urlString = element->imageSourceURL();
203     }
204     return urlString.isEmpty() ? URL() : node->document().completeURL(stripLeadingAndTrailingHTMLSpaces(urlString));
205 }
206
207 void Pasteboard::writeImage(Node* node, const URL&, const String& title)
208 {
209     ASSERT(node);
210
211     if (!(node->renderer() && node->renderer()->isImage()))
212         return;
213
214     RenderImage* renderer = toRenderImage(node->renderer());
215     CachedImage* cachedImage = renderer->cachedImage();
216     if (!cachedImage || cachedImage->errorOccurred())
217         return;
218     Image* image = cachedImage->imageForRenderer(renderer);
219     ASSERT(image);
220
221     m_dataObject->clearAll();
222
223     URL url = getURLForImageNode(node);
224     if (!url.isEmpty()) {
225         m_dataObject->setURL(url, title);
226
227         m_dataObject->setMarkup(createMarkup(toElement(node), IncludeNode, 0, ResolveAllURLs));
228     }
229
230     GRefPtr<GdkPixbuf> pixbuf = adoptGRef(image->getGdkPixbuf());
231     m_dataObject->setImage(pixbuf.get());
232
233     if (m_gtkClipboard)
234         PasteboardHelper::defaultPasteboardHelper()->writeClipboardContents(m_gtkClipboard);
235 }
236
237 void Pasteboard::writePasteboard(const Pasteboard& sourcePasteboard)
238 {
239     RefPtr<DataObjectGtk> sourceDataObject = sourcePasteboard.dataObject();
240     m_dataObject->clearAll();
241
242     if (sourceDataObject->hasText())
243         m_dataObject->setText(sourceDataObject->text());
244     if (sourceDataObject->hasMarkup())
245         m_dataObject->setMarkup(sourceDataObject->markup());
246     if (sourceDataObject->hasURL())
247         m_dataObject->setURL(sourceDataObject->url(), sourceDataObject->urlLabel());
248     if (sourceDataObject->hasURIList())
249         m_dataObject->setURIList(sourceDataObject->uriList());
250     if (sourceDataObject->hasImage())
251         m_dataObject->setImage(sourceDataObject->image());
252
253     if (m_gtkClipboard)
254         PasteboardHelper::defaultPasteboardHelper()->writeClipboardContents(m_gtkClipboard);
255 }
256
257 void Pasteboard::clear()
258 {
259     // We do not clear filenames. According to the spec: "The clearData() method
260     // does not affect whether any files were included in the drag, so the types
261     // attribute's list might still not be empty after calling clearData() (it would
262     // still contain the "Files" string if any files were included in the drag)."
263     m_dataObject->clearAllExceptFilenames();
264
265     if (m_gtkClipboard)
266         PasteboardHelper::defaultPasteboardHelper()->writeClipboardContents(m_gtkClipboard);
267 }
268
269 void Pasteboard::clear(const String& type)
270 {
271     switch (dataObjectTypeFromHTMLClipboardType(type)) {
272     case ClipboardDataTypeURIList:
273     case ClipboardDataTypeURL:
274         m_dataObject->clearURIList();
275         break;
276     case ClipboardDataTypeMarkup:
277         m_dataObject->clearMarkup();
278         break;
279     case ClipboardDataTypeText:
280         m_dataObject->clearText();
281         break;
282     case ClipboardDataTypeImage:
283         m_dataObject->clearImage();
284         break;
285     case ClipboardDataTypeUnknown:
286         m_dataObject->clearAll();
287         break;
288     }
289
290     if (m_gtkClipboard)
291         PasteboardHelper::defaultPasteboardHelper()->writeClipboardContents(m_gtkClipboard);
292 }
293
294 bool Pasteboard::canSmartReplace()
295 {
296     return m_gtkClipboard && PasteboardHelper::defaultPasteboardHelper()->clipboardContentSupportsSmartReplace(m_gtkClipboard);
297 }
298
299 #if ENABLE(DRAG_SUPPORT)
300 void Pasteboard::setDragImage(DragImageRef, const IntPoint& hotSpot)
301 {
302 }
303 #endif
304
305 PassRefPtr<DocumentFragment> Pasteboard::documentFragment(Frame* frame, PassRefPtr<Range> context,
306                                                           bool allowPlainText, bool& chosePlainText)
307 {
308     if (m_gtkClipboard)
309         PasteboardHelper::defaultPasteboardHelper()->getClipboardContents(m_gtkClipboard);
310
311     chosePlainText = false;
312
313     if (m_dataObject->hasMarkup()) {
314         RefPtr<DocumentFragment> fragment = createFragmentFromMarkup(frame->document(), m_dataObject->markup(), emptyString(), DisallowScriptingAndPluginContent);
315         if (fragment)
316             return fragment.release();
317     }
318
319     if (!allowPlainText)
320         return 0;
321
322     if (m_dataObject->hasText()) {
323         chosePlainText = true;
324         RefPtr<DocumentFragment> fragment = createFragmentFromText(context.get(), m_dataObject->text());
325         if (fragment)
326             return fragment.release();
327     }
328
329     return 0;
330 }
331
332 void Pasteboard::read(PasteboardPlainText& text)
333 {
334     if (m_gtkClipboard)
335         PasteboardHelper::defaultPasteboardHelper()->getClipboardContents(m_gtkClipboard);
336     text.text = m_dataObject->text();
337 }
338
339 bool Pasteboard::hasData()
340 {
341     if (m_gtkClipboard)
342         PasteboardHelper::defaultPasteboardHelper()->getClipboardContents(m_gtkClipboard);
343
344     return m_dataObject->hasText() || m_dataObject->hasMarkup() || m_dataObject->hasURIList() || m_dataObject->hasImage();
345 }
346
347 Vector<String> Pasteboard::types()
348 {
349     if (m_gtkClipboard)
350         PasteboardHelper::defaultPasteboardHelper()->getClipboardContents(m_gtkClipboard);
351
352     Vector<String> types;
353     if (m_dataObject->hasText()) {
354         types.append(ASCIILiteral("text/plain"));
355         types.append(ASCIILiteral("Text"));
356         types.append(ASCIILiteral("text"));
357     }
358
359     if (m_dataObject->hasMarkup())
360         types.append(ASCIILiteral("text/html"));
361
362     if (m_dataObject->hasURIList()) {
363         types.append(ASCIILiteral("text/uri-list"));
364         types.append(ASCIILiteral("URL"));
365     }
366
367     if (m_dataObject->hasFilenames())
368         types.append(ASCIILiteral("Files"));
369
370     return types;
371 }
372
373 String Pasteboard::readString(const String& type)
374 {
375     if (m_gtkClipboard)
376         PasteboardHelper::defaultPasteboardHelper()->getClipboardContents(m_gtkClipboard);
377
378     switch (dataObjectTypeFromHTMLClipboardType(type)) {
379     case ClipboardDataTypeURIList:
380         return m_dataObject->uriList();
381     case ClipboardDataTypeURL:
382         return m_dataObject->url();
383     case ClipboardDataTypeMarkup:
384         return m_dataObject->markup();
385     case ClipboardDataTypeText:
386         return m_dataObject->text();
387     case ClipboardDataTypeImage:
388     case ClipboardDataTypeUnknown:
389         break;
390     }
391
392     return String();
393 }
394
395 Vector<String> Pasteboard::readFilenames()
396 {
397     if (m_gtkClipboard)
398         PasteboardHelper::defaultPasteboardHelper()->getClipboardContents(m_gtkClipboard);
399
400     return m_dataObject->filenames();
401 }
402
403 }