1/* 2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. 3 * Copyright (C) 2008, 2009 Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "config.h" 28#include "ClipboardChromium.h" 29 30#include "CachedImage.h" 31#include "ChromiumBridge.h" 32#include "ChromiumDataObject.h" 33#include "ClipboardUtilitiesChromium.h" 34#include "Document.h" 35#include "Element.h" 36#include "FileList.h" 37#include "Frame.h" 38#include "HTMLNames.h" 39#include "NamedAttrMap.h" 40#include "MIMETypeRegistry.h" 41#include "markup.h" 42#include "NamedNodeMap.h" 43#include "Pasteboard.h" 44#include "PlatformString.h" 45#include "Range.h" 46#include "RenderImage.h" 47#include "StringBuilder.h" 48 49namespace WebCore { 50 51using namespace HTMLNames; 52 53// We provide the IE clipboard types (URL and Text), and the clipboard types specified in the WHATWG Web Applications 1.0 draft 54// see http://www.whatwg.org/specs/web-apps/current-work/ Section 6.3.5.3 55 56enum ClipboardDataType { ClipboardDataTypeNone, ClipboardDataTypeURL, ClipboardDataTypeText, ClipboardDataTypeDownloadURL }; 57 58static ClipboardDataType clipboardTypeFromMIMEType(const String& type) 59{ 60 String cleanType = type.stripWhiteSpace().lower(); 61 62 // two special cases for IE compatibility 63 if (cleanType == "text" || cleanType == "text/plain" || cleanType.startsWith("text/plain;")) 64 return ClipboardDataTypeText; 65 if (cleanType == "url" || cleanType == "text/uri-list") 66 return ClipboardDataTypeURL; 67 if (cleanType == "downloadurl") 68 return ClipboardDataTypeDownloadURL; 69 70 return ClipboardDataTypeNone; 71} 72 73ClipboardChromium::ClipboardChromium(bool isForDragging, 74 PassRefPtr<ChromiumDataObject> dataObject, 75 ClipboardAccessPolicy policy) 76 : Clipboard(policy, isForDragging) 77 , m_dataObject(dataObject) 78{ 79} 80 81PassRefPtr<ClipboardChromium> ClipboardChromium::create(bool isForDragging, 82 PassRefPtr<ChromiumDataObject> dataObject, ClipboardAccessPolicy policy) 83{ 84 return adoptRef(new ClipboardChromium(isForDragging, dataObject, policy)); 85} 86 87void ClipboardChromium::clearData(const String& type) 88{ 89 if (policy() != ClipboardWritable || !m_dataObject) 90 return; 91 92 ClipboardDataType dataType = clipboardTypeFromMIMEType(type); 93 94 if (dataType == ClipboardDataTypeURL) { 95 m_dataObject->url = KURL(); 96 m_dataObject->urlTitle = ""; 97 } 98 99 if (dataType == ClipboardDataTypeText) 100 m_dataObject->plainText = ""; 101} 102 103void ClipboardChromium::clearAllData() 104{ 105 if (policy() != ClipboardWritable) 106 return; 107 108 m_dataObject->clear(); 109} 110 111String ClipboardChromium::getData(const String& type, bool& success) const 112{ 113 success = false; 114 if (policy() != ClipboardReadable || !m_dataObject) 115 return String(); 116 117 ClipboardDataType dataType = clipboardTypeFromMIMEType(type); 118 String text; 119 if (dataType == ClipboardDataTypeText) { 120 if (!isForDragging()) { 121 // If this isn't for a drag, it's for a cut/paste event handler. 122 // In this case, we need to check the clipboard. 123 PasteboardPrivate::ClipboardBuffer buffer = 124 Pasteboard::generalPasteboard()->isSelectionMode() ? 125 PasteboardPrivate::SelectionBuffer : 126 PasteboardPrivate::StandardBuffer; 127 text = ChromiumBridge::clipboardReadPlainText(buffer); 128 success = !text.isEmpty(); 129 } else if (!m_dataObject->plainText.isEmpty()) { 130 success = true; 131 text = m_dataObject->plainText; 132 } 133 } else if (dataType == ClipboardDataTypeURL) { 134 // FIXME: Handle the cut/paste event. This requires adding a new IPC 135 // message to get the URL from the clipboard directly. 136 if (!m_dataObject->url.isEmpty()) { 137 success = true; 138 text = m_dataObject->url.string(); 139 } 140 } 141 142 return text; 143} 144 145bool ClipboardChromium::setData(const String& type, const String& data) 146{ 147 if (policy() != ClipboardWritable) 148 return false; 149 150 ClipboardDataType winType = clipboardTypeFromMIMEType(type); 151 152 if (winType == ClipboardDataTypeURL) { 153 m_dataObject->url = KURL(ParsedURLString, data); 154 return m_dataObject->url.isValid(); 155 } 156 157 if (winType == ClipboardDataTypeText) { 158 m_dataObject->plainText = data; 159 return true; 160 } 161 162 if (winType == ClipboardDataTypeDownloadURL) { 163 m_dataObject->downloadMetadata = data; 164 KURL url = KURL(ParsedURLString, data); 165 if (url.isValid()) 166 m_dataObject->downloadURL = url; 167 return true; 168 } 169 170 return false; 171} 172 173// extensions beyond IE's API 174HashSet<String> ClipboardChromium::types() const 175{ 176 HashSet<String> results; 177 if (policy() != ClipboardReadable && policy() != ClipboardTypesReadable) 178 return results; 179 180 if (!m_dataObject) 181 return results; 182 183 if (!m_dataObject->filenames.isEmpty()) 184 results.add("Files"); 185 186 if (m_dataObject->url.isValid()) { 187 results.add("URL"); 188 results.add("text/uri-list"); 189 } 190 191 if (!m_dataObject->plainText.isEmpty()) { 192 results.add("Text"); 193 results.add("text/plain"); 194 } 195 196 return results; 197} 198 199PassRefPtr<FileList> ClipboardChromium::files() const 200{ 201 if (policy() != ClipboardReadable) 202 return FileList::create(); 203 204 if (!m_dataObject || m_dataObject->filenames.isEmpty()) 205 return FileList::create(); 206 207 RefPtr<FileList> fileList = FileList::create(); 208 for (size_t i = 0; i < m_dataObject->filenames.size(); ++i) 209 fileList->append(File::create(m_dataObject->filenames.at(i))); 210 211 return fileList.release(); 212} 213 214void ClipboardChromium::setDragImage(CachedImage* image, Node* node, const IntPoint& loc) 215{ 216 if (policy() != ClipboardImageWritable && policy() != ClipboardWritable) 217 return; 218 219 if (m_dragImage) 220 m_dragImage->removeClient(this); 221 m_dragImage = image; 222 if (m_dragImage) 223 m_dragImage->addClient(this); 224 225 m_dragLoc = loc; 226 m_dragImageElement = node; 227} 228 229void ClipboardChromium::setDragImage(CachedImage* img, const IntPoint& loc) 230{ 231 setDragImage(img, 0, loc); 232} 233 234void ClipboardChromium::setDragImageElement(Node* node, const IntPoint& loc) 235{ 236 setDragImage(0, node, loc); 237} 238 239DragImageRef ClipboardChromium::createDragImage(IntPoint& loc) const 240{ 241 DragImageRef result = 0; 242 if (m_dragImage) { 243 result = createDragImageFromImage(m_dragImage->image()); 244 loc = m_dragLoc; 245 } 246 return result; 247} 248 249static String imageToMarkup(const String& url, Element* element) 250{ 251 StringBuilder markup; 252 markup.append("<img src=\""); 253 markup.append(url); 254 markup.append("\""); 255 // Copy over attributes. If we are dragging an image, we expect things like 256 // the id to be copied as well. 257 NamedNodeMap* attrs = element->attributes(); 258 unsigned length = attrs->length(); 259 for (unsigned i = 0; i < length; ++i) { 260 Attribute* attr = attrs->attributeItem(i); 261 if (attr->localName() == "src") 262 continue; 263 markup.append(" "); 264 markup.append(attr->localName()); 265 markup.append("=\""); 266 String escapedAttr = attr->value(); 267 escapedAttr.replace("\"", """); 268 markup.append(escapedAttr); 269 markup.append("\""); 270 } 271 272 markup.append("/>"); 273 return markup.toString(); 274} 275 276static CachedImage* getCachedImage(Element* element) 277{ 278 // Attempt to pull CachedImage from element 279 ASSERT(element); 280 RenderObject* renderer = element->renderer(); 281 if (!renderer || !renderer->isImage()) 282 return 0; 283 284 RenderImage* image = toRenderImage(renderer); 285 if (image->cachedImage() && !image->cachedImage()->errorOccurred()) 286 return image->cachedImage(); 287 288 return 0; 289} 290 291static void writeImageToDataObject(ChromiumDataObject* dataObject, Element* element, 292 const KURL& url) 293{ 294 // Shove image data into a DataObject for use as a file 295 CachedImage* cachedImage = getCachedImage(element); 296 if (!cachedImage || !cachedImage->image() || !cachedImage->isLoaded()) 297 return; 298 299 SharedBuffer* imageBuffer = cachedImage->image()->data(); 300 if (!imageBuffer || !imageBuffer->size()) 301 return; 302 303 dataObject->fileContent = imageBuffer; 304 305 // Determine the filename for the file contents of the image. We try to 306 // use the alt tag if one exists, otherwise we fall back on the suggested 307 // filename in the http header, and finally we resort to using the filename 308 // in the URL. 309 String extension = MIMETypeRegistry::getPreferredExtensionForMIMEType( 310 cachedImage->response().mimeType()); 311 dataObject->fileExtension = extension.isEmpty() ? "" : "." + extension; 312 String title = element->getAttribute(altAttr); 313 if (title.isEmpty()) 314 title = cachedImage->response().suggestedFilename(); 315 316 title = ClipboardChromium::validateFileName(title, dataObject); 317 dataObject->fileContentFilename = title + dataObject->fileExtension; 318} 319 320void ClipboardChromium::declareAndWriteDragImage(Element* element, const KURL& url, const String& title, Frame* frame) 321{ 322 if (!m_dataObject) 323 return; 324 325 m_dataObject->url = url; 326 m_dataObject->urlTitle = title; 327 328 // Write the bytes in the image to the file format. 329 writeImageToDataObject(m_dataObject.get(), element, url); 330 331 AtomicString imageURL = element->getAttribute(srcAttr); 332 if (imageURL.isEmpty()) 333 return; 334 335 String fullURL = frame->document()->completeURL(deprecatedParseURL(imageURL)); 336 if (fullURL.isEmpty()) 337 return; 338 339 // Put img tag on the clipboard referencing the image 340 m_dataObject->textHtml = imageToMarkup(fullURL, element); 341} 342 343void ClipboardChromium::writeURL(const KURL& url, const String& title, Frame*) 344{ 345 if (!m_dataObject) 346 return; 347 m_dataObject->url = url; 348 m_dataObject->urlTitle = title; 349 350 // The URL can also be used as plain text. 351 m_dataObject->plainText = url.string(); 352 353 // The URL can also be used as an HTML fragment. 354 m_dataObject->textHtml = urlToMarkup(url, title); 355 m_dataObject->htmlBaseUrl = url; 356} 357 358void ClipboardChromium::writeRange(Range* selectedRange, Frame* frame) 359{ 360 ASSERT(selectedRange); 361 if (!m_dataObject) 362 return; 363 364 m_dataObject->textHtml = createMarkup(selectedRange, 0, 365 AnnotateForInterchange); 366 m_dataObject->htmlBaseUrl = frame->document()->url(); 367 368 String str = frame->selectedText(); 369#if OS(WINDOWS) 370 replaceNewlinesWithWindowsStyleNewlines(str); 371#endif 372 replaceNBSPWithSpace(str); 373 m_dataObject->plainText = str; 374} 375 376void ClipboardChromium::writePlainText(const String& text) 377{ 378 if (!m_dataObject) 379 return; 380 381 String str = text; 382#if OS(WINDOWS) 383 replaceNewlinesWithWindowsStyleNewlines(str); 384#endif 385 replaceNBSPWithSpace(str); 386 m_dataObject->plainText = str; 387} 388 389bool ClipboardChromium::hasData() 390{ 391 if (!m_dataObject) 392 return false; 393 394 return m_dataObject->hasData(); 395} 396 397} // namespace WebCore 398