1 /*
2 * Copyright (C) 2007 Apple Inc. All rights reserved.
3 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
4 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
5 * Copyright (C) 2010 Sencha, Inc. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "config.h"
30 #include "ClipboardQt.h"
31
32 #include "CachedImage.h"
33 #include "DataTransferItemsQt.h"
34 #include "Document.h"
35 #include "DragData.h"
36 #include "Element.h"
37 #include "FileList.h"
38 #include "Frame.h"
39 #include "HTMLNames.h"
40 #include "HTMLParserIdioms.h"
41 #include "Image.h"
42 #include "IntPoint.h"
43 #include "KURL.h"
44 #include "NotImplemented.h"
45 #include "PlatformString.h"
46 #include "Range.h"
47 #include "RenderImage.h"
48 #include "markup.h"
49 #include <wtf/text/StringHash.h>
50
51 #include <QApplication>
52 #include <QClipboard>
53 #include <QList>
54 #include <QMimeData>
55 #include <QStringList>
56 #include <QTextCodec>
57 #include <QUrl>
58 #include <qdebug.h>
59
60 #define methodDebug() qDebug("ClipboardQt: %s", __FUNCTION__)
61
62 namespace WebCore {
63
isTextMimeType(const String & type)64 static bool isTextMimeType(const String& type)
65 {
66 return type == "text/plain" || type.startsWith("text/plain;");
67 }
68
isHtmlMimeType(const String & type)69 static bool isHtmlMimeType(const String& type)
70 {
71 return type == "text/html" || type.startsWith("text/html;");
72 }
73
create(ClipboardAccessPolicy policy,DragData * dragData,Frame * frame)74 PassRefPtr<Clipboard> Clipboard::create(ClipboardAccessPolicy policy, DragData* dragData, Frame* frame)
75 {
76 return ClipboardQt::create(policy, dragData->platformData(), frame);
77 }
78
ClipboardQt(ClipboardAccessPolicy policy,const QMimeData * readableClipboard,Frame * frame)79 ClipboardQt::ClipboardQt(ClipboardAccessPolicy policy, const QMimeData* readableClipboard, Frame* frame)
80 : Clipboard(policy, DragAndDrop)
81 , m_readableData(readableClipboard)
82 , m_writableData(0)
83 , m_frame(frame)
84 {
85 Q_ASSERT(policy == ClipboardReadable || policy == ClipboardTypesReadable);
86 }
87
ClipboardQt(ClipboardAccessPolicy policy,ClipboardType clipboardType,Frame * frame)88 ClipboardQt::ClipboardQt(ClipboardAccessPolicy policy, ClipboardType clipboardType, Frame* frame)
89 : Clipboard(policy, clipboardType)
90 , m_readableData(0)
91 , m_writableData(0)
92 , m_frame(frame)
93 {
94 Q_ASSERT(policy == ClipboardReadable || policy == ClipboardWritable || policy == ClipboardNumb);
95
96 #ifndef QT_NO_CLIPBOARD
97 if (policy != ClipboardWritable) {
98 Q_ASSERT(isForCopyAndPaste());
99 m_readableData = QApplication::clipboard()->mimeData();
100 }
101 #endif
102 }
103
~ClipboardQt()104 ClipboardQt::~ClipboardQt()
105 {
106 if (m_writableData && isForCopyAndPaste())
107 m_writableData = 0;
108 else
109 delete m_writableData;
110 m_readableData = 0;
111 }
112
clearData(const String & type)113 void ClipboardQt::clearData(const String& type)
114 {
115 if (policy() != ClipboardWritable)
116 return;
117
118 if (m_writableData) {
119 m_writableData->removeFormat(type);
120 if (m_writableData->formats().isEmpty()) {
121 if (isForDragAndDrop())
122 delete m_writableData;
123 m_writableData = 0;
124 }
125 }
126 #ifndef QT_NO_CLIPBOARD
127 if (isForCopyAndPaste())
128 QApplication::clipboard()->setMimeData(m_writableData);
129 #endif
130 }
131
clearAllData()132 void ClipboardQt::clearAllData()
133 {
134 if (policy() != ClipboardWritable)
135 return;
136
137 #ifndef QT_NO_CLIPBOARD
138 if (isForCopyAndPaste())
139 QApplication::clipboard()->setMimeData(0);
140 else
141 #endif
142 delete m_writableData;
143 m_writableData = 0;
144 }
145
getData(const String & type,bool & success) const146 String ClipboardQt::getData(const String& type, bool& success) const
147 {
148
149 if (policy() != ClipboardReadable) {
150 success = false;
151 return String();
152 }
153
154 if (isHtmlMimeType(type) && m_readableData->hasHtml()) {
155 success = true;
156 return m_readableData->html();
157 }
158
159 if (isTextMimeType(type) && m_readableData->hasText()) {
160 success = true;
161 return m_readableData->text();
162 }
163
164 ASSERT(m_readableData);
165 QByteArray rawData = m_readableData->data(type);
166 QString data = QTextCodec::codecForName("UTF-16")->toUnicode(rawData);
167 success = !data.isEmpty();
168 return data;
169 }
170
setData(const String & type,const String & data)171 bool ClipboardQt::setData(const String& type, const String& data)
172 {
173 if (policy() != ClipboardWritable)
174 return false;
175
176 if (!m_writableData)
177 m_writableData = new QMimeData;
178
179 if (isTextMimeType(type))
180 m_writableData->setText(QString(data));
181 else if (isHtmlMimeType(type))
182 m_writableData->setHtml(QString(data));
183 else {
184 QByteArray array(reinterpret_cast<const char*>(data.characters()), data.length() * 2);
185 m_writableData->setData(QString(type), array);
186 }
187
188 #ifndef QT_NO_CLIPBOARD
189 if (isForCopyAndPaste())
190 QApplication::clipboard()->setMimeData(m_writableData);
191 #endif
192 return true;
193 }
194
195 // extensions beyond IE's API
types() const196 HashSet<String> ClipboardQt::types() const
197 {
198 if (policy() != ClipboardReadable && policy() != ClipboardTypesReadable)
199 return HashSet<String>();
200
201 ASSERT(m_readableData);
202 HashSet<String> result;
203 QStringList formats = m_readableData->formats();
204 for (int i = 0; i < formats.count(); ++i)
205 result.add(formats.at(i));
206 return result;
207 }
208
files() const209 PassRefPtr<FileList> ClipboardQt::files() const
210 {
211 if (policy() != ClipboardReadable || !m_readableData->hasUrls())
212 return FileList::create();
213
214 RefPtr<FileList> fileList = FileList::create();
215 QList<QUrl> urls = m_readableData->urls();
216
217 for (int i = 0; i < urls.size(); i++) {
218 QUrl url = urls[i];
219 if (url.scheme() != QLatin1String("file"))
220 continue;
221 fileList->append(File::create(url.toLocalFile()));
222 }
223
224 return fileList.release();
225 }
226
setDragImage(CachedImage * image,const IntPoint & point)227 void ClipboardQt::setDragImage(CachedImage* image, const IntPoint& point)
228 {
229 setDragImage(image, 0, point);
230 }
231
setDragImageElement(Node * node,const IntPoint & point)232 void ClipboardQt::setDragImageElement(Node* node, const IntPoint& point)
233 {
234 setDragImage(0, node, point);
235 }
236
setDragImage(CachedImage * image,Node * node,const IntPoint & loc)237 void ClipboardQt::setDragImage(CachedImage* image, Node *node, const IntPoint &loc)
238 {
239 if (policy() != ClipboardImageWritable && policy() != ClipboardWritable)
240 return;
241
242 if (m_dragImage)
243 m_dragImage->removeClient(this);
244 m_dragImage = image;
245 if (m_dragImage)
246 m_dragImage->addClient(this);
247
248 m_dragLoc = loc;
249 m_dragImageElement = node;
250 }
251
createDragImage(IntPoint & dragLoc) const252 DragImageRef ClipboardQt::createDragImage(IntPoint& dragLoc) const
253 {
254 if (!m_dragImage)
255 return 0;
256 dragLoc = m_dragLoc;
257 return m_dragImage->image()->nativeImageForCurrentFrame();
258 }
259
260
getCachedImage(Element * element)261 static CachedImage* getCachedImage(Element* element)
262 {
263 // Attempt to pull CachedImage from element
264 ASSERT(element);
265 RenderObject* renderer = element->renderer();
266 if (!renderer || !renderer->isImage())
267 return 0;
268
269 RenderImage* image = toRenderImage(renderer);
270 if (image->cachedImage() && !image->cachedImage()->errorOccurred())
271 return image->cachedImage();
272
273 return 0;
274 }
275
declareAndWriteDragImage(Element * element,const KURL & url,const String & title,Frame * frame)276 void ClipboardQt::declareAndWriteDragImage(Element* element, const KURL& url, const String& title, Frame* frame)
277 {
278 ASSERT(frame);
279
280 // WebCore::writeURL(m_writableDataObject.get(), url, title, true, false);
281 if (!m_writableData)
282 m_writableData = new QMimeData;
283
284 CachedImage* cachedImage = getCachedImage(element);
285 if (!cachedImage || !cachedImage->image() || !cachedImage->isLoaded())
286 return;
287 QPixmap* pixmap = cachedImage->image()->nativeImageForCurrentFrame();
288 if (pixmap)
289 m_writableData->setImageData(*pixmap);
290
291 AtomicString imageURL = element->getAttribute(HTMLNames::srcAttr);
292 if (imageURL.isEmpty())
293 return;
294
295 KURL fullURL = frame->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(imageURL));
296 if (fullURL.isEmpty())
297 return;
298
299 QList<QUrl> urls;
300 urls.append(url);
301
302 m_writableData->setText(title);
303 m_writableData->setUrls(urls);
304 m_writableData->setHtml(imageToMarkup(fullURL, element));
305 #ifndef QT_NO_CLIPBOARD
306 if (isForCopyAndPaste())
307 QApplication::clipboard()->setMimeData(m_writableData);
308 #endif
309 }
310
writeURL(const KURL & url,const String & title,Frame * frame)311 void ClipboardQt::writeURL(const KURL& url, const String& title, Frame* frame)
312 {
313 ASSERT(frame);
314
315 QList<QUrl> urls;
316 urls.append(frame->document()->completeURL(url.string()));
317 if (!m_writableData)
318 m_writableData = new QMimeData;
319 m_writableData->setUrls(urls);
320 m_writableData->setText(title);
321 #ifndef QT_NO_CLIPBOARD
322 if (isForCopyAndPaste())
323 QApplication::clipboard()->setMimeData(m_writableData);
324 #endif
325 }
326
writeRange(Range * range,Frame * frame)327 void ClipboardQt::writeRange(Range* range, Frame* frame)
328 {
329 ASSERT(range);
330 ASSERT(frame);
331
332 if (!m_writableData)
333 m_writableData = new QMimeData;
334 QString text = frame->editor()->selectedText();
335 text.replace(QChar(0xa0), QLatin1Char(' '));
336 m_writableData->setText(text);
337 m_writableData->setHtml(createMarkup(range, 0, AnnotateForInterchange, false, AbsoluteURLs));
338 #ifndef QT_NO_CLIPBOARD
339 if (isForCopyAndPaste())
340 QApplication::clipboard()->setMimeData(m_writableData);
341 #endif
342 }
343
writePlainText(const String & str)344 void ClipboardQt::writePlainText(const String& str)
345 {
346 if (!m_writableData)
347 m_writableData = new QMimeData;
348 QString text = str;
349 text.replace(QChar(0xa0), QLatin1Char(' '));
350 m_writableData->setText(text);
351 #ifndef QT_NO_CLIPBOARD
352 if (isForCopyAndPaste())
353 QApplication::clipboard()->setMimeData(m_writableData);
354 #endif
355 }
356
hasData()357 bool ClipboardQt::hasData()
358 {
359 const QMimeData *data = m_readableData ? m_readableData : m_writableData;
360 if (!data)
361 return false;
362 return data->formats().count() > 0;
363 }
364
365 #if ENABLE(DATA_TRANSFER_ITEMS)
items()366 PassRefPtr<DataTransferItems> ClipboardQt::items()
367 {
368
369 if (!m_frame && !m_frame->document())
370 return 0;
371
372 RefPtr<DataTransferItemsQt> items = DataTransferItemsQt::create(this, m_frame->document()->scriptExecutionContext());
373
374 if (!m_readableData)
375 return items;
376
377 if (isForCopyAndPaste() && policy() == ClipboardReadable) {
378 const QStringList types = m_readableData->formats();
379 for (int i = 0; i < types.count(); ++i)
380 items->addPasteboardItem(types.at(i));
381 }
382 return items;
383 }
384 #endif
385
386 }
387