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 "ChromiumDataObject.h" 32 #include "ClipboardMimeTypes.h" 33 #include "ClipboardUtilitiesChromium.h" 34 #include "DataTransferItemsChromium.h" 35 #include "Document.h" 36 #include "DragData.h" 37 #include "Element.h" 38 #include "FileList.h" 39 #include "Frame.h" 40 #include "HTMLNames.h" 41 #include "HTMLParserIdioms.h" 42 #include "Image.h" 43 #include "MIMETypeRegistry.h" 44 #include "NamedNodeMap.h" 45 #include "Range.h" 46 #include "RenderImage.h" 47 #include "ScriptExecutionContext.h" 48 #include "markup.h" 49 50 #include <wtf/text/StringBuilder.h> 51 #include <wtf/text/WTFString.h> 52 53 namespace WebCore { 54 55 using namespace HTMLNames; 56 57 // We provide the IE clipboard types (URL and Text), and the clipboard types specified in the WHATWG Web Applications 1.0 draft 58 // see http://www.whatwg.org/specs/web-apps/current-work/ Section 6.3.5.3 59 60 static String normalizeType(const String& type) 61 { 62 String cleanType = type.stripWhiteSpace().lower(); 63 if (cleanType == mimeTypeText || cleanType.startsWith(mimeTypeTextPlainEtc)) 64 return mimeTypeTextPlain; 65 return cleanType; 66 } 67 68 PassRefPtr<Clipboard> Clipboard::create(ClipboardAccessPolicy policy, DragData* dragData, Frame* frame) 69 { 70 return ClipboardChromium::create(DragAndDrop, dragData->platformData(), policy, frame); 71 } 72 73 ClipboardChromium::ClipboardChromium(ClipboardType clipboardType, 74 PassRefPtr<ChromiumDataObject> dataObject, 75 ClipboardAccessPolicy policy, 76 Frame* frame) 77 : Clipboard(policy, clipboardType) 78 , m_dataObject(dataObject) 79 , m_frame(frame) 80 { 81 } 82 83 PassRefPtr<ClipboardChromium> ClipboardChromium::create(ClipboardType clipboardType, 84 PassRefPtr<ChromiumDataObject> dataObject, ClipboardAccessPolicy policy, Frame* frame) 85 { 86 return adoptRef(new ClipboardChromium(clipboardType, dataObject, policy, frame)); 87 } 88 89 void ClipboardChromium::clearData(const String& type) 90 { 91 if (policy() != ClipboardWritable || !m_dataObject) 92 return; 93 94 m_dataObject->clearData(normalizeType(type)); 95 96 ASSERT_NOT_REACHED(); 97 } 98 99 void ClipboardChromium::clearAllData() 100 { 101 if (policy() != ClipboardWritable) 102 return; 103 104 m_dataObject->clearAll(); 105 } 106 107 String ClipboardChromium::getData(const String& type, bool& success) const 108 { 109 success = false; 110 if (policy() != ClipboardReadable || !m_dataObject) 111 return String(); 112 113 return m_dataObject->getData(normalizeType(type), success); 114 } 115 116 bool ClipboardChromium::setData(const String& type, const String& data) 117 { 118 if (policy() != ClipboardWritable) 119 return false; 120 121 return m_dataObject->setData(normalizeType(type), data); 122 } 123 124 // extensions beyond IE's API 125 HashSet<String> ClipboardChromium::types() const 126 { 127 HashSet<String> results; 128 if (policy() != ClipboardReadable && policy() != ClipboardTypesReadable) 129 return results; 130 131 if (!m_dataObject) 132 return results; 133 134 results = m_dataObject->types(); 135 136 if (m_dataObject->containsFilenames()) 137 results.add(mimeTypeFiles); 138 139 return results; 140 } 141 142 PassRefPtr<FileList> ClipboardChromium::files() const 143 { 144 if (policy() != ClipboardReadable) 145 return FileList::create(); 146 147 if (!m_dataObject) 148 return FileList::create(); 149 150 const Vector<String>& filenames = m_dataObject->filenames(); 151 RefPtr<FileList> fileList = FileList::create(); 152 for (size_t i = 0; i < filenames.size(); ++i) 153 fileList->append(File::create(filenames.at(i))); 154 155 return fileList.release(); 156 } 157 158 void ClipboardChromium::setDragImage(CachedImage* image, Node* node, const IntPoint& loc) 159 { 160 if (policy() != ClipboardImageWritable && policy() != ClipboardWritable) 161 return; 162 163 if (m_dragImage) 164 m_dragImage->removeClient(this); 165 m_dragImage = image; 166 if (m_dragImage) 167 m_dragImage->addClient(this); 168 169 m_dragLoc = loc; 170 m_dragImageElement = node; 171 } 172 173 void ClipboardChromium::setDragImage(CachedImage* img, const IntPoint& loc) 174 { 175 setDragImage(img, 0, loc); 176 } 177 178 void ClipboardChromium::setDragImageElement(Node* node, const IntPoint& loc) 179 { 180 setDragImage(0, node, loc); 181 } 182 183 DragImageRef ClipboardChromium::createDragImage(IntPoint& loc) const 184 { 185 DragImageRef result = 0; 186 if (m_dragImageElement) { 187 if (m_frame) { 188 result = m_frame->nodeImage(m_dragImageElement.get()); 189 loc = m_dragLoc; 190 } 191 } else if (m_dragImage) { 192 result = createDragImageFromImage(m_dragImage->image()); 193 loc = m_dragLoc; 194 } 195 return result; 196 } 197 198 static String imageToMarkup(const String& url, Element* element) 199 { 200 StringBuilder markup; 201 markup.append("<img src=\""); 202 markup.append(url); 203 markup.append('"'); 204 // Copy over attributes. If we are dragging an image, we expect things like 205 // the id to be copied as well. 206 NamedNodeMap* attrs = element->attributes(); 207 unsigned length = attrs->length(); 208 for (unsigned i = 0; i < length; ++i) { 209 Attribute* attr = attrs->attributeItem(i); 210 if (attr->localName() == "src") 211 continue; 212 markup.append(' '); 213 markup.append(attr->localName()); 214 markup.append("=\""); 215 String escapedAttr = attr->value(); 216 escapedAttr.replace("\"", """); 217 markup.append(escapedAttr); 218 markup.append('"'); 219 } 220 221 markup.append("/>"); 222 return markup.toString(); 223 } 224 225 static CachedImage* getCachedImage(Element* element) 226 { 227 // Attempt to pull CachedImage from element 228 ASSERT(element); 229 RenderObject* renderer = element->renderer(); 230 if (!renderer || !renderer->isImage()) 231 return 0; 232 233 RenderImage* image = toRenderImage(renderer); 234 if (image->cachedImage() && !image->cachedImage()->errorOccurred()) 235 return image->cachedImage(); 236 237 return 0; 238 } 239 240 static void writeImageToDataObject(ChromiumDataObject* dataObject, Element* element, 241 const KURL& url) 242 { 243 // Shove image data into a DataObject for use as a file 244 CachedImage* cachedImage = getCachedImage(element); 245 if (!cachedImage || !cachedImage->image() || !cachedImage->isLoaded()) 246 return; 247 248 SharedBuffer* imageBuffer = cachedImage->image()->data(); 249 if (!imageBuffer || !imageBuffer->size()) 250 return; 251 252 dataObject->setFileContent(imageBuffer); 253 254 // Determine the filename for the file contents of the image. We try to 255 // use the alt tag if one exists, otherwise we fall back on the suggested 256 // filename in the http header, and finally we resort to using the filename 257 // in the URL. 258 String extension = MIMETypeRegistry::getPreferredExtensionForMIMEType( 259 cachedImage->response().mimeType()); 260 dataObject->setFileExtension(extension.isEmpty() ? "" : "." + extension); 261 String title = element->getAttribute(altAttr); 262 if (title.isEmpty()) 263 title = cachedImage->response().suggestedFilename(); 264 265 title = ClipboardChromium::validateFileName(title, dataObject); 266 dataObject->setFileContentFilename(title + dataObject->fileExtension()); 267 } 268 269 void ClipboardChromium::declareAndWriteDragImage(Element* element, const KURL& url, const String& title, Frame* frame) 270 { 271 if (!m_dataObject) 272 return; 273 274 m_dataObject->setData(mimeTypeURL, url); 275 m_dataObject->setUrlTitle(title); 276 277 // Write the bytes in the image to the file format. 278 writeImageToDataObject(m_dataObject.get(), element, url); 279 280 AtomicString imageURL = element->getAttribute(srcAttr); 281 if (imageURL.isEmpty()) 282 return; 283 284 String fullURL = frame->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(imageURL)); 285 if (fullURL.isEmpty()) 286 return; 287 288 // Put img tag on the clipboard referencing the image 289 m_dataObject->setData(mimeTypeTextHTML, imageToMarkup(fullURL, element)); 290 } 291 292 void ClipboardChromium::writeURL(const KURL& url, const String& title, Frame*) 293 { 294 if (!m_dataObject) 295 return; 296 ASSERT(!url.isEmpty()); 297 m_dataObject->setData(mimeTypeURL, url); 298 m_dataObject->setUrlTitle(title); 299 300 // The URL can also be used as plain text. 301 m_dataObject->setData(mimeTypeTextPlain, url.string()); 302 303 // The URL can also be used as an HTML fragment. 304 m_dataObject->setData(mimeTypeTextHTML, urlToMarkup(url, title)); 305 m_dataObject->setHtmlBaseUrl(url); 306 } 307 308 void ClipboardChromium::writeRange(Range* selectedRange, Frame* frame) 309 { 310 ASSERT(selectedRange); 311 if (!m_dataObject) 312 return; 313 314 m_dataObject->setData(mimeTypeTextHTML, createMarkup(selectedRange, 0, AnnotateForInterchange, false, AbsoluteURLs)); 315 m_dataObject->setHtmlBaseUrl(frame->document()->url()); 316 317 String str = frame->editor()->selectedText(); 318 #if OS(WINDOWS) 319 replaceNewlinesWithWindowsStyleNewlines(str); 320 #endif 321 replaceNBSPWithSpace(str); 322 m_dataObject->setData(mimeTypeTextPlain, str); 323 } 324 325 void ClipboardChromium::writePlainText(const String& text) 326 { 327 if (!m_dataObject) 328 return; 329 330 String str = text; 331 #if OS(WINDOWS) 332 replaceNewlinesWithWindowsStyleNewlines(str); 333 #endif 334 replaceNBSPWithSpace(str); 335 m_dataObject->setData(mimeTypeTextPlain, str); 336 } 337 338 bool ClipboardChromium::hasData() 339 { 340 if (!m_dataObject) 341 return false; 342 343 return m_dataObject->hasData(); 344 } 345 346 #if ENABLE(DATA_TRANSFER_ITEMS) 347 PassRefPtr<DataTransferItems> ClipboardChromium::items() 348 { 349 RefPtr<DataTransferItemsChromium> items = DataTransferItemsChromium::create(this, m_frame->document()->scriptExecutionContext()); 350 351 if (!m_dataObject) 352 return items; 353 354 if (isForCopyAndPaste() && policy() == ClipboardReadable) { 355 // Iterate through the types and add them. 356 HashSet<String> types = m_dataObject->types(); 357 for (HashSet<String>::const_iterator it = types.begin(); it != types.end(); ++it) 358 items->addPasteboardItem(*it); 359 } 360 return items; 361 } 362 #endif 363 364 } // namespace WebCore 365