Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / clipboard / Clipboard.cpp
1 /*
2  * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "core/clipboard/Clipboard.h"
28
29 #include "HTMLNames.h"
30 #include "core/clipboard/DataObject.h"
31 #include "core/clipboard/DataTransferItem.h"
32 #include "core/clipboard/DataTransferItemList.h"
33 #include "core/editing/markup.h"
34 #include "core/fetch/ImageResource.h"
35 #include "core/fileapi/FileList.h"
36 #include "core/frame/Frame.h"
37 #include "core/html/HTMLImageElement.h"
38 #include "core/rendering/RenderImage.h"
39 #include "core/rendering/RenderObject.h"
40 #include "platform/DragImage.h"
41 #include "platform/MIMETypeRegistry.h"
42 #include "platform/clipboard/ClipboardMimeTypes.h"
43 #include "platform/clipboard/ClipboardUtilities.h"
44
45 namespace WebCore {
46
47 // These "conversion" methods are called by both WebCore and WebKit, and never make sense to JS, so we don't
48 // worry about security for these. They don't allow access to the pasteboard anyway.
49 static DragOperation dragOpFromIEOp(const String& op)
50 {
51     // yep, it's really just this fixed set
52     if (op == "uninitialized")
53         return DragOperationEvery;
54     if (op == "none")
55         return DragOperationNone;
56     if (op == "copy")
57         return DragOperationCopy;
58     if (op == "link")
59         return DragOperationLink;
60     if (op == "move")
61         return (DragOperation)(DragOperationGeneric | DragOperationMove);
62     if (op == "copyLink")
63         return (DragOperation)(DragOperationCopy | DragOperationLink);
64     if (op == "copyMove")
65         return (DragOperation)(DragOperationCopy | DragOperationGeneric | DragOperationMove);
66     if (op == "linkMove")
67         return (DragOperation)(DragOperationLink | DragOperationGeneric | DragOperationMove);
68     if (op == "all")
69         return DragOperationEvery;
70     return DragOperationPrivate; // really a marker for "no conversion"
71 }
72
73 static String IEOpFromDragOp(DragOperation op)
74 {
75     bool moveSet = !!((DragOperationGeneric | DragOperationMove) & op);
76
77     if ((moveSet && (op & DragOperationCopy) && (op & DragOperationLink))
78         || (op == DragOperationEvery))
79         return "all";
80     if (moveSet && (op & DragOperationCopy))
81         return "copyMove";
82     if (moveSet && (op & DragOperationLink))
83         return "linkMove";
84     if ((op & DragOperationCopy) && (op & DragOperationLink))
85         return "copyLink";
86     if (moveSet)
87         return "move";
88     if (op & DragOperationCopy)
89         return "copy";
90     if (op & DragOperationLink)
91         return "link";
92     return "none";
93 }
94
95 // We provide the IE clipboard types (URL and Text), and the clipboard types specified in the WHATWG Web Applications 1.0 draft
96 // see http://www.whatwg.org/specs/web-apps/current-work/ Section 6.3.5.3
97 static String normalizeType(const String& type, bool* convertToURL = 0)
98 {
99     String cleanType = type.stripWhiteSpace().lower();
100     if (cleanType == mimeTypeText || cleanType.startsWith(mimeTypeTextPlainEtc))
101         return mimeTypeTextPlain;
102     if (cleanType == mimeTypeURL) {
103         if (convertToURL)
104             *convertToURL = true;
105         return mimeTypeTextURIList;
106     }
107     return cleanType;
108 }
109
110 PassRefPtr<Clipboard> Clipboard::create(ClipboardType type, ClipboardAccessPolicy policy, PassRefPtr<DataObject> dataObject)
111 {
112     return adoptRef(new Clipboard(type, policy , dataObject));
113 }
114
115 Clipboard::~Clipboard()
116 {
117 }
118
119 void Clipboard::setDropEffect(const String &effect)
120 {
121     if (!isForDragAndDrop())
122         return;
123
124     // The attribute must ignore any attempts to set it to a value other than none, copy, link, and move.
125     if (effect != "none" && effect != "copy"  && effect != "link" && effect != "move")
126         return;
127
128     // FIXME: The spec actually allows this in all circumstances, even though there's no point in
129     // setting the drop effect when this condition is not true.
130     if (canReadTypes())
131         m_dropEffect = effect;
132 }
133
134 void Clipboard::setEffectAllowed(const String &effect)
135 {
136     if (!isForDragAndDrop())
137         return;
138
139     if (dragOpFromIEOp(effect) == DragOperationPrivate) {
140         // This means that there was no conversion, and the effectAllowed that
141         // we are passed isn't a valid effectAllowed, so we should ignore it,
142         // and not set m_effectAllowed.
143
144         // The attribute must ignore any attempts to set it to a value other than
145         // none, copy, copyLink, copyMove, link, linkMove, move, all, and uninitialized.
146         return;
147     }
148
149
150     if (canWriteData())
151         m_effectAllowed = effect;
152 }
153
154 void Clipboard::clearData(const String& type)
155 {
156     if (!canWriteData())
157         return;
158
159     if (type.isNull())
160         m_dataObject->clearAll();
161     else
162         m_dataObject->clearData(normalizeType(type));
163 }
164
165 String Clipboard::getData(const String& type) const
166 {
167     if (!canReadData())
168         return String();
169
170     bool convertToURL = false;
171     String data = m_dataObject->getData(normalizeType(type, &convertToURL));
172     if (!convertToURL)
173         return data;
174     return convertURIListToURL(data);
175 }
176
177 bool Clipboard::setData(const String& type, const String& data)
178 {
179     if (!canWriteData())
180         return false;
181
182     return m_dataObject->setData(normalizeType(type), data);
183 }
184
185 // extensions beyond IE's API
186 Vector<String> Clipboard::types() const
187 {
188     Vector<String> types;
189     if (!canReadTypes())
190         return types;
191
192     ListHashSet<String> typesSet = m_dataObject->types();
193     types.appendRange(typesSet.begin(), typesSet.end());
194     return types;
195 }
196
197 PassRefPtr<FileList> Clipboard::files() const
198 {
199     RefPtr<FileList> files = FileList::create();
200     if (!canReadData())
201         return files.release();
202
203     for (size_t i = 0; i < m_dataObject->length(); ++i) {
204         if (m_dataObject->item(i)->kind() == DataObjectItem::FileKind) {
205             RefPtr<Blob> blob = m_dataObject->item(i)->getAsFile();
206             if (blob && blob->isFile())
207                 files->append(toFile(blob.get()));
208         }
209     }
210
211     return files.release();
212 }
213
214 void Clipboard::setDragImage(Element* image, int x, int y, ExceptionState& exceptionState)
215 {
216     if (!isForDragAndDrop())
217         return;
218
219     if (!image) {
220         exceptionState.throwTypeError("setDragImage: Invalid first argument");
221         return;
222     }
223     IntPoint location(x, y);
224     if (image->hasTagName(HTMLNames::imgTag) && !image->inDocument())
225         setDragImageResource(toHTMLImageElement(image)->cachedImage(), location);
226     else
227         setDragImageElement(image, location);
228 }
229
230 void Clipboard::setDragImageResource(ImageResource* img, const IntPoint& loc)
231 {
232     setDragImage(img, 0, loc);
233 }
234
235 void Clipboard::setDragImageElement(Node* node, const IntPoint& loc)
236 {
237     setDragImage(0, node, loc);
238 }
239
240 PassOwnPtr<DragImage> Clipboard::createDragImage(IntPoint& loc, Frame* frame) const
241 {
242     if (m_dragImageElement) {
243         loc = m_dragLoc;
244         return frame->nodeImage(m_dragImageElement.get());
245     }
246     if (m_dragImage) {
247         loc = m_dragLoc;
248         return DragImage::create(m_dragImage->image());
249     }
250     return nullptr;
251 }
252
253 static ImageResource* getImageResource(Element* element)
254 {
255     // Attempt to pull ImageResource from element
256     ASSERT(element);
257     RenderObject* renderer = element->renderer();
258     if (!renderer || !renderer->isImage())
259         return 0;
260
261     RenderImage* image = toRenderImage(renderer);
262     if (image->cachedImage() && !image->cachedImage()->errorOccurred())
263         return image->cachedImage();
264
265     return 0;
266 }
267
268 static void writeImageToDataObject(DataObject* dataObject, Element* element, const KURL& url)
269 {
270     // Shove image data into a DataObject for use as a file
271     ImageResource* cachedImage = getImageResource(element);
272     if (!cachedImage || !cachedImage->imageForRenderer(element->renderer()) || !cachedImage->isLoaded())
273         return;
274
275     SharedBuffer* imageBuffer = cachedImage->imageForRenderer(element->renderer())->data();
276     if (!imageBuffer || !imageBuffer->size())
277         return;
278
279     String imageExtension = cachedImage->image()->filenameExtension();
280     ASSERT(!imageExtension.isEmpty());
281
282     // Determine the filename for the file contents of the image.
283     String filename = cachedImage->response().suggestedFilename();
284     if (filename.isEmpty())
285         filename = url.lastPathComponent();
286
287     String fileExtension;
288     if (filename.isEmpty()) {
289         filename = element->getAttribute(HTMLNames::altAttr);
290     } else {
291         // Strip any existing extension. Assume that alt text is usually not a filename.
292         int extensionIndex = filename.reverseFind('.');
293         if (extensionIndex != -1) {
294             fileExtension = filename.substring(extensionIndex + 1);
295             filename.truncate(extensionIndex);
296         }
297     }
298
299     if (!fileExtension.isEmpty() && fileExtension != imageExtension) {
300         String imageMimeType = MIMETypeRegistry::getMIMETypeForExtension(imageExtension);
301         ASSERT(imageMimeType.startsWith("image/"));
302         // Use the file extension only if it has imageMimeType: it's untrustworthy otherwise.
303         if (imageMimeType == MIMETypeRegistry::getMIMETypeForExtension(fileExtension))
304             imageExtension = fileExtension;
305     }
306
307     imageExtension = "." + imageExtension;
308     validateFilename(filename, imageExtension);
309
310     dataObject->addSharedBuffer(filename + imageExtension, imageBuffer);
311 }
312
313 void Clipboard::declareAndWriteDragImage(Element* element, const KURL& url, const String& title)
314 {
315     if (!m_dataObject)
316         return;
317
318     m_dataObject->setURLAndTitle(url, title);
319
320     // Write the bytes in the image to the file format.
321     writeImageToDataObject(m_dataObject.get(), element, url);
322
323     // Put img tag on the clipboard referencing the image
324     m_dataObject->setData(mimeTypeTextHTML, createMarkup(element, IncludeNode, 0, ResolveAllURLs));
325 }
326
327 void Clipboard::writeURL(const KURL& url, const String& title)
328 {
329     if (!m_dataObject)
330         return;
331     ASSERT(!url.isEmpty());
332
333     m_dataObject->setURLAndTitle(url, title);
334
335     // The URL can also be used as plain text.
336     m_dataObject->setData(mimeTypeTextPlain, url.string());
337
338     // The URL can also be used as an HTML fragment.
339     m_dataObject->setHTMLAndBaseURL(urlToMarkup(url, title), url);
340 }
341
342 void Clipboard::writeRange(Range* selectedRange, Frame* frame)
343 {
344     ASSERT(selectedRange);
345     if (!m_dataObject)
346         return;
347
348     m_dataObject->setHTMLAndBaseURL(createMarkup(selectedRange, 0, AnnotateForInterchange, false, ResolveNonLocalURLs), frame->document()->url());
349
350     String str = frame->selectedTextForClipboard();
351 #if OS(WIN)
352     replaceNewlinesWithWindowsStyleNewlines(str);
353 #endif
354     replaceNBSPWithSpace(str);
355     m_dataObject->setData(mimeTypeTextPlain, str);
356 }
357
358 void Clipboard::writePlainText(const String& text)
359 {
360     if (!m_dataObject)
361         return;
362
363     String str = text;
364 #if OS(WIN)
365     replaceNewlinesWithWindowsStyleNewlines(str);
366 #endif
367     replaceNBSPWithSpace(str);
368
369     m_dataObject->setData(mimeTypeTextPlain, str);
370 }
371
372 bool Clipboard::hasData()
373 {
374     ASSERT(isForDragAndDrop());
375
376     return m_dataObject->length() > 0;
377 }
378
379 void Clipboard::setAccessPolicy(ClipboardAccessPolicy policy)
380 {
381     // once you go numb, can never go back
382     ASSERT(m_policy != ClipboardNumb || policy == ClipboardNumb);
383     m_policy = policy;
384 }
385
386 bool Clipboard::canReadTypes() const
387 {
388     return m_policy == ClipboardReadable || m_policy == ClipboardTypesReadable || m_policy == ClipboardWritable;
389 }
390
391 bool Clipboard::canReadData() const
392 {
393     return m_policy == ClipboardReadable || m_policy == ClipboardWritable;
394 }
395
396 bool Clipboard::canWriteData() const
397 {
398     return m_policy == ClipboardWritable;
399 }
400
401 bool Clipboard::canSetDragImage() const
402 {
403     return m_policy == ClipboardImageWritable || m_policy == ClipboardWritable;
404 }
405
406 DragOperation Clipboard::sourceOperation() const
407 {
408     DragOperation op = dragOpFromIEOp(m_effectAllowed);
409     ASSERT(op != DragOperationPrivate);
410     return op;
411 }
412
413 DragOperation Clipboard::destinationOperation() const
414 {
415     DragOperation op = dragOpFromIEOp(m_dropEffect);
416     ASSERT(op == DragOperationCopy || op == DragOperationNone || op == DragOperationLink || op == (DragOperation)(DragOperationGeneric | DragOperationMove) || op == DragOperationEvery);
417     return op;
418 }
419
420 void Clipboard::setSourceOperation(DragOperation op)
421 {
422     ASSERT_ARG(op, op != DragOperationPrivate);
423     m_effectAllowed = IEOpFromDragOp(op);
424 }
425
426 void Clipboard::setDestinationOperation(DragOperation op)
427 {
428     ASSERT_ARG(op, op == DragOperationCopy || op == DragOperationNone || op == DragOperationLink || op == DragOperationGeneric || op == DragOperationMove || op == (DragOperation)(DragOperationGeneric | DragOperationMove));
429     m_dropEffect = IEOpFromDragOp(op);
430 }
431
432 bool Clipboard::hasDropZoneType(const String& keyword)
433 {
434     if (keyword.startsWith("file:"))
435         return hasFileOfType(keyword.substring(5));
436
437     if (keyword.startsWith("string:"))
438         return hasStringOfType(keyword.substring(7));
439
440     return false;
441 }
442
443 PassRefPtr<DataTransferItemList> Clipboard::items()
444 {
445     // FIXME: According to the spec, we are supposed to return the same collection of items each
446     // time. We now return a wrapper that always wraps the *same* set of items, so JS shouldn't be
447     // able to tell, but we probably still want to fix this.
448     return DataTransferItemList::create(this, m_dataObject);
449 }
450
451 PassRefPtr<DataObject> Clipboard::dataObject() const
452 {
453     return m_dataObject;
454 }
455
456 Clipboard::Clipboard(ClipboardType type, ClipboardAccessPolicy policy, PassRefPtr<DataObject> dataObject)
457     : m_policy(policy)
458     , m_dropEffect("uninitialized")
459     , m_effectAllowed("uninitialized")
460     , m_clipboardType(type)
461     , m_dataObject(dataObject)
462 {
463     ScriptWrappable::init(this);
464 }
465
466 void Clipboard::setDragImage(ImageResource* image, Node* node, const IntPoint& loc)
467 {
468     if (!canSetDragImage())
469         return;
470
471     m_dragImage = image;
472     m_dragLoc = loc;
473     m_dragImageElement = node;
474 }
475
476 bool Clipboard::hasFileOfType(const String& type) const
477 {
478     if (!canReadTypes())
479         return false;
480
481     RefPtr<FileList> fileList = files();
482     if (fileList->isEmpty())
483         return false;
484
485     for (unsigned f = 0; f < fileList->length(); f++) {
486         if (equalIgnoringCase(fileList->item(f)->type(), type))
487             return true;
488     }
489     return false;
490 }
491
492 bool Clipboard::hasStringOfType(const String& type) const
493 {
494     if (!canReadTypes())
495         return false;
496
497     return types().contains(type);
498 }
499
500 DragOperation convertDropZoneOperationToDragOperation(const String& dragOperation)
501 {
502     if (dragOperation == "copy")
503         return DragOperationCopy;
504     if (dragOperation == "move")
505         return DragOperationMove;
506     if (dragOperation == "link")
507         return DragOperationLink;
508     return DragOperationNone;
509 }
510
511 String convertDragOperationToDropZoneOperation(DragOperation operation)
512 {
513     switch (operation) {
514     case DragOperationCopy:
515         return String("copy");
516     case DragOperationMove:
517         return String("move");
518     case DragOperationLink:
519         return String("link");
520     default:
521         return String("copy");
522     }
523 }
524
525 } // namespace WebCore