| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2004, 2005, 2006, 2008, 2010 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 #import "config.h" | |
| 27 #import "ClipboardMac.h" | |
| 28 | |
| 29 #import "CachedImage.h" | |
| 30 #import "CachedImageClient.h" | |
| 31 #import "DOMElementInternal.h" | |
| 32 #import "DragClient.h" | |
| 33 #import "DragController.h" | |
| 34 #import "DragData.h" | |
| 35 #import "Editor.h" | |
| 36 #import "FileList.h" | |
| 37 #import "Frame.h" | |
| 38 #import "Image.h" | |
| 39 #import "Page.h" | |
| 40 #import "Pasteboard.h" | |
| 41 #import "PasteboardStrategy.h" | |
| 42 #import "PlatformStrategies.h" | |
| 43 #import "RenderImage.h" | |
| 44 #import "ScriptExecutionContext.h" | |
| 45 #import "SecurityOrigin.h" | |
| 46 #import "WebCoreSystemInterface.h" | |
| 47 | |
| 48 | |
| 49 namespace WebCore { | |
| 50 | |
| 51 #if ENABLE(DRAG_SUPPORT) | |
| 52 PassRefPtr<Clipboard> Clipboard::create(ClipboardAccessPolicy policy, DragData*
dragData, Frame* frame) | |
| 53 { | |
| 54 return ClipboardMac::create(DragAndDrop, dragData->pasteboardName(), policy,
dragData->containsFiles() ? ClipboardMac::DragAndDropFiles : ClipboardMac::Drag
AndDropData, frame); | |
| 55 } | |
| 56 #endif | |
| 57 | |
| 58 ClipboardMac::ClipboardMac(ClipboardType clipboardType, const String& pasteboard
Name, ClipboardAccessPolicy policy, ClipboardContents clipboardContents, Frame *
frame) | |
| 59 : Clipboard(policy, clipboardType) | |
| 60 , m_pasteboardName(pasteboardName) | |
| 61 , m_clipboardContents(clipboardContents) | |
| 62 , m_frame(frame) | |
| 63 { | |
| 64 m_changeCount = platformStrategies()->pasteboardStrategy()->changeCount(m_pa
steboardName); | |
| 65 } | |
| 66 | |
| 67 ClipboardMac::~ClipboardMac() | |
| 68 { | |
| 69 if (m_dragImage) | |
| 70 m_dragImage->removeClient(this); | |
| 71 } | |
| 72 | |
| 73 bool ClipboardMac::hasData() | |
| 74 { | |
| 75 Vector<String> types; | |
| 76 platformStrategies()->pasteboardStrategy()->getTypes(types, m_pasteboardName
); | |
| 77 return !types.isEmpty(); | |
| 78 } | |
| 79 | |
| 80 static String cocoaTypeFromHTMLClipboardType(const String& type) | |
| 81 { | |
| 82 // http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#dom-
datatransfer-setdata | |
| 83 String qType = type.lower(); | |
| 84 | |
| 85 if (qType == "text") | |
| 86 qType = "text/plain"; | |
| 87 if (qType == "url") | |
| 88 qType = "text/uri-list"; | |
| 89 | |
| 90 // Ignore any trailing charset - JS strings are Unicode, which encapsulates
the charset issue | |
| 91 if (qType == "text/plain" || qType.startsWith("text/plain;")) | |
| 92 return String(NSStringPboardType); | |
| 93 if (qType == "text/uri-list") | |
| 94 // special case because UTI doesn't work with Cocoa's URL type | |
| 95 return String(NSURLPboardType); // note special case in getData to read
NSFilenamesType | |
| 96 | |
| 97 // Blacklist types that might contain subframe information | |
| 98 if (qType == "text/rtf" || qType == "public.rtf" || qType == "com.apple.trad
itional-mac-plain-text") | |
| 99 return String(); | |
| 100 | |
| 101 // Try UTI now | |
| 102 String mimeType = qType; | |
| 103 if (RetainPtr<CFStringRef> utiType = adoptCF(UTTypeCreatePreferredIdentifier
ForTag(kUTTagClassMIMEType, mimeType.createCFString().get(), NULL))) { | |
| 104 RetainPtr<CFStringRef> pbType = adoptCF(UTTypeCopyPreferredTagWithClass(
utiType.get(), kUTTagClassNSPboardType)); | |
| 105 if (pbType) | |
| 106 return pbType.get(); | |
| 107 } | |
| 108 | |
| 109 // No mapping, just pass the whole string though | |
| 110 return qType; | |
| 111 } | |
| 112 | |
| 113 static String utiTypeFromCocoaType(const String& type) | |
| 114 { | |
| 115 if (RetainPtr<CFStringRef> utiType = adoptCF(UTTypeCreatePreferredIdentifier
ForTag(kUTTagClassNSPboardType, type.createCFString().get(), 0))) { | |
| 116 if (RetainPtr<CFStringRef> mimeType = adoptCF(UTTypeCopyPreferredTagWith
Class(utiType.get(), kUTTagClassMIMEType))) | |
| 117 return String(mimeType.get()); | |
| 118 } | |
| 119 return String(); | |
| 120 } | |
| 121 | |
| 122 static void addHTMLClipboardTypesForCocoaType(ListHashSet<String>& resultTypes,
const String& cocoaType, const String& pasteboardName) | |
| 123 { | |
| 124 // UTI may not do these right, so make sure we get the right, predictable re
sult | |
| 125 if (cocoaType == String(NSStringPboardType)) { | |
| 126 resultTypes.add("text/plain"); | |
| 127 return; | |
| 128 } | |
| 129 if (cocoaType == String(NSURLPboardType)) { | |
| 130 resultTypes.add("text/uri-list"); | |
| 131 return; | |
| 132 } | |
| 133 if (cocoaType == String(NSFilenamesPboardType)) { | |
| 134 // If file list is empty, add nothing. | |
| 135 // Note that there is a chance that the file list count could have chang
ed since we grabbed the types array. | |
| 136 // However, this is not really an issue for us doing a sanity check here
. | |
| 137 Vector<String> fileList; | |
| 138 platformStrategies()->pasteboardStrategy()->getPathnamesForType(fileList
, String(NSFilenamesPboardType), pasteboardName); | |
| 139 if (!fileList.isEmpty()) { | |
| 140 // It is unknown if NSFilenamesPboardType always implies NSURLPboard
Type in Cocoa, | |
| 141 // but NSFilenamesPboardType should imply both 'text/uri-list' and '
Files' | |
| 142 resultTypes.add("text/uri-list"); | |
| 143 resultTypes.add("Files"); | |
| 144 } | |
| 145 return; | |
| 146 } | |
| 147 String utiType = utiTypeFromCocoaType(cocoaType); | |
| 148 if (!utiType.isEmpty()) { | |
| 149 resultTypes.add(utiType); | |
| 150 return; | |
| 151 } | |
| 152 // No mapping, just pass the whole string though | |
| 153 resultTypes.add(cocoaType); | |
| 154 } | |
| 155 | |
| 156 void ClipboardMac::clearData(const String& type) | |
| 157 { | |
| 158 if (!canWriteData()) | |
| 159 return; | |
| 160 | |
| 161 // note NSPasteboard enforces changeCount itself on writing - can't write if
not the owner | |
| 162 | |
| 163 String cocoaType = cocoaTypeFromHTMLClipboardType(type); | |
| 164 if (!cocoaType.isEmpty()) | |
| 165 platformStrategies()->pasteboardStrategy()->setStringForType("", cocoaTy
pe, m_pasteboardName); | |
| 166 } | |
| 167 | |
| 168 void ClipboardMac::clearAllData() | |
| 169 { | |
| 170 if (!canWriteData()) | |
| 171 return; | |
| 172 | |
| 173 // note NSPasteboard enforces changeCount itself on writing - can't write if
not the owner | |
| 174 | |
| 175 Pasteboard pasteboard(m_pasteboardName); | |
| 176 pasteboard.clear(); | |
| 177 } | |
| 178 | |
| 179 static Vector<String> absoluteURLsFromPasteboardFilenames(const String& pasteboa
rdName, bool onlyFirstURL = false) | |
| 180 { | |
| 181 Vector<String> fileList; | |
| 182 platformStrategies()->pasteboardStrategy()->getPathnamesForType(fileList, St
ring(NSFilenamesPboardType), pasteboardName); | |
| 183 | |
| 184 if (fileList.isEmpty()) | |
| 185 return fileList; | |
| 186 | |
| 187 size_t count = onlyFirstURL ? 1 : fileList.size(); | |
| 188 Vector<String> urls; | |
| 189 for (size_t i = 0; i < count; i++) { | |
| 190 NSURL *url = [NSURL fileURLWithPath:fileList[i]]; | |
| 191 urls.append(String([url absoluteString])); | |
| 192 } | |
| 193 return urls; | |
| 194 } | |
| 195 | |
| 196 static Vector<String> absoluteURLsFromPasteboard(const String& pasteboardName, b
ool onlyFirstURL = false) | |
| 197 { | |
| 198 // NOTE: We must always check [availableTypes containsObject:] before access
ing pasteboard data | |
| 199 // or CoreFoundation will printf when there is not data of the corresponding
type. | |
| 200 Vector<String> availableTypes; | |
| 201 Vector<String> absoluteURLs; | |
| 202 platformStrategies()->pasteboardStrategy()->getTypes(availableTypes, pastebo
ardName); | |
| 203 | |
| 204 // Try NSFilenamesPboardType because it contains a list | |
| 205 if (availableTypes.contains(String(NSFilenamesPboardType))) { | |
| 206 absoluteURLs = absoluteURLsFromPasteboardFilenames(pasteboardName, onlyF
irstURL); | |
| 207 if (!absoluteURLs.isEmpty()) | |
| 208 return absoluteURLs; | |
| 209 } | |
| 210 | |
| 211 // Fallback to NSURLPboardType (which is a single URL) | |
| 212 if (availableTypes.contains(String(NSURLPboardType))) { | |
| 213 absoluteURLs.append(platformStrategies()->pasteboardStrategy()->stringFo
rType(String(NSURLPboardType), pasteboardName)); | |
| 214 return absoluteURLs; | |
| 215 } | |
| 216 | |
| 217 // No file paths on the pasteboard, return nil | |
| 218 return Vector<String>(); | |
| 219 } | |
| 220 | |
| 221 String ClipboardMac::getData(const String& type) const | |
| 222 { | |
| 223 if (!canReadData() || m_clipboardContents == DragAndDropFiles) | |
| 224 return String(); | |
| 225 | |
| 226 const String& cocoaType = cocoaTypeFromHTMLClipboardType(type); | |
| 227 String cocoaValue; | |
| 228 | |
| 229 // Grab the value off the pasteboard corresponding to the cocoaType | |
| 230 if (cocoaType == String(NSURLPboardType)) { | |
| 231 // "url" and "text/url-list" both map to NSURLPboardType in cocoaTypeFro
mHTMLClipboardType(), "url" only wants the first URL | |
| 232 bool onlyFirstURL = (equalIgnoringCase(type, "url")); | |
| 233 Vector<String> absoluteURLs = absoluteURLsFromPasteboard(m_pasteboardNam
e, onlyFirstURL); | |
| 234 for (size_t i = 0; i < absoluteURLs.size(); i++) | |
| 235 cocoaValue = i ? "\n" + absoluteURLs[i]: absoluteURLs[i]; | |
| 236 } else if (cocoaType == String(NSStringPboardType)) | |
| 237 cocoaValue = [platformStrategies()->pasteboardStrategy()->stringForType(
cocoaType, m_pasteboardName) precomposedStringWithCanonicalMapping]; | |
| 238 else if (!cocoaType.isEmpty()) | |
| 239 cocoaValue = platformStrategies()->pasteboardStrategy()->stringForType(c
ocoaType, m_pasteboardName); | |
| 240 | |
| 241 // Enforce changeCount ourselves for security. We check after reading inste
ad of before to be | |
| 242 // sure it doesn't change between our testing the change count and accessing
the data. | |
| 243 if (!cocoaValue.isEmpty() && m_changeCount == platformStrategies()->pasteboa
rdStrategy()->changeCount(m_pasteboardName)) { | |
| 244 return cocoaValue; | |
| 245 } | |
| 246 | |
| 247 return String(); | |
| 248 } | |
| 249 | |
| 250 bool ClipboardMac::setData(const String &type, const String &data) | |
| 251 { | |
| 252 if (!canWriteData() || m_clipboardContents == DragAndDropFiles) | |
| 253 return false; | |
| 254 // note NSPasteboard enforces changeCount itself on writing - can't write if
not the owner | |
| 255 | |
| 256 const String& cocoaType = cocoaTypeFromHTMLClipboardType(type); | |
| 257 String cocoaData = data; | |
| 258 | |
| 259 if (cocoaType == String(NSURLPboardType) || cocoaType == String(kUTTypeFileU
RL)) { | |
| 260 NSURL *url = [NSURL URLWithString:cocoaData]; | |
| 261 if ([url isFileURL]) | |
| 262 return false; | |
| 263 | |
| 264 Vector<String> types; | |
| 265 types.append(cocoaType); | |
| 266 platformStrategies()->pasteboardStrategy()->setTypes(types, m_pasteboard
Name); | |
| 267 platformStrategies()->pasteboardStrategy()->setStringForType(cocoaData,
cocoaType, m_pasteboardName); | |
| 268 | |
| 269 return true; | |
| 270 } | |
| 271 | |
| 272 if (!cocoaType.isEmpty()) { | |
| 273 // everything else we know of goes on the pboard as a string | |
| 274 Vector<String> types; | |
| 275 types.append(cocoaType); | |
| 276 platformStrategies()->pasteboardStrategy()->addTypes(types, m_pasteboard
Name); | |
| 277 platformStrategies()->pasteboardStrategy()->setStringForType(cocoaData,
cocoaType, m_pasteboardName); | |
| 278 return true; | |
| 279 } | |
| 280 | |
| 281 return false; | |
| 282 } | |
| 283 | |
| 284 ListHashSet<String> ClipboardMac::types() const | |
| 285 { | |
| 286 if (!canReadTypes()) | |
| 287 return ListHashSet<String>(); | |
| 288 | |
| 289 Vector<String> types; | |
| 290 platformStrategies()->pasteboardStrategy()->getTypes(types, m_pasteboardName
); | |
| 291 | |
| 292 // Enforce changeCount ourselves for security. We check after reading inste
ad of before to be | |
| 293 // sure it doesn't change between our testing the change count and accessing
the data. | |
| 294 if (m_changeCount != platformStrategies()->pasteboardStrategy()->changeCount
(m_pasteboardName)) | |
| 295 return ListHashSet<String>(); | |
| 296 | |
| 297 ListHashSet<String> result; | |
| 298 // FIXME: This loop could be split into two stages. One which adds all the H
TML5 specified types | |
| 299 // and a second which adds all the extra types from the cocoa clipboard (whi
ch is Mac-only behavior). | |
| 300 for (size_t i = 0; i < types.size(); i++) { | |
| 301 if (types[i] == "NeXT plain ascii pasteboard type") | |
| 302 continue; // skip this ancient type that gets auto-supplied by som
e system conversion | |
| 303 | |
| 304 addHTMLClipboardTypesForCocoaType(result, types[i], m_pasteboardName); | |
| 305 } | |
| 306 | |
| 307 return result; | |
| 308 } | |
| 309 | |
| 310 // FIXME: We could cache the computed fileList if necessary | |
| 311 // Currently each access gets a new copy, setData() modifications to the | |
| 312 // clipboard are not reflected in any FileList objects the page has accessed and
stored | |
| 313 PassRefPtr<FileList> ClipboardMac::files() const | |
| 314 { | |
| 315 if (!canReadData() || m_clipboardContents == DragAndDropData) | |
| 316 return FileList::create(); | |
| 317 | |
| 318 Vector<String> absoluteURLs = absoluteURLsFromPasteboardFilenames(m_pasteboa
rdName); | |
| 319 | |
| 320 RefPtr<FileList> fileList = FileList::create(); | |
| 321 for (size_t i = 0; i < absoluteURLs.size(); i++) { | |
| 322 NSURL *absoluteURL = [NSURL URLWithString:absoluteURLs[i]]; | |
| 323 ASSERT([absoluteURL isFileURL]); | |
| 324 fileList->append(File::create([absoluteURL path], File::AllContentTypes)
); | |
| 325 } | |
| 326 return fileList.release(); // We will always return a FileList, sometimes em
pty | |
| 327 } | |
| 328 | |
| 329 // The rest of these getters don't really have any impact on security, so for no
w make no checks | |
| 330 | |
| 331 void ClipboardMac::setDragImage(CachedImage* img, const IntPoint &loc) | |
| 332 { | |
| 333 setDragImage(img, 0, loc); | |
| 334 } | |
| 335 | |
| 336 void ClipboardMac::setDragImageElement(Node *node, const IntPoint &loc) | |
| 337 { | |
| 338 setDragImage(0, node, loc); | |
| 339 } | |
| 340 | |
| 341 void ClipboardMac::setDragImage(CachedImage* image, Node *node, const IntPoint &
loc) | |
| 342 { | |
| 343 if (canSetDragImage()) { | |
| 344 if (m_dragImage) | |
| 345 m_dragImage->removeClient(this); | |
| 346 m_dragImage = image; | |
| 347 if (m_dragImage) | |
| 348 m_dragImage->addClient(this); | |
| 349 | |
| 350 m_dragLoc = loc; | |
| 351 m_dragImageElement = node; | |
| 352 | |
| 353 if (dragStarted() && m_changeCount == platformStrategies()->pasteboardSt
rategy()->changeCount(m_pasteboardName)) { | |
| 354 NSPoint cocoaLoc; | |
| 355 NSImage* cocoaImage = dragNSImage(cocoaLoc); | |
| 356 if (cocoaImage) { | |
| 357 // Dashboard wants to be able to set the drag image during dragg
ing, but Cocoa does not allow this. | |
| 358 // Instead we must drop down to the CoreGraphics API. | |
| 359 wkSetDragImage(cocoaImage, cocoaLoc); | |
| 360 | |
| 361 // Hack: We must post an event to wake up the NSDragManager, whi
ch is sitting in a nextEvent call | |
| 362 // up the stack from us because the CoreFoundation drag manager
does not use the run loop by itself. | |
| 363 // This is the most innocuous event to use, per Kristen Forster. | |
| 364 NSEvent* ev = [NSEvent mouseEventWithType:NSMouseMoved location:
NSZeroPoint | |
| 365 modifierFlags:0 timestamp:0 windowNumber:0 context:nil event
Number:0 clickCount:0 pressure:0]; | |
| 366 [NSApp postEvent:ev atStart:YES]; | |
| 367 } | |
| 368 } | |
| 369 // Else either 1) we haven't started dragging yet, so we rely on the par
t to install this drag image | |
| 370 // as part of getting the drag kicked off, or 2) Someone kept a ref to t
he clipboard and is trying to | |
| 371 // set the image way too late. | |
| 372 } | |
| 373 } | |
| 374 | |
| 375 void ClipboardMac::writeRange(Range* range, Frame* frame) | |
| 376 { | |
| 377 ASSERT(range); | |
| 378 ASSERT(frame); | |
| 379 Pasteboard pasteboard(m_pasteboardName); | |
| 380 pasteboard.writeSelection(range, frame->editor()->smartInsertDeleteEnabled()
&& frame->selection()->granularity() == WordGranularity, frame, IncludeImageAlt
TextForClipboard); | |
| 381 } | |
| 382 | |
| 383 void ClipboardMac::writePlainText(const String& text) | |
| 384 { | |
| 385 Pasteboard pasteboard(m_pasteboardName); | |
| 386 pasteboard.writePlainText(text, Pasteboard::CannotSmartReplace); | |
| 387 } | |
| 388 | |
| 389 void ClipboardMac::writeURL(const KURL& url, const String& title, Frame* frame) | |
| 390 { | |
| 391 ASSERT(frame); | |
| 392 ASSERT(m_pasteboardName); | |
| 393 Pasteboard pasteboard(m_pasteboardName); | |
| 394 pasteboard.writeURL(url, title, frame); | |
| 395 } | |
| 396 | |
| 397 #if ENABLE(DRAG_SUPPORT) | |
| 398 void ClipboardMac::declareAndWriteDragImage(Element* element, const KURL& url, c
onst String& title, Frame* frame) | |
| 399 { | |
| 400 ASSERT(frame); | |
| 401 if (Page* page = frame->page()) | |
| 402 page->dragController()->client()->declareAndWriteDragImage(m_pasteboardN
ame, kit(element), url, title, frame); | |
| 403 } | |
| 404 #endif // ENABLE(DRAG_SUPPORT) | |
| 405 | |
| 406 DragImageRef ClipboardMac::createDragImage(IntPoint& loc) const | |
| 407 { | |
| 408 NSPoint nsloc = NSMakePoint(loc.x(), loc.y()); | |
| 409 DragImageRef result = dragNSImage(nsloc); | |
| 410 loc = (IntPoint)nsloc; | |
| 411 return result; | |
| 412 } | |
| 413 | |
| 414 NSImage *ClipboardMac::dragNSImage(NSPoint& loc) const | |
| 415 { | |
| 416 NSImage *result = nil; | |
| 417 if (m_dragImageElement) { | |
| 418 if (m_frame) { | |
| 419 NSRect imageRect; | |
| 420 NSRect elementRect; | |
| 421 result = m_frame->snapshotDragImage(m_dragImageElement.get(), &image
Rect, &elementRect); | |
| 422 // Client specifies point relative to element, not the whole image,
which may include child | |
| 423 // layers spread out all over the place. | |
| 424 loc.x = elementRect.origin.x - imageRect.origin.x + m_dragLoc.x(); | |
| 425 loc.y = elementRect.origin.y - imageRect.origin.y + m_dragLoc.y(); | |
| 426 loc.y = imageRect.size.height - loc.y; | |
| 427 } | |
| 428 } else if (m_dragImage) { | |
| 429 result = m_dragImage->image()->getNSImage(); | |
| 430 | |
| 431 loc = m_dragLoc; | |
| 432 loc.y = [result size].height - loc.y; | |
| 433 } | |
| 434 return result; | |
| 435 } | |
| 436 | |
| 437 } | |
| OLD | NEW |