• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 Martin Robinson <mrobinson@webkit.org>
3  * Copyright (C) Igalia S.L.
4  * All rights reserved.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  */
22 #include "config.h"
23 #include "PasteboardHelper.h"
24 
25 #include "Chrome.h"
26 #include "DataObjectGtk.h"
27 #include "Frame.h"
28 #include "GtkVersioning.h"
29 #include "Page.h"
30 #include "Pasteboard.h"
31 #include "TextResourceDecoder.h"
32 #include <gtk/gtk.h>
33 #include <wtf/gobject/GOwnPtr.h>
34 
35 namespace WebCore {
36 
37 static GdkAtom textPlainAtom;
38 static GdkAtom markupAtom;
39 static GdkAtom netscapeURLAtom;
40 static GdkAtom uriListAtom;
41 static String gMarkupPrefix;
42 
removeMarkupPrefix(String & markup)43 static void removeMarkupPrefix(String& markup)
44 {
45 
46     // The markup prefix is not harmful, but we remove it from the string anyway, so that
47     // we can have consistent results with other ports during the layout tests.
48     if (markup.startsWith(gMarkupPrefix))
49         markup.remove(0, gMarkupPrefix.length());
50 }
51 
initGdkAtoms()52 static void initGdkAtoms()
53 {
54     static gboolean initialized = FALSE;
55 
56     if (initialized)
57         return;
58 
59     initialized = TRUE;
60 
61     textPlainAtom = gdk_atom_intern("text/plain;charset=utf-8", FALSE);
62     markupAtom = gdk_atom_intern("text/html", FALSE);
63     netscapeURLAtom = gdk_atom_intern("_NETSCAPE_URL", FALSE);
64     uriListAtom = gdk_atom_intern("text/uri-list", FALSE);
65     gMarkupPrefix = "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">";
66 }
67 
PasteboardHelper()68 PasteboardHelper::PasteboardHelper()
69     : m_targetList(gtk_target_list_new(0, 0))
70 {
71     initGdkAtoms();
72 }
73 
~PasteboardHelper()74 PasteboardHelper::~PasteboardHelper()
75 {
76     gtk_target_list_unref(m_targetList);
77 }
78 
initializeTargetList()79 void PasteboardHelper::initializeTargetList()
80 {
81     gtk_target_list_add_text_targets(m_targetList, getIdForTargetType(TargetTypeText));
82     gtk_target_list_add(m_targetList, markupAtom, 0, getIdForTargetType(TargetTypeMarkup));
83     gtk_target_list_add_uri_targets(m_targetList, getIdForTargetType(TargetTypeURIList));
84     gtk_target_list_add(m_targetList, netscapeURLAtom, 0, getIdForTargetType(TargetTypeNetscapeURL));
85     gtk_target_list_add_image_targets(m_targetList, getIdForTargetType(TargetTypeImage), TRUE);
86 }
87 
widgetFromFrame(Frame * frame)88 static inline GtkWidget* widgetFromFrame(Frame* frame)
89 {
90     ASSERT(frame);
91     Page* page = frame->page();
92     ASSERT(page);
93     Chrome* chrome = page->chrome();
94     ASSERT(chrome);
95     PlatformPageClient client = chrome->platformPageClient();
96     ASSERT(client);
97     return client;
98 }
99 
getCurrentClipboard(Frame * frame)100 GtkClipboard* PasteboardHelper::getCurrentClipboard(Frame* frame)
101 {
102     if (usePrimarySelectionClipboard(widgetFromFrame(frame)))
103         return getPrimarySelectionClipboard(frame);
104     return getClipboard(frame);
105 }
106 
getClipboard(Frame * frame) const107 GtkClipboard* PasteboardHelper::getClipboard(Frame* frame) const
108 {
109     return gtk_widget_get_clipboard(widgetFromFrame(frame), GDK_SELECTION_CLIPBOARD);
110 }
111 
getPrimarySelectionClipboard(Frame * frame) const112 GtkClipboard* PasteboardHelper::getPrimarySelectionClipboard(Frame* frame) const
113 {
114     return gtk_widget_get_clipboard(widgetFromFrame(frame), GDK_SELECTION_PRIMARY);
115 }
116 
targetList() const117 GtkTargetList* PasteboardHelper::targetList() const
118 {
119     return m_targetList;
120 }
121 
selectionDataToUTF8String(GtkSelectionData * data)122 static String selectionDataToUTF8String(GtkSelectionData* data)
123 {
124     // g_strndup guards against selection data that is not null-terminated.
125     GOwnPtr<gchar> markupString(g_strndup(reinterpret_cast<const char*>(gtk_selection_data_get_data(data)), gtk_selection_data_get_length(data)));
126     return String::fromUTF8(markupString.get());
127 }
128 
getClipboardContents(GtkClipboard * clipboard)129 void PasteboardHelper::getClipboardContents(GtkClipboard* clipboard)
130 {
131     DataObjectGtk* dataObject = DataObjectGtk::forClipboard(clipboard);
132     ASSERT(dataObject);
133 
134     if (gtk_clipboard_wait_is_text_available(clipboard)) {
135         GOwnPtr<gchar> textData(gtk_clipboard_wait_for_text(clipboard));
136         if (textData)
137             dataObject->setText(String::fromUTF8(textData.get()));
138     }
139 
140     if (gtk_clipboard_wait_is_target_available(clipboard, markupAtom)) {
141         if (GtkSelectionData* data = gtk_clipboard_wait_for_contents(clipboard, markupAtom)) {
142             String markup(selectionDataToUTF8String(data));
143             removeMarkupPrefix(markup);
144             dataObject->setMarkup(markup);
145             gtk_selection_data_free(data);
146         }
147     }
148 
149     if (gtk_clipboard_wait_is_target_available(clipboard, uriListAtom)) {
150         if (GtkSelectionData* data = gtk_clipboard_wait_for_contents(clipboard, uriListAtom)) {
151             dataObject->setURIList(selectionDataToUTF8String(data));
152             gtk_selection_data_free(data);
153         }
154     }
155 }
156 
fillSelectionData(GtkSelectionData * selectionData,guint info,DataObjectGtk * dataObject)157 void PasteboardHelper::fillSelectionData(GtkSelectionData* selectionData, guint info, DataObjectGtk* dataObject)
158 {
159     if (info == getIdForTargetType(TargetTypeText))
160         gtk_selection_data_set_text(selectionData, dataObject->text().utf8().data(), -1);
161 
162     else if (info == getIdForTargetType(TargetTypeMarkup)) {
163         // Some Linux applications refuse to accept pasted markup unless it is
164         // prefixed by a content-type meta tag.
165         CString markup = (gMarkupPrefix + dataObject->markup()).utf8();
166         gtk_selection_data_set(selectionData, markupAtom, 8,
167             reinterpret_cast<const guchar*>(markup.data()), markup.length() + 1);
168 
169     } else if (info == getIdForTargetType(TargetTypeURIList)) {
170         CString uriList = dataObject->uriList().utf8();
171         gtk_selection_data_set(selectionData, uriListAtom, 8,
172             reinterpret_cast<const guchar*>(uriList.data()), uriList.length() + 1);
173 
174     } else if (info == getIdForTargetType(TargetTypeNetscapeURL) && dataObject->hasURL()) {
175         String url(dataObject->url());
176         String result(url);
177         result.append("\n");
178 
179         if (dataObject->hasText())
180             result.append(dataObject->text());
181         else
182             result.append(url);
183 
184         GOwnPtr<gchar> resultData(g_strdup(result.utf8().data()));
185         gtk_selection_data_set(selectionData, netscapeURLAtom, 8,
186             reinterpret_cast<const guchar*>(resultData.get()), strlen(resultData.get()) + 1);
187 
188     } else if (info == getIdForTargetType(TargetTypeImage))
189         gtk_selection_data_set_pixbuf(selectionData, dataObject->image());
190 }
191 
targetListForDataObject(DataObjectGtk * dataObject)192 GtkTargetList* PasteboardHelper::targetListForDataObject(DataObjectGtk* dataObject)
193 {
194     GtkTargetList* list = gtk_target_list_new(0, 0);
195 
196     if (dataObject->hasText())
197         gtk_target_list_add_text_targets(list, getIdForTargetType(TargetTypeText));
198 
199     if (dataObject->hasMarkup())
200         gtk_target_list_add(list, markupAtom, 0, getIdForTargetType(TargetTypeMarkup));
201 
202     if (dataObject->hasURIList()) {
203         gtk_target_list_add_uri_targets(list, getIdForTargetType(TargetTypeURIList));
204         gtk_target_list_add(list, netscapeURLAtom, 0, getIdForTargetType(TargetTypeNetscapeURL));
205     }
206 
207     if (dataObject->hasImage())
208         gtk_target_list_add_image_targets(list, getIdForTargetType(TargetTypeImage), TRUE);
209 
210     return list;
211 }
212 
fillDataObjectFromDropData(GtkSelectionData * data,guint info,DataObjectGtk * dataObject)213 void PasteboardHelper::fillDataObjectFromDropData(GtkSelectionData* data, guint info, DataObjectGtk* dataObject)
214 {
215     if (!gtk_selection_data_get_data(data))
216         return;
217 
218     GdkAtom target = gtk_selection_data_get_target(data);
219     if (target == textPlainAtom)
220         dataObject->setText(selectionDataToUTF8String(data));
221     else if (target == markupAtom) {
222         String markup(selectionDataToUTF8String(data));
223         removeMarkupPrefix(markup);
224         dataObject->setMarkup(markup);
225     } else if (target == uriListAtom) {
226         dataObject->setURIList(selectionDataToUTF8String(data));
227     } else if (target == netscapeURLAtom) {
228         String urlWithLabel(selectionDataToUTF8String(data));
229         Vector<String> pieces;
230         urlWithLabel.split("\n", pieces);
231 
232         // Give preference to text/uri-list here, as it can hold more
233         // than one URI but still take  the label if there is one.
234         if (!dataObject->hasURIList())
235             dataObject->setURIList(pieces[0]);
236         if (pieces.size() > 1)
237             dataObject->setText(pieces[1]);
238     }
239 }
240 
dropAtomsForContext(GtkWidget * widget,GdkDragContext * context)241 Vector<GdkAtom> PasteboardHelper::dropAtomsForContext(GtkWidget* widget, GdkDragContext* context)
242 {
243     // Always search for these common atoms.
244     Vector<GdkAtom> dropAtoms;
245     dropAtoms.append(textPlainAtom);
246     dropAtoms.append(markupAtom);
247     dropAtoms.append(uriListAtom);
248     dropAtoms.append(netscapeURLAtom);
249 
250     // For images, try to find the most applicable image type.
251     GRefPtr<GtkTargetList> list(gtk_target_list_new(0, 0));
252     gtk_target_list_add_image_targets(list.get(), getIdForTargetType(TargetTypeImage), TRUE);
253     GdkAtom atom = gtk_drag_dest_find_target(widget, context, list.get());
254     if (atom != GDK_NONE)
255         dropAtoms.append(atom);
256 
257     return dropAtoms;
258 }
259 
260 static DataObjectGtk* settingClipboardDataObject = 0;
261 
getClipboardContentsCallback(GtkClipboard * clipboard,GtkSelectionData * selectionData,guint info,gpointer data)262 static void getClipboardContentsCallback(GtkClipboard* clipboard, GtkSelectionData *selectionData, guint info, gpointer data)
263 {
264     DataObjectGtk* dataObject = DataObjectGtk::forClipboard(clipboard);
265     ASSERT(dataObject);
266     Pasteboard::generalPasteboard()->helper()->fillSelectionData(selectionData, info, dataObject);
267 }
268 
clearClipboardContentsCallback(GtkClipboard * clipboard,gpointer data)269 static void clearClipboardContentsCallback(GtkClipboard* clipboard, gpointer data)
270 {
271     DataObjectGtk* dataObject = DataObjectGtk::forClipboard(clipboard);
272     ASSERT(dataObject);
273 
274     // Only clear the DataObject for this clipboard if we are not currently setting it.
275     if (dataObject != settingClipboardDataObject)
276         dataObject->clear();
277 
278     if (!data)
279         return;
280 
281     GClosure* callback = static_cast<GClosure*>(data);
282     GValue firstArgument = {0, {{0}}};
283     g_value_init(&firstArgument, G_TYPE_POINTER);
284     g_value_set_pointer(&firstArgument, clipboard);
285     g_closure_invoke(callback, 0, 1, &firstArgument, 0);
286     g_closure_unref(callback);
287 }
288 
writeClipboardContents(GtkClipboard * clipboard,GClosure * callback)289 void PasteboardHelper::writeClipboardContents(GtkClipboard* clipboard, GClosure* callback)
290 {
291     DataObjectGtk* dataObject = DataObjectGtk::forClipboard(clipboard);
292     GtkTargetList* list = targetListForDataObject(dataObject);
293 
294     int numberOfTargets;
295     GtkTargetEntry* table = gtk_target_table_new_from_list(list, &numberOfTargets);
296 
297     if (numberOfTargets > 0 && table) {
298         settingClipboardDataObject = dataObject;
299 
300         gtk_clipboard_set_with_data(clipboard, table, numberOfTargets,
301             getClipboardContentsCallback, clearClipboardContentsCallback, callback);
302         gtk_clipboard_set_can_store(clipboard, 0, 0);
303 
304         settingClipboardDataObject = 0;
305 
306     } else
307         gtk_clipboard_clear(clipboard);
308 
309     if (table)
310         gtk_target_table_free(table, numberOfTargets);
311     gtk_target_list_unref(list);
312 }
313 
314 }
315 
316