Optimize hasTagName when called on an HTMLElement
[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 "SVGElement.h"
38 #include "SVGNames.h"
39 #include "XLinkNames.h"
40 #include "markup.h"
41 #include <gtk/gtk.h>
42
43
44 namespace WebCore {
45
46 enum ClipboardDataType {
47     ClipboardDataTypeText,
48     ClipboardDataTypeMarkup,
49     ClipboardDataTypeURIList,
50     ClipboardDataTypeURL,
51     ClipboardDataTypeImage,
52     ClipboardDataTypeUnknown
53 };
54
55 PassOwnPtr<Pasteboard> Pasteboard::create(GtkClipboard* gtkClipboard)
56 {
57     return adoptPtr(new Pasteboard(gtkClipboard));
58 }
59
60 PassOwnPtr<Pasteboard> Pasteboard::create(PassRefPtr<DataObjectGtk> dataObject)
61 {
62     return adoptPtr(new Pasteboard(dataObject));
63 }
64
65 PassOwnPtr<Pasteboard> Pasteboard::createForCopyAndPaste()
66 {
67     return create(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
68 }
69
70 PassOwnPtr<Pasteboard> Pasteboard::createForGlobalSelection()
71 {
72     return create(gtk_clipboard_get(GDK_SELECTION_PRIMARY));
73 }
74
75 PassOwnPtr<Pasteboard> Pasteboard::createPrivate()
76 {
77     return create(DataObjectGtk::create());
78 }
79
80 #if ENABLE(DRAG_SUPPORT)
81 PassOwnPtr<Pasteboard> Pasteboard::createForDragAndDrop()
82 {
83     return create(DataObjectGtk::create());
84 }
85
86 PassOwnPtr<Pasteboard> Pasteboard::createForDragAndDrop(const DragData& dragData)
87 {
88     return create(dragData.platformData());
89 }
90 #endif
91
92 Pasteboard::Pasteboard(PassRefPtr<DataObjectGtk> dataObject)
93     : m_dataObject(dataObject)
94     , m_gtkClipboard(0)
95 {
96     ASSERT(m_dataObject);
97 }
98
99 Pasteboard::Pasteboard(GtkClipboard* gtkClipboard)
100     : m_dataObject(DataObjectGtk::forClipboard(gtkClipboard))
101     , m_gtkClipboard(gtkClipboard)
102 {
103     ASSERT(m_dataObject);
104 }
105
106 Pasteboard::~Pasteboard()
107 {
108 }
109
110 PassRefPtr<DataObjectGtk> Pasteboard::dataObject() const
111 {
112     return m_dataObject;
113 }
114
115 static ClipboardDataType dataObjectTypeFromHTMLClipboardType(const String& rawType)
116 {
117     String type(rawType.stripWhiteSpace());
118
119     // Two special cases for IE compatibility
120     if (type == "Text" || type == "text")
121         return ClipboardDataTypeText;
122     if (type == "URL")
123         return ClipboardDataTypeURL;
124
125     // From the Mac port: Ignore any trailing charset - JS strings are
126     // Unicode, which encapsulates the charset issue.
127     if (type == "text/plain" || type.startsWith("text/plain;"))
128         return ClipboardDataTypeText;
129     if (type == "text/html" || type.startsWith("text/html;"))
130         return ClipboardDataTypeMarkup;
131     if (type == "Files" || type == "text/uri-list" || type.startsWith("text/uri-list;"))
132         return ClipboardDataTypeURIList;
133
134     // Not a known type, so just default to using the text portion.
135     return ClipboardDataTypeUnknown;
136 }
137
138 bool Pasteboard::writeString(const String& type, const String& data)
139 {
140     switch (dataObjectTypeFromHTMLClipboardType(type)) {
141     case ClipboardDataTypeURIList:
142     case ClipboardDataTypeURL:
143         m_dataObject->setURIList(data);
144         return true;
145     case ClipboardDataTypeMarkup:
146         m_dataObject->setMarkup(data);
147         return true;
148     case ClipboardDataTypeText:
149         m_dataObject->setText(data);
150         return true;
151     case ClipboardDataTypeUnknown:
152         m_dataObject->setUnknownTypeData(type, data);
153         return true;
154     case ClipboardDataTypeImage:
155         break;
156     }
157
158     return false;
159 }
160
161 void Pasteboard::writeSelection(Range& selectedRange, bool canSmartCopyOrDelete, Frame& frame, ShouldSerializeSelectedTextForClipboard shouldSerializeSelectedTextForClipboard)
162 {
163     m_dataObject->clearAll();
164     m_dataObject->setText(shouldSerializeSelectedTextForClipboard == IncludeImageAltTextForClipboard ? frame.editor().selectedTextForClipboard() : frame.editor().selectedText());
165     m_dataObject->setMarkup(createMarkup(selectedRange, 0, AnnotateForInterchange, false, ResolveNonLocalURLs));
166
167     if (m_gtkClipboard)
168         PasteboardHelper::defaultPasteboardHelper()->writeClipboardContents(m_gtkClipboard, canSmartCopyOrDelete ? PasteboardHelper::IncludeSmartPaste : PasteboardHelper::DoNotIncludeSmartPaste);
169 }
170
171 void Pasteboard::writePlainText(const String& text, SmartReplaceOption smartReplaceOption)
172 {
173     m_dataObject->clearAll();
174     m_dataObject->setText(text);
175
176     if (m_gtkClipboard)
177         PasteboardHelper::defaultPasteboardHelper()->writeClipboardContents(m_gtkClipboard, (smartReplaceOption == CanSmartReplace) ? PasteboardHelper::IncludeSmartPaste : PasteboardHelper::DoNotIncludeSmartPaste);
178 }
179
180 void Pasteboard::write(const PasteboardURL& pasteboardURL)
181 {
182     ASSERT(!pasteboardURL.url.isEmpty());
183
184     m_dataObject->clearAll();
185     m_dataObject->setURL(pasteboardURL.url, pasteboardURL.title);
186
187     if (m_gtkClipboard)
188         PasteboardHelper::defaultPasteboardHelper()->writeClipboardContents(m_gtkClipboard);
189 }
190
191 static URL getURLForImageElement(Element& element)
192 {
193     // FIXME: Later this code should be shared with Chromium somehow. Chances are all platforms want it.
194     AtomicString urlString;
195     if (isHTMLImageElement(element) || isHTMLInputElement(element))
196         urlString = element.getAttribute(HTMLNames::srcAttr);
197     else if (element.hasTagName(SVGNames::imageTag))
198         urlString = element.getAttribute(XLinkNames::hrefAttr);
199     else if (element.hasTagName(HTMLNames::embedTag) || isHTMLObjectElement(element))
200         urlString = element.imageSourceURL();
201
202     return urlString.isEmpty() ? URL() : element.document().completeURL(stripLeadingAndTrailingHTMLSpaces(urlString));
203 }
204
205 void Pasteboard::writeImage(Element& element, const URL&, const String& title)
206 {
207     if (!(element.renderer() && element.renderer()->isRenderImage()))
208         return;
209
210     RenderImage* renderer = toRenderImage(element.renderer());
211     CachedImage* cachedImage = renderer->cachedImage();
212     if (!cachedImage || cachedImage->errorOccurred())
213         return;
214     Image* image = cachedImage->imageForRenderer(renderer);
215     ASSERT(image);
216
217     m_dataObject->clearAll();
218
219     URL url = getURLForImageElement(element);
220     if (!url.isEmpty()) {
221         m_dataObject->setURL(url, title);
222
223         m_dataObject->setMarkup(createMarkup(element, IncludeNode, 0, ResolveAllURLs));
224     }
225
226     GRefPtr<GdkPixbuf> pixbuf = adoptGRef(image->getGdkPixbuf());
227     m_dataObject->setImage(pixbuf.get());
228
229     if (m_gtkClipboard)
230         PasteboardHelper::defaultPasteboardHelper()->writeClipboardContents(m_gtkClipboard);
231 }
232
233 void Pasteboard::writePasteboard(const Pasteboard& sourcePasteboard)
234 {
235     RefPtr<DataObjectGtk> sourceDataObject = sourcePasteboard.dataObject();
236     m_dataObject->clearAll();
237
238     if (sourceDataObject->hasText())
239         m_dataObject->setText(sourceDataObject->text());
240     if (sourceDataObject->hasMarkup())
241         m_dataObject->setMarkup(sourceDataObject->markup());
242     if (sourceDataObject->hasURL())
243         m_dataObject->setURL(sourceDataObject->url(), sourceDataObject->urlLabel());
244     if (sourceDataObject->hasURIList())
245         m_dataObject->setURIList(sourceDataObject->uriList());
246     if (sourceDataObject->hasImage())
247         m_dataObject->setImage(sourceDataObject->image());
248     if (sourceDataObject->hasUnknownTypeData()) {
249         auto types = m_dataObject->unknownTypes();
250         auto end = types.end();
251         for (auto it = types.begin(); it != end; ++it)
252             m_dataObject->setUnknownTypeData(it->key, it->value);
253     }
254
255     if (m_gtkClipboard)
256         PasteboardHelper::defaultPasteboardHelper()->writeClipboardContents(m_gtkClipboard);
257 }
258
259 void Pasteboard::clear()
260 {
261     // We do not clear filenames. According to the spec: "The clearData() method
262     // does not affect whether any files were included in the drag, so the types
263     // attribute's list might still not be empty after calling clearData() (it would
264     // still contain the "Files" string if any files were included in the drag)."
265     m_dataObject->clearAllExceptFilenames();
266
267     if (m_gtkClipboard)
268         PasteboardHelper::defaultPasteboardHelper()->writeClipboardContents(m_gtkClipboard);
269 }
270
271 void Pasteboard::clear(const String& type)
272 {
273     switch (dataObjectTypeFromHTMLClipboardType(type)) {
274     case ClipboardDataTypeURIList:
275     case ClipboardDataTypeURL:
276         m_dataObject->clearURIList();
277         break;
278     case ClipboardDataTypeMarkup:
279         m_dataObject->clearMarkup();
280         break;
281     case ClipboardDataTypeText:
282         m_dataObject->clearText();
283         break;
284     case ClipboardDataTypeImage:
285         m_dataObject->clearImage();
286         break;
287     case ClipboardDataTypeUnknown:
288         m_dataObject->clearAll();
289         break;
290     }
291
292     if (m_gtkClipboard)
293         PasteboardHelper::defaultPasteboardHelper()->writeClipboardContents(m_gtkClipboard);
294 }
295
296 bool Pasteboard::canSmartReplace()
297 {
298     return m_gtkClipboard && PasteboardHelper::defaultPasteboardHelper()->clipboardContentSupportsSmartReplace(m_gtkClipboard);
299 }
300
301 #if ENABLE(DRAG_SUPPORT)
302 void Pasteboard::setDragImage(DragImageRef, const IntPoint&)
303 {
304 }
305 #endif
306
307 PassRefPtr<DocumentFragment> Pasteboard::documentFragment(Frame& frame, Range& context, bool allowPlainText, bool& chosePlainText)
308 {
309     if (m_gtkClipboard)
310         PasteboardHelper::defaultPasteboardHelper()->getClipboardContents(m_gtkClipboard);
311
312     chosePlainText = false;
313
314     if (m_dataObject->hasMarkup()) {
315         if (frame.document()) {
316             RefPtr<DocumentFragment> fragment = createFragmentFromMarkup(*frame.document(), m_dataObject->markup(), emptyString(), DisallowScriptingAndPluginContent);
317             if (fragment)
318                 return fragment.release();
319         }
320     }
321
322     if (!allowPlainText)
323         return 0;
324
325     if (m_dataObject->hasText()) {
326         chosePlainText = true;
327         RefPtr<DocumentFragment> fragment = createFragmentFromText(context, m_dataObject->text());
328         if (fragment)
329             return fragment.release();
330     }
331
332     return 0;
333 }
334
335 void Pasteboard::read(PasteboardPlainText& text)
336 {
337     if (m_gtkClipboard)
338         PasteboardHelper::defaultPasteboardHelper()->getClipboardContents(m_gtkClipboard);
339     text.text = m_dataObject->text();
340 }
341
342 bool Pasteboard::hasData()
343 {
344     if (m_gtkClipboard)
345         PasteboardHelper::defaultPasteboardHelper()->getClipboardContents(m_gtkClipboard);
346
347     return m_dataObject->hasText() || m_dataObject->hasMarkup() || m_dataObject->hasURIList() || m_dataObject->hasImage() || m_dataObject->hasUnknownTypeData();
348 }
349
350 Vector<String> Pasteboard::types()
351 {
352     if (m_gtkClipboard)
353         PasteboardHelper::defaultPasteboardHelper()->getClipboardContents(m_gtkClipboard);
354
355     Vector<String> types;
356     if (m_dataObject->hasText()) {
357         types.append(ASCIILiteral("text/plain"));
358         types.append(ASCIILiteral("Text"));
359         types.append(ASCIILiteral("text"));
360     }
361
362     if (m_dataObject->hasMarkup())
363         types.append(ASCIILiteral("text/html"));
364
365     if (m_dataObject->hasURIList()) {
366         types.append(ASCIILiteral("text/uri-list"));
367         types.append(ASCIILiteral("URL"));
368     }
369
370     if (m_dataObject->hasFilenames())
371         types.append(ASCIILiteral("Files"));
372
373     auto unknownTypes = m_dataObject->unknownTypes();
374     auto end = unknownTypes.end();
375     for (auto it = unknownTypes.begin(); it != end; ++it)
376         types.append(it->key);
377
378     return types;
379 }
380
381 String Pasteboard::readString(const String& type)
382 {
383     if (m_gtkClipboard)
384         PasteboardHelper::defaultPasteboardHelper()->getClipboardContents(m_gtkClipboard);
385
386     switch (dataObjectTypeFromHTMLClipboardType(type)) {
387     case ClipboardDataTypeURIList:
388         return m_dataObject->uriList();
389     case ClipboardDataTypeURL:
390         return m_dataObject->url();
391     case ClipboardDataTypeMarkup:
392         return m_dataObject->markup();
393     case ClipboardDataTypeText:
394         return m_dataObject->text();
395     case ClipboardDataTypeUnknown:
396         return m_dataObject->unknownTypeData(type);
397     case ClipboardDataTypeImage:
398         break;
399     }
400
401     return String();
402 }
403
404 Vector<String> Pasteboard::readFilenames()
405 {
406     if (m_gtkClipboard)
407         PasteboardHelper::defaultPasteboardHelper()->getClipboardContents(m_gtkClipboard);
408
409     return m_dataObject->filenames();
410 }
411
412 }