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