2 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "core/clipboard/Clipboard.h"
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"
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)
51 // yep, it's really just this fixed set
52 if (op == "uninitialized")
53 return DragOperationEvery;
55 return DragOperationNone;
57 return DragOperationCopy;
59 return DragOperationLink;
61 return (DragOperation)(DragOperationGeneric | DragOperationMove);
63 return (DragOperation)(DragOperationCopy | DragOperationLink);
65 return (DragOperation)(DragOperationCopy | DragOperationGeneric | DragOperationMove);
67 return (DragOperation)(DragOperationLink | DragOperationGeneric | DragOperationMove);
69 return DragOperationEvery;
70 return DragOperationPrivate; // really a marker for "no conversion"
73 static String IEOpFromDragOp(DragOperation op)
75 bool moveSet = !!((DragOperationGeneric | DragOperationMove) & op);
77 if ((moveSet && (op & DragOperationCopy) && (op & DragOperationLink))
78 || (op == DragOperationEvery))
80 if (moveSet && (op & DragOperationCopy))
82 if (moveSet && (op & DragOperationLink))
84 if ((op & DragOperationCopy) && (op & DragOperationLink))
88 if (op & DragOperationCopy)
90 if (op & DragOperationLink)
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)
99 String cleanType = type.stripWhiteSpace().lower();
100 if (cleanType == mimeTypeText || cleanType.startsWith(mimeTypeTextPlainEtc))
101 return mimeTypeTextPlain;
102 if (cleanType == mimeTypeURL) {
104 *convertToURL = true;
105 return mimeTypeTextURIList;
110 PassRefPtr<Clipboard> Clipboard::create(ClipboardType type, ClipboardAccessPolicy policy, PassRefPtr<DataObject> dataObject)
112 return adoptRef(new Clipboard(type, policy , dataObject));
115 Clipboard::~Clipboard()
119 void Clipboard::setDropEffect(const String &effect)
121 if (!isForDragAndDrop())
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")
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.
131 m_dropEffect = effect;
134 void Clipboard::setEffectAllowed(const String &effect)
136 if (!isForDragAndDrop())
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.
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.
151 m_effectAllowed = effect;
154 void Clipboard::clearData(const String& type)
160 m_dataObject->clearAll();
162 m_dataObject->clearData(normalizeType(type));
165 String Clipboard::getData(const String& type) const
170 bool convertToURL = false;
171 String data = m_dataObject->getData(normalizeType(type, &convertToURL));
174 return convertURIListToURL(data);
177 bool Clipboard::setData(const String& type, const String& data)
182 return m_dataObject->setData(normalizeType(type), data);
185 // extensions beyond IE's API
186 Vector<String> Clipboard::types() const
188 Vector<String> types;
192 ListHashSet<String> typesSet = m_dataObject->types();
193 types.appendRange(typesSet.begin(), typesSet.end());
197 PassRefPtr<FileList> Clipboard::files() const
199 RefPtr<FileList> files = FileList::create();
201 return files.release();
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()));
211 return files.release();
214 void Clipboard::setDragImage(Element* image, int x, int y, ExceptionState& exceptionState)
216 if (!isForDragAndDrop())
220 exceptionState.throwTypeError("setDragImage: Invalid first argument");
223 IntPoint location(x, y);
224 if (image->hasTagName(HTMLNames::imgTag) && !image->inDocument())
225 setDragImageResource(toHTMLImageElement(image)->cachedImage(), location);
227 setDragImageElement(image, location);
230 void Clipboard::setDragImageResource(ImageResource* img, const IntPoint& loc)
232 setDragImage(img, 0, loc);
235 void Clipboard::setDragImageElement(Node* node, const IntPoint& loc)
237 setDragImage(0, node, loc);
240 PassOwnPtr<DragImage> Clipboard::createDragImage(IntPoint& loc, Frame* frame) const
242 if (m_dragImageElement) {
244 return frame->nodeImage(m_dragImageElement.get());
248 return DragImage::create(m_dragImage->image());
253 static ImageResource* getImageResource(Element* element)
255 // Attempt to pull ImageResource from element
257 RenderObject* renderer = element->renderer();
258 if (!renderer || !renderer->isImage())
261 RenderImage* image = toRenderImage(renderer);
262 if (image->cachedImage() && !image->cachedImage()->errorOccurred())
263 return image->cachedImage();
268 static void writeImageToDataObject(DataObject* dataObject, Element* element, const KURL& url)
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())
275 SharedBuffer* imageBuffer = cachedImage->imageForRenderer(element->renderer())->data();
276 if (!imageBuffer || !imageBuffer->size())
279 String imageExtension = cachedImage->image()->filenameExtension();
280 ASSERT(!imageExtension.isEmpty());
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();
287 String fileExtension;
288 if (filename.isEmpty()) {
289 filename = element->getAttribute(HTMLNames::altAttr);
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);
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;
307 imageExtension = "." + imageExtension;
308 validateFilename(filename, imageExtension);
310 dataObject->addSharedBuffer(filename + imageExtension, imageBuffer);
313 void Clipboard::declareAndWriteDragImage(Element* element, const KURL& url, const String& title)
318 m_dataObject->setURLAndTitle(url, title);
320 // Write the bytes in the image to the file format.
321 writeImageToDataObject(m_dataObject.get(), element, url);
323 // Put img tag on the clipboard referencing the image
324 m_dataObject->setData(mimeTypeTextHTML, createMarkup(element, IncludeNode, 0, ResolveAllURLs));
327 void Clipboard::writeURL(const KURL& url, const String& title)
331 ASSERT(!url.isEmpty());
333 m_dataObject->setURLAndTitle(url, title);
335 // The URL can also be used as plain text.
336 m_dataObject->setData(mimeTypeTextPlain, url.string());
338 // The URL can also be used as an HTML fragment.
339 m_dataObject->setHTMLAndBaseURL(urlToMarkup(url, title), url);
342 void Clipboard::writeRange(Range* selectedRange, Frame* frame)
344 ASSERT(selectedRange);
348 m_dataObject->setHTMLAndBaseURL(createMarkup(selectedRange, 0, AnnotateForInterchange, false, ResolveNonLocalURLs), frame->document()->url());
350 String str = frame->selectedTextForClipboard();
352 replaceNewlinesWithWindowsStyleNewlines(str);
354 replaceNBSPWithSpace(str);
355 m_dataObject->setData(mimeTypeTextPlain, str);
358 void Clipboard::writePlainText(const String& text)
365 replaceNewlinesWithWindowsStyleNewlines(str);
367 replaceNBSPWithSpace(str);
369 m_dataObject->setData(mimeTypeTextPlain, str);
372 bool Clipboard::hasData()
374 ASSERT(isForDragAndDrop());
376 return m_dataObject->length() > 0;
379 void Clipboard::setAccessPolicy(ClipboardAccessPolicy policy)
381 // once you go numb, can never go back
382 ASSERT(m_policy != ClipboardNumb || policy == ClipboardNumb);
386 bool Clipboard::canReadTypes() const
388 return m_policy == ClipboardReadable || m_policy == ClipboardTypesReadable || m_policy == ClipboardWritable;
391 bool Clipboard::canReadData() const
393 return m_policy == ClipboardReadable || m_policy == ClipboardWritable;
396 bool Clipboard::canWriteData() const
398 return m_policy == ClipboardWritable;
401 bool Clipboard::canSetDragImage() const
403 return m_policy == ClipboardImageWritable || m_policy == ClipboardWritable;
406 DragOperation Clipboard::sourceOperation() const
408 DragOperation op = dragOpFromIEOp(m_effectAllowed);
409 ASSERT(op != DragOperationPrivate);
413 DragOperation Clipboard::destinationOperation() const
415 DragOperation op = dragOpFromIEOp(m_dropEffect);
416 ASSERT(op == DragOperationCopy || op == DragOperationNone || op == DragOperationLink || op == (DragOperation)(DragOperationGeneric | DragOperationMove) || op == DragOperationEvery);
420 void Clipboard::setSourceOperation(DragOperation op)
422 ASSERT_ARG(op, op != DragOperationPrivate);
423 m_effectAllowed = IEOpFromDragOp(op);
426 void Clipboard::setDestinationOperation(DragOperation op)
428 ASSERT_ARG(op, op == DragOperationCopy || op == DragOperationNone || op == DragOperationLink || op == DragOperationGeneric || op == DragOperationMove || op == (DragOperation)(DragOperationGeneric | DragOperationMove));
429 m_dropEffect = IEOpFromDragOp(op);
432 bool Clipboard::hasDropZoneType(const String& keyword)
434 if (keyword.startsWith("file:"))
435 return hasFileOfType(keyword.substring(5));
437 if (keyword.startsWith("string:"))
438 return hasStringOfType(keyword.substring(7));
443 PassRefPtr<DataTransferItemList> Clipboard::items()
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);
451 PassRefPtr<DataObject> Clipboard::dataObject() const
456 Clipboard::Clipboard(ClipboardType type, ClipboardAccessPolicy policy, PassRefPtr<DataObject> dataObject)
458 , m_dropEffect("uninitialized")
459 , m_effectAllowed("uninitialized")
460 , m_clipboardType(type)
461 , m_dataObject(dataObject)
463 ScriptWrappable::init(this);
466 void Clipboard::setDragImage(ImageResource* image, Node* node, const IntPoint& loc)
468 if (!canSetDragImage())
473 m_dragImageElement = node;
476 bool Clipboard::hasFileOfType(const String& type) const
481 RefPtr<FileList> fileList = files();
482 if (fileList->isEmpty())
485 for (unsigned f = 0; f < fileList->length(); f++) {
486 if (equalIgnoringCase(fileList->item(f)->type(), type))
492 bool Clipboard::hasStringOfType(const String& type) const
497 return types().contains(type);
500 DragOperation convertDropZoneOperationToDragOperation(const String& dragOperation)
502 if (dragOperation == "copy")
503 return DragOperationCopy;
504 if (dragOperation == "move")
505 return DragOperationMove;
506 if (dragOperation == "link")
507 return DragOperationLink;
508 return DragOperationNone;
511 String convertDragOperationToDropZoneOperation(DragOperation operation)
514 case DragOperationCopy:
515 return String("copy");
516 case DragOperationMove:
517 return String("move");
518 case DragOperationLink:
519 return String("link");
521 return String("copy");
525 } // namespace WebCore