| /* |
| * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. |
| * Copyright (C) 2008, 2009 Google Inc. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "ClipboardChromium.h" |
| |
| #include "CachedImage.h" |
| #include "ChromiumBridge.h" |
| #include "ChromiumDataObject.h" |
| #include "ClipboardUtilitiesChromium.h" |
| #include "Document.h" |
| #include "Element.h" |
| #include "FileList.h" |
| #include "Frame.h" |
| #include "HTMLNames.h" |
| #include "NamedAttrMap.h" |
| #include "MIMETypeRegistry.h" |
| #include "markup.h" |
| #include "NamedNodeMap.h" |
| #include "Pasteboard.h" |
| #include "PlatformString.h" |
| #include "Range.h" |
| #include "RenderImage.h" |
| #include "StringBuilder.h" |
| |
| namespace WebCore { |
| |
| using namespace HTMLNames; |
| |
| // We provide the IE clipboard types (URL and Text), and the clipboard types specified in the WHATWG Web Applications 1.0 draft |
| // see http://www.whatwg.org/specs/web-apps/current-work/ Section 6.3.5.3 |
| |
| enum ClipboardDataType { ClipboardDataTypeNone, ClipboardDataTypeURL, ClipboardDataTypeText, ClipboardDataTypeDownloadURL }; |
| |
| static ClipboardDataType clipboardTypeFromMIMEType(const String& type) |
| { |
| String cleanType = type.stripWhiteSpace().lower(); |
| |
| // two special cases for IE compatibility |
| if (cleanType == "text" || cleanType == "text/plain" || cleanType.startsWith("text/plain;")) |
| return ClipboardDataTypeText; |
| if (cleanType == "url" || cleanType == "text/uri-list") |
| return ClipboardDataTypeURL; |
| if (cleanType == "downloadurl") |
| return ClipboardDataTypeDownloadURL; |
| |
| return ClipboardDataTypeNone; |
| } |
| |
| ClipboardChromium::ClipboardChromium(bool isForDragging, |
| PassRefPtr<ChromiumDataObject> dataObject, |
| ClipboardAccessPolicy policy) |
| : Clipboard(policy, isForDragging) |
| , m_dataObject(dataObject) |
| { |
| } |
| |
| PassRefPtr<ClipboardChromium> ClipboardChromium::create(bool isForDragging, |
| PassRefPtr<ChromiumDataObject> dataObject, ClipboardAccessPolicy policy) |
| { |
| return adoptRef(new ClipboardChromium(isForDragging, dataObject, policy)); |
| } |
| |
| void ClipboardChromium::clearData(const String& type) |
| { |
| if (policy() != ClipboardWritable || !m_dataObject) |
| return; |
| |
| ClipboardDataType dataType = clipboardTypeFromMIMEType(type); |
| |
| if (dataType == ClipboardDataTypeURL) { |
| m_dataObject->url = KURL(); |
| m_dataObject->urlTitle = ""; |
| } |
| |
| if (dataType == ClipboardDataTypeText) |
| m_dataObject->plainText = ""; |
| } |
| |
| void ClipboardChromium::clearAllData() |
| { |
| if (policy() != ClipboardWritable) |
| return; |
| |
| m_dataObject->clear(); |
| } |
| |
| String ClipboardChromium::getData(const String& type, bool& success) const |
| { |
| success = false; |
| if (policy() != ClipboardReadable || !m_dataObject) |
| return String(); |
| |
| ClipboardDataType dataType = clipboardTypeFromMIMEType(type); |
| String text; |
| if (dataType == ClipboardDataTypeText) { |
| if (!isForDragging()) { |
| // If this isn't for a drag, it's for a cut/paste event handler. |
| // In this case, we need to check the clipboard. |
| PasteboardPrivate::ClipboardBuffer buffer = |
| Pasteboard::generalPasteboard()->isSelectionMode() ? |
| PasteboardPrivate::SelectionBuffer : |
| PasteboardPrivate::StandardBuffer; |
| text = ChromiumBridge::clipboardReadPlainText(buffer); |
| success = !text.isEmpty(); |
| } else if (!m_dataObject->plainText.isEmpty()) { |
| success = true; |
| text = m_dataObject->plainText; |
| } |
| } else if (dataType == ClipboardDataTypeURL) { |
| // FIXME: Handle the cut/paste event. This requires adding a new IPC |
| // message to get the URL from the clipboard directly. |
| if (!m_dataObject->url.isEmpty()) { |
| success = true; |
| text = m_dataObject->url.string(); |
| } |
| } |
| |
| return text; |
| } |
| |
| bool ClipboardChromium::setData(const String& type, const String& data) |
| { |
| if (policy() != ClipboardWritable) |
| return false; |
| |
| ClipboardDataType winType = clipboardTypeFromMIMEType(type); |
| |
| if (winType == ClipboardDataTypeURL) { |
| m_dataObject->url = KURL(ParsedURLString, data); |
| return m_dataObject->url.isValid(); |
| } |
| |
| if (winType == ClipboardDataTypeText) { |
| m_dataObject->plainText = data; |
| return true; |
| } |
| |
| if (winType == ClipboardDataTypeDownloadURL) { |
| KURL url = KURL(ParsedURLString, data); |
| if (url.isValid()) { |
| m_dataObject->downloadURL = url; |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| // extensions beyond IE's API |
| HashSet<String> ClipboardChromium::types() const |
| { |
| HashSet<String> results; |
| if (policy() != ClipboardReadable && policy() != ClipboardTypesReadable) |
| return results; |
| |
| if (!m_dataObject) |
| return results; |
| |
| if (!m_dataObject->filenames.isEmpty()) |
| results.add("Files"); |
| |
| if (m_dataObject->url.isValid()) { |
| results.add("URL"); |
| results.add("text/uri-list"); |
| } |
| |
| if (!m_dataObject->plainText.isEmpty()) { |
| results.add("Text"); |
| results.add("text/plain"); |
| } |
| |
| return results; |
| } |
| |
| PassRefPtr<FileList> ClipboardChromium::files() const |
| { |
| if (policy() != ClipboardReadable) |
| return FileList::create(); |
| |
| if (!m_dataObject || m_dataObject->filenames.isEmpty()) |
| return FileList::create(); |
| |
| RefPtr<FileList> fileList = FileList::create(); |
| for (size_t i = 0; i < m_dataObject->filenames.size(); ++i) |
| fileList->append(File::create(m_dataObject->filenames.at(i))); |
| |
| return fileList.release(); |
| } |
| |
| void ClipboardChromium::setDragImage(CachedImage* image, Node* node, const IntPoint& loc) |
| { |
| if (policy() != ClipboardImageWritable && policy() != ClipboardWritable) |
| return; |
| |
| if (m_dragImage) |
| m_dragImage->removeClient(this); |
| m_dragImage = image; |
| if (m_dragImage) |
| m_dragImage->addClient(this); |
| |
| m_dragLoc = loc; |
| m_dragImageElement = node; |
| } |
| |
| void ClipboardChromium::setDragImage(CachedImage* img, const IntPoint& loc) |
| { |
| setDragImage(img, 0, loc); |
| } |
| |
| void ClipboardChromium::setDragImageElement(Node* node, const IntPoint& loc) |
| { |
| setDragImage(0, node, loc); |
| } |
| |
| DragImageRef ClipboardChromium::createDragImage(IntPoint& loc) const |
| { |
| DragImageRef result = 0; |
| if (m_dragImage) { |
| result = createDragImageFromImage(m_dragImage->image()); |
| loc = m_dragLoc; |
| } |
| return result; |
| } |
| |
| static String imageToMarkup(const String& url, Element* element) |
| { |
| StringBuilder markup; |
| markup.append("<img src=\""); |
| markup.append(url); |
| markup.append("\""); |
| // Copy over attributes. If we are dragging an image, we expect things like |
| // the id to be copied as well. |
| NamedNodeMap* attrs = element->attributes(); |
| unsigned length = attrs->length(); |
| for (unsigned i = 0; i < length; ++i) { |
| Attribute* attr = attrs->attributeItem(i); |
| if (attr->localName() == "src") |
| continue; |
| markup.append(" "); |
| markup.append(attr->localName()); |
| markup.append("=\""); |
| String escapedAttr = attr->value(); |
| escapedAttr.replace("\"", """); |
| markup.append(escapedAttr); |
| markup.append("\""); |
| } |
| |
| markup.append("/>"); |
| return markup.toString(); |
| } |
| |
| static CachedImage* getCachedImage(Element* element) |
| { |
| // Attempt to pull CachedImage from element |
| ASSERT(element); |
| RenderObject* renderer = element->renderer(); |
| if (!renderer || !renderer->isImage()) |
| return 0; |
| |
| RenderImage* image = toRenderImage(renderer); |
| if (image->cachedImage() && !image->cachedImage()->errorOccurred()) |
| return image->cachedImage(); |
| |
| return 0; |
| } |
| |
| static void writeImageToDataObject(ChromiumDataObject* dataObject, Element* element, |
| const KURL& url) |
| { |
| // Shove image data into a DataObject for use as a file |
| CachedImage* cachedImage = getCachedImage(element); |
| if (!cachedImage || !cachedImage->image() || !cachedImage->isLoaded()) |
| return; |
| |
| SharedBuffer* imageBuffer = cachedImage->image()->data(); |
| if (!imageBuffer || !imageBuffer->size()) |
| return; |
| |
| dataObject->fileContent = imageBuffer; |
| |
| // Determine the filename for the file contents of the image. We try to |
| // use the alt tag if one exists, otherwise we fall back on the suggested |
| // filename in the http header, and finally we resort to using the filename |
| // in the URL. |
| String extension = MIMETypeRegistry::getPreferredExtensionForMIMEType( |
| cachedImage->response().mimeType()); |
| dataObject->fileExtension = extension.isEmpty() ? "" : "." + extension; |
| String title = element->getAttribute(altAttr); |
| if (title.isEmpty()) |
| title = cachedImage->response().suggestedFilename(); |
| |
| title = ClipboardChromium::validateFileName(title, dataObject); |
| dataObject->fileContentFilename = title + dataObject->fileExtension; |
| } |
| |
| void ClipboardChromium::declareAndWriteDragImage(Element* element, const KURL& url, const String& title, Frame* frame) |
| { |
| if (!m_dataObject) |
| return; |
| |
| m_dataObject->url = url; |
| m_dataObject->urlTitle = title; |
| |
| // Write the bytes in the image to the file format. |
| writeImageToDataObject(m_dataObject.get(), element, url); |
| |
| AtomicString imageURL = element->getAttribute(srcAttr); |
| if (imageURL.isEmpty()) |
| return; |
| |
| String fullURL = frame->document()->completeURL(deprecatedParseURL(imageURL)); |
| if (fullURL.isEmpty()) |
| return; |
| |
| // Put img tag on the clipboard referencing the image |
| m_dataObject->textHtml = imageToMarkup(fullURL, element); |
| } |
| |
| void ClipboardChromium::writeURL(const KURL& url, const String& title, Frame*) |
| { |
| if (!m_dataObject) |
| return; |
| m_dataObject->url = url; |
| m_dataObject->urlTitle = title; |
| |
| // The URL can also be used as plain text. |
| m_dataObject->plainText = url.string(); |
| |
| // The URL can also be used as an HTML fragment. |
| m_dataObject->textHtml = urlToMarkup(url, title); |
| m_dataObject->htmlBaseUrl = url; |
| } |
| |
| void ClipboardChromium::writeRange(Range* selectedRange, Frame* frame) |
| { |
| ASSERT(selectedRange); |
| if (!m_dataObject) |
| return; |
| |
| m_dataObject->textHtml = createMarkup(selectedRange, 0, |
| AnnotateForInterchange); |
| #if OS(DARWIN) |
| m_dataObject->textHtml = String("<meta charset='utf-8' id='webkit-interchange-charset'>") + m_dataObject->textHtml; |
| #endif |
| m_dataObject->htmlBaseUrl = frame->document()->url(); |
| |
| String str = frame->selectedText(); |
| #if OS(WINDOWS) |
| replaceNewlinesWithWindowsStyleNewlines(str); |
| #endif |
| replaceNBSPWithSpace(str); |
| m_dataObject->plainText = str; |
| } |
| |
| bool ClipboardChromium::hasData() |
| { |
| if (!m_dataObject) |
| return false; |
| |
| return m_dataObject->hasData(); |
| } |
| |
| } // namespace WebCore |