• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "core/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/LocalFrame.h"
37 #include "core/html/HTMLImageElement.h"
38 #include "core/rendering/RenderImage.h"
39 #include "core/rendering/RenderLayer.h"
40 #include "core/rendering/RenderObject.h"
41 #include "platform/DragImage.h"
42 #include "platform/MIMETypeRegistry.h"
43 #include "platform/clipboard/ClipboardMimeTypes.h"
44 #include "platform/clipboard/ClipboardUtilities.h"
45 
46 namespace WebCore {
47 
48 // These "conversion" methods are called by both WebCore and WebKit, and never make sense to JS, so we don't
49 // worry about security for these. They don't allow access to the pasteboard anyway.
dragOpFromIEOp(const String & op)50 static DragOperation dragOpFromIEOp(const String& op)
51 {
52     // yep, it's really just this fixed set
53     if (op == "uninitialized")
54         return DragOperationEvery;
55     if (op == "none")
56         return DragOperationNone;
57     if (op == "copy")
58         return DragOperationCopy;
59     if (op == "link")
60         return DragOperationLink;
61     if (op == "move")
62         return (DragOperation)(DragOperationGeneric | DragOperationMove);
63     if (op == "copyLink")
64         return (DragOperation)(DragOperationCopy | DragOperationLink);
65     if (op == "copyMove")
66         return (DragOperation)(DragOperationCopy | DragOperationGeneric | DragOperationMove);
67     if (op == "linkMove")
68         return (DragOperation)(DragOperationLink | DragOperationGeneric | DragOperationMove);
69     if (op == "all")
70         return DragOperationEvery;
71     return DragOperationPrivate; // really a marker for "no conversion"
72 }
73 
IEOpFromDragOp(DragOperation op)74 static String IEOpFromDragOp(DragOperation op)
75 {
76     bool moveSet = !!((DragOperationGeneric | DragOperationMove) & op);
77 
78     if ((moveSet && (op & DragOperationCopy) && (op & DragOperationLink))
79         || (op == DragOperationEvery))
80         return "all";
81     if (moveSet && (op & DragOperationCopy))
82         return "copyMove";
83     if (moveSet && (op & DragOperationLink))
84         return "linkMove";
85     if ((op & DragOperationCopy) && (op & DragOperationLink))
86         return "copyLink";
87     if (moveSet)
88         return "move";
89     if (op & DragOperationCopy)
90         return "copy";
91     if (op & DragOperationLink)
92         return "link";
93     return "none";
94 }
95 
96 // We provide the IE clipboard types (URL and Text), and the clipboard types specified in the WHATWG Web Applications 1.0 draft
97 // see http://www.whatwg.org/specs/web-apps/current-work/ Section 6.3.5.3
normalizeType(const String & type,bool * convertToURL=0)98 static String normalizeType(const String& type, bool* convertToURL = 0)
99 {
100     String cleanType = type.stripWhiteSpace().lower();
101     if (cleanType == mimeTypeText || cleanType.startsWith(mimeTypeTextPlainEtc))
102         return mimeTypeTextPlain;
103     if (cleanType == mimeTypeURL) {
104         if (convertToURL)
105             *convertToURL = true;
106         return mimeTypeTextURIList;
107     }
108     return cleanType;
109 }
110 
create(ClipboardType type,ClipboardAccessPolicy policy,PassRefPtrWillBeRawPtr<DataObject> dataObject)111 PassRefPtrWillBeRawPtr<Clipboard> Clipboard::create(ClipboardType type, ClipboardAccessPolicy policy, PassRefPtrWillBeRawPtr<DataObject> dataObject)
112 {
113     return adoptRefWillBeNoop(new Clipboard(type, policy , dataObject));
114 }
115 
~Clipboard()116 Clipboard::~Clipboard()
117 {
118 }
119 
setDropEffect(const String & effect)120 void Clipboard::setDropEffect(const String &effect)
121 {
122     if (!isForDragAndDrop())
123         return;
124 
125     // The attribute must ignore any attempts to set it to a value other than none, copy, link, and move.
126     if (effect != "none" && effect != "copy"  && effect != "link" && effect != "move")
127         return;
128 
129     // FIXME: The spec actually allows this in all circumstances, even though there's no point in
130     // setting the drop effect when this condition is not true.
131     if (canReadTypes())
132         m_dropEffect = effect;
133 }
134 
setEffectAllowed(const String & effect)135 void Clipboard::setEffectAllowed(const String &effect)
136 {
137     if (!isForDragAndDrop())
138         return;
139 
140     if (dragOpFromIEOp(effect) == DragOperationPrivate) {
141         // This means that there was no conversion, and the effectAllowed that
142         // we are passed isn't a valid effectAllowed, so we should ignore it,
143         // and not set m_effectAllowed.
144 
145         // The attribute must ignore any attempts to set it to a value other than
146         // none, copy, copyLink, copyMove, link, linkMove, move, all, and uninitialized.
147         return;
148     }
149 
150 
151     if (canWriteData())
152         m_effectAllowed = effect;
153 }
154 
clearData(const String & type)155 void Clipboard::clearData(const String& type)
156 {
157     if (!canWriteData())
158         return;
159 
160     if (type.isNull())
161         m_dataObject->clearAll();
162     else
163         m_dataObject->clearData(normalizeType(type));
164 }
165 
getData(const String & type) const166 String Clipboard::getData(const String& type) const
167 {
168     if (!canReadData())
169         return String();
170 
171     bool convertToURL = false;
172     String data = m_dataObject->getData(normalizeType(type, &convertToURL));
173     if (!convertToURL)
174         return data;
175     return convertURIListToURL(data);
176 }
177 
setData(const String & type,const String & data)178 bool Clipboard::setData(const String& type, const String& data)
179 {
180     if (!canWriteData())
181         return false;
182 
183     return m_dataObject->setData(normalizeType(type), data);
184 }
185 
186 // extensions beyond IE's API
types() const187 Vector<String> Clipboard::types() const
188 {
189     Vector<String> types;
190     if (!canReadTypes())
191         return types;
192 
193     ListHashSet<String> typesSet = m_dataObject->types();
194     types.appendRange(typesSet.begin(), typesSet.end());
195     return types;
196 }
197 
files() const198 PassRefPtrWillBeRawPtr<FileList> Clipboard::files() const
199 {
200     RefPtrWillBeRawPtr<FileList> files = FileList::create();
201     if (!canReadData())
202         return files.release();
203 
204     for (size_t i = 0; i < m_dataObject->length(); ++i) {
205         if (m_dataObject->item(i)->kind() == DataObjectItem::FileKind) {
206             RefPtrWillBeRawPtr<Blob> blob = m_dataObject->item(i)->getAsFile();
207             if (blob && blob->isFile())
208                 files->append(toFile(blob.get()));
209         }
210     }
211 
212     return files.release();
213 }
214 
setDragImage(Element * image,int x,int y,ExceptionState & exceptionState)215 void Clipboard::setDragImage(Element* image, int x, int y, ExceptionState& exceptionState)
216 {
217     if (!isForDragAndDrop())
218         return;
219 
220     if (!image) {
221         exceptionState.throwTypeError("setDragImage: Invalid first argument");
222         return;
223     }
224     IntPoint location(x, y);
225     if (isHTMLImageElement(*image) && !image->inDocument())
226         setDragImageResource(toHTMLImageElement(*image).cachedImage(), location);
227     else
228         setDragImageElement(image, location);
229 }
230 
clearDragImage()231 void Clipboard::clearDragImage()
232 {
233     if (!canSetDragImage())
234         return;
235 
236     m_dragImage = 0;
237     m_dragLoc = IntPoint();
238     m_dragImageElement = nullptr;
239 }
240 
setDragImageResource(ImageResource * img,const IntPoint & loc)241 void Clipboard::setDragImageResource(ImageResource* img, const IntPoint& loc)
242 {
243     setDragImage(img, 0, loc);
244 }
245 
setDragImageElement(Node * node,const IntPoint & loc)246 void Clipboard::setDragImageElement(Node* node, const IntPoint& loc)
247 {
248     setDragImage(0, node, loc);
249 }
250 
createDragImage(IntPoint & loc,LocalFrame * frame) const251 PassOwnPtr<DragImage> Clipboard::createDragImage(IntPoint& loc, LocalFrame* frame) const
252 {
253     if (m_dragImageElement) {
254         loc = m_dragLoc;
255 
256         return frame->nodeImage(*m_dragImageElement);
257     }
258     if (m_dragImage) {
259         loc = m_dragLoc;
260         return DragImage::create(m_dragImage->image());
261     }
262     return nullptr;
263 }
264 
getImageResource(Element * element)265 static ImageResource* getImageResource(Element* element)
266 {
267     // Attempt to pull ImageResource from element
268     ASSERT(element);
269     RenderObject* renderer = element->renderer();
270     if (!renderer || !renderer->isImage())
271         return 0;
272 
273     RenderImage* image = toRenderImage(renderer);
274     if (image->cachedImage() && !image->cachedImage()->errorOccurred())
275         return image->cachedImage();
276 
277     return 0;
278 }
279 
writeImageToDataObject(DataObject * dataObject,Element * element,const KURL & url)280 static void writeImageToDataObject(DataObject* dataObject, Element* element, const KURL& url)
281 {
282     // Shove image data into a DataObject for use as a file
283     ImageResource* cachedImage = getImageResource(element);
284     if (!cachedImage || !cachedImage->imageForRenderer(element->renderer()) || !cachedImage->isLoaded())
285         return;
286 
287     SharedBuffer* imageBuffer = cachedImage->imageForRenderer(element->renderer())->data();
288     if (!imageBuffer || !imageBuffer->size())
289         return;
290 
291     String imageExtension = cachedImage->image()->filenameExtension();
292     ASSERT(!imageExtension.isEmpty());
293 
294     // Determine the filename for the file contents of the image.
295     String filename = cachedImage->response().suggestedFilename();
296     if (filename.isEmpty())
297         filename = url.lastPathComponent();
298 
299     String fileExtension;
300     if (filename.isEmpty()) {
301         filename = element->getAttribute(HTMLNames::altAttr);
302     } else {
303         // Strip any existing extension. Assume that alt text is usually not a filename.
304         int extensionIndex = filename.reverseFind('.');
305         if (extensionIndex != -1) {
306             fileExtension = filename.substring(extensionIndex + 1);
307             filename.truncate(extensionIndex);
308         }
309     }
310 
311     if (!fileExtension.isEmpty() && fileExtension != imageExtension) {
312         String imageMimeType = MIMETypeRegistry::getMIMETypeForExtension(imageExtension);
313         ASSERT(imageMimeType.startsWith("image/"));
314         // Use the file extension only if it has imageMimeType: it's untrustworthy otherwise.
315         if (imageMimeType == MIMETypeRegistry::getMIMETypeForExtension(fileExtension))
316             imageExtension = fileExtension;
317     }
318 
319     imageExtension = "." + imageExtension;
320     validateFilename(filename, imageExtension);
321 
322     dataObject->addSharedBuffer(filename + imageExtension, imageBuffer);
323 }
324 
declareAndWriteDragImage(Element * element,const KURL & url,const String & title)325 void Clipboard::declareAndWriteDragImage(Element* element, const KURL& url, const String& title)
326 {
327     if (!m_dataObject)
328         return;
329 
330     m_dataObject->setURLAndTitle(url, title);
331 
332     // Write the bytes in the image to the file format.
333     writeImageToDataObject(m_dataObject.get(), element, url);
334 
335     // Put img tag on the clipboard referencing the image
336     m_dataObject->setData(mimeTypeTextHTML, createMarkup(element, IncludeNode, 0, ResolveAllURLs));
337 }
338 
writeURL(const KURL & url,const String & title)339 void Clipboard::writeURL(const KURL& url, const String& title)
340 {
341     if (!m_dataObject)
342         return;
343     ASSERT(!url.isEmpty());
344 
345     m_dataObject->setURLAndTitle(url, title);
346 
347     // The URL can also be used as plain text.
348     m_dataObject->setData(mimeTypeTextPlain, url.string());
349 
350     // The URL can also be used as an HTML fragment.
351     m_dataObject->setHTMLAndBaseURL(urlToMarkup(url, title), url);
352 }
353 
writeRange(Range * selectedRange,LocalFrame * frame)354 void Clipboard::writeRange(Range* selectedRange, LocalFrame* frame)
355 {
356     ASSERT(selectedRange);
357     if (!m_dataObject)
358         return;
359 
360     m_dataObject->setHTMLAndBaseURL(createMarkup(selectedRange, 0, AnnotateForInterchange, false, ResolveNonLocalURLs), frame->document()->url());
361 
362     String str = frame->selectedTextForClipboard();
363 #if OS(WIN)
364     replaceNewlinesWithWindowsStyleNewlines(str);
365 #endif
366     replaceNBSPWithSpace(str);
367     m_dataObject->setData(mimeTypeTextPlain, str);
368 }
369 
writePlainText(const String & text)370 void Clipboard::writePlainText(const String& text)
371 {
372     if (!m_dataObject)
373         return;
374 
375     String str = text;
376 #if OS(WIN)
377     replaceNewlinesWithWindowsStyleNewlines(str);
378 #endif
379     replaceNBSPWithSpace(str);
380 
381     m_dataObject->setData(mimeTypeTextPlain, str);
382 }
383 
hasData()384 bool Clipboard::hasData()
385 {
386     ASSERT(isForDragAndDrop());
387 
388     return m_dataObject->length() > 0;
389 }
390 
setAccessPolicy(ClipboardAccessPolicy policy)391 void Clipboard::setAccessPolicy(ClipboardAccessPolicy policy)
392 {
393     // once you go numb, can never go back
394     ASSERT(m_policy != ClipboardNumb || policy == ClipboardNumb);
395     m_policy = policy;
396 }
397 
canReadTypes() const398 bool Clipboard::canReadTypes() const
399 {
400     return m_policy == ClipboardReadable || m_policy == ClipboardTypesReadable || m_policy == ClipboardWritable;
401 }
402 
canReadData() const403 bool Clipboard::canReadData() const
404 {
405     return m_policy == ClipboardReadable || m_policy == ClipboardWritable;
406 }
407 
canWriteData() const408 bool Clipboard::canWriteData() const
409 {
410     return m_policy == ClipboardWritable;
411 }
412 
canSetDragImage() const413 bool Clipboard::canSetDragImage() const
414 {
415     return m_policy == ClipboardImageWritable || m_policy == ClipboardWritable;
416 }
417 
sourceOperation() const418 DragOperation Clipboard::sourceOperation() const
419 {
420     DragOperation op = dragOpFromIEOp(m_effectAllowed);
421     ASSERT(op != DragOperationPrivate);
422     return op;
423 }
424 
destinationOperation() const425 DragOperation Clipboard::destinationOperation() const
426 {
427     DragOperation op = dragOpFromIEOp(m_dropEffect);
428     ASSERT(op == DragOperationCopy || op == DragOperationNone || op == DragOperationLink || op == (DragOperation)(DragOperationGeneric | DragOperationMove) || op == DragOperationEvery);
429     return op;
430 }
431 
setSourceOperation(DragOperation op)432 void Clipboard::setSourceOperation(DragOperation op)
433 {
434     ASSERT_ARG(op, op != DragOperationPrivate);
435     m_effectAllowed = IEOpFromDragOp(op);
436 }
437 
setDestinationOperation(DragOperation op)438 void Clipboard::setDestinationOperation(DragOperation op)
439 {
440     ASSERT_ARG(op, op == DragOperationCopy || op == DragOperationNone || op == DragOperationLink || op == DragOperationGeneric || op == DragOperationMove || op == (DragOperation)(DragOperationGeneric | DragOperationMove));
441     m_dropEffect = IEOpFromDragOp(op);
442 }
443 
hasDropZoneType(const String & keyword)444 bool Clipboard::hasDropZoneType(const String& keyword)
445 {
446     if (keyword.startsWith("file:"))
447         return hasFileOfType(keyword.substring(5));
448 
449     if (keyword.startsWith("string:"))
450         return hasStringOfType(keyword.substring(7));
451 
452     return false;
453 }
454 
items()455 PassRefPtrWillBeRawPtr<DataTransferItemList> Clipboard::items()
456 {
457     // FIXME: According to the spec, we are supposed to return the same collection of items each
458     // time. We now return a wrapper that always wraps the *same* set of items, so JS shouldn't be
459     // able to tell, but we probably still want to fix this.
460     return DataTransferItemList::create(this, m_dataObject);
461 }
462 
dataObject() const463 PassRefPtrWillBeRawPtr<DataObject> Clipboard::dataObject() const
464 {
465     return m_dataObject;
466 }
467 
Clipboard(ClipboardType type,ClipboardAccessPolicy policy,PassRefPtrWillBeRawPtr<DataObject> dataObject)468 Clipboard::Clipboard(ClipboardType type, ClipboardAccessPolicy policy, PassRefPtrWillBeRawPtr<DataObject> dataObject)
469     : m_policy(policy)
470     , m_dropEffect("uninitialized")
471     , m_effectAllowed("uninitialized")
472     , m_clipboardType(type)
473     , m_dataObject(dataObject)
474 {
475     ScriptWrappable::init(this);
476 }
477 
setDragImage(ImageResource * image,Node * node,const IntPoint & loc)478 void Clipboard::setDragImage(ImageResource* image, Node* node, const IntPoint& loc)
479 {
480     if (!canSetDragImage())
481         return;
482 
483     m_dragImage = image;
484     m_dragLoc = loc;
485     m_dragImageElement = node;
486 }
487 
hasFileOfType(const String & type) const488 bool Clipboard::hasFileOfType(const String& type) const
489 {
490     if (!canReadTypes())
491         return false;
492 
493     RefPtrWillBeRawPtr<FileList> fileList = files();
494     if (fileList->isEmpty())
495         return false;
496 
497     for (unsigned f = 0; f < fileList->length(); f++) {
498         if (equalIgnoringCase(fileList->item(f)->type(), type))
499             return true;
500     }
501     return false;
502 }
503 
hasStringOfType(const String & type) const504 bool Clipboard::hasStringOfType(const String& type) const
505 {
506     if (!canReadTypes())
507         return false;
508 
509     return types().contains(type);
510 }
511 
convertDropZoneOperationToDragOperation(const String & dragOperation)512 DragOperation convertDropZoneOperationToDragOperation(const String& dragOperation)
513 {
514     if (dragOperation == "copy")
515         return DragOperationCopy;
516     if (dragOperation == "move")
517         return DragOperationMove;
518     if (dragOperation == "link")
519         return DragOperationLink;
520     return DragOperationNone;
521 }
522 
convertDragOperationToDropZoneOperation(DragOperation operation)523 String convertDragOperationToDropZoneOperation(DragOperation operation)
524 {
525     switch (operation) {
526     case DragOperationCopy:
527         return String("copy");
528     case DragOperationMove:
529         return String("move");
530     case DragOperationLink:
531         return String("link");
532     default:
533         return String("copy");
534     }
535 }
536 
trace(Visitor * visitor)537 void Clipboard::trace(Visitor* visitor)
538 {
539     visitor->trace(m_dataObject);
540     visitor->trace(m_dragImageElement);
541 }
542 
543 } // namespace WebCore
544