1 | /* |
2 | * Copyright (C) 2006, 2007, 2008, 2013 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 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 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 | #include "config.h" |
27 | #include "DataTransfer.h" |
28 | |
29 | #include "CachedImage.h" |
30 | #include "CachedImageClient.h" |
31 | #include "DataTransferItem.h" |
32 | #include "DataTransferItemList.h" |
33 | #include "DocumentFragment.h" |
34 | #include "DragData.h" |
35 | #include "Editor.h" |
36 | #include "FileList.h" |
37 | #include "Frame.h" |
38 | #include "FrameLoader.h" |
39 | #include "HTMLImageElement.h" |
40 | #include "HTMLParserIdioms.h" |
41 | #include "Image.h" |
42 | #include "Pasteboard.h" |
43 | #include "RuntimeEnabledFeatures.h" |
44 | #include "Settings.h" |
45 | #include "StaticPasteboard.h" |
46 | #include "WebContentReader.h" |
47 | #include "WebCorePasteboardFileReader.h" |
48 | #include "markup.h" |
49 | #include <wtf/URLParser.h> |
50 | #include <wtf/unicode/CharacterNames.h> |
51 | |
52 | namespace WebCore { |
53 | |
54 | #if ENABLE(DRAG_SUPPORT) |
55 | |
56 | class DragImageLoader final : private CachedImageClient { |
57 | WTF_MAKE_NONCOPYABLE(DragImageLoader); WTF_MAKE_FAST_ALLOCATED; |
58 | public: |
59 | explicit DragImageLoader(DataTransfer*); |
60 | void startLoading(CachedResourceHandle<CachedImage>&); |
61 | void stopLoading(CachedResourceHandle<CachedImage>&); |
62 | void moveToDataTransfer(DataTransfer&); |
63 | |
64 | private: |
65 | void imageChanged(CachedImage*, const IntRect*) override; |
66 | DataTransfer* m_dataTransfer; |
67 | }; |
68 | |
69 | #endif |
70 | |
71 | DataTransfer::DataTransfer(StoreMode mode, std::unique_ptr<Pasteboard> pasteboard, Type type) |
72 | : m_storeMode(mode) |
73 | , m_pasteboard(WTFMove(pasteboard)) |
74 | #if ENABLE(DRAG_SUPPORT) |
75 | , m_type(type) |
76 | , m_dropEffect("uninitialized"_s ) |
77 | , m_effectAllowed("uninitialized"_s ) |
78 | , m_shouldUpdateDragImage(false) |
79 | #endif |
80 | { |
81 | #if !ENABLE(DRAG_SUPPORT) |
82 | ASSERT_UNUSED(type, type != Type::DragAndDropData && type != Type::DragAndDropFiles); |
83 | #endif |
84 | } |
85 | |
86 | Ref<DataTransfer> DataTransfer::createForCopyAndPaste(Document& document, StoreMode storeMode, std::unique_ptr<Pasteboard>&& pasteboard) |
87 | { |
88 | auto dataTransfer = adoptRef(*new DataTransfer(storeMode, WTFMove(pasteboard))); |
89 | dataTransfer->m_originIdentifier = document.originIdentifierForPasteboard(); |
90 | return dataTransfer; |
91 | } |
92 | |
93 | DataTransfer::~DataTransfer() |
94 | { |
95 | #if ENABLE(DRAG_SUPPORT) |
96 | if (m_dragImageLoader && m_dragImage) |
97 | m_dragImageLoader->stopLoading(m_dragImage); |
98 | #endif |
99 | } |
100 | |
101 | bool DataTransfer::canReadTypes() const |
102 | { |
103 | return m_storeMode == StoreMode::Readonly || m_storeMode == StoreMode::Protected || m_storeMode == StoreMode::ReadWrite; |
104 | } |
105 | |
106 | bool DataTransfer::canReadData() const |
107 | { |
108 | return m_storeMode == StoreMode::Readonly || m_storeMode == StoreMode::ReadWrite; |
109 | } |
110 | |
111 | bool DataTransfer::canWriteData() const |
112 | { |
113 | return m_storeMode == StoreMode::ReadWrite; |
114 | } |
115 | |
116 | static String normalizeType(const String& type) |
117 | { |
118 | if (type.isNull()) |
119 | return type; |
120 | |
121 | String lowercaseType = stripLeadingAndTrailingHTMLSpaces(type).convertToASCIILowercase(); |
122 | if (lowercaseType == "text" || lowercaseType.startsWith("text/plain;" )) |
123 | return "text/plain" ; |
124 | if (lowercaseType == "url" || lowercaseType.startsWith("text/uri-list;" )) |
125 | return "text/uri-list" ; |
126 | if (lowercaseType.startsWith("text/html;" )) |
127 | return "text/html" ; |
128 | |
129 | return lowercaseType; |
130 | } |
131 | |
132 | void DataTransfer::clearData(const String& type) |
133 | { |
134 | if (!canWriteData()) |
135 | return; |
136 | |
137 | String normalizedType = normalizeType(type); |
138 | if (normalizedType.isNull()) |
139 | m_pasteboard->clear(); |
140 | else |
141 | m_pasteboard->clear(normalizedType); |
142 | if (m_itemList) |
143 | m_itemList->didClearStringData(normalizedType); |
144 | } |
145 | |
146 | static String readURLsFromPasteboardAsString(Pasteboard& pasteboard, Function<bool(const String&)>&& shouldIncludeURL) |
147 | { |
148 | StringBuilder urlList; |
149 | for (const auto& urlString : pasteboard.readAllStrings("text/uri-list"_s )) { |
150 | if (!shouldIncludeURL(urlString)) |
151 | continue; |
152 | if (!urlList.isEmpty()) |
153 | urlList.append(newlineCharacter); |
154 | urlList.append(urlString); |
155 | } |
156 | return urlList.toString(); |
157 | } |
158 | |
159 | String DataTransfer::getDataForItem(Document& document, const String& type) const |
160 | { |
161 | if (!canReadData()) |
162 | return { }; |
163 | |
164 | auto lowercaseType = stripLeadingAndTrailingHTMLSpaces(type).convertToASCIILowercase(); |
165 | if (shouldSuppressGetAndSetDataToAvoidExposingFilePaths()) { |
166 | if (lowercaseType == "text/uri-list" ) { |
167 | return readURLsFromPasteboardAsString(*m_pasteboard, [] (auto& urlString) { |
168 | return Pasteboard::canExposeURLToDOMWhenPasteboardContainsFiles(urlString); |
169 | }); |
170 | } |
171 | |
172 | if (lowercaseType == "text/html" && RuntimeEnabledFeatures::sharedFeatures().customPasteboardDataEnabled()) { |
173 | // If the pasteboard contains files and the page requests 'text/html', we only read from rich text types to prevent file |
174 | // paths from leaking (e.g. from plain text data on the pasteboard) since we sanitize cross-origin markup. However, if |
175 | // custom pasteboard data is disabled, then we can't ensure that the markup we deliver is sanitized, so we fall back to |
176 | // current behavior and return an empty string. |
177 | return readStringFromPasteboard(document, lowercaseType, WebContentReadingPolicy::OnlyRichTextTypes); |
178 | } |
179 | |
180 | return { }; |
181 | } |
182 | |
183 | return readStringFromPasteboard(document, lowercaseType, WebContentReadingPolicy::AnyType); |
184 | } |
185 | |
186 | String DataTransfer::readStringFromPasteboard(Document& document, const String& lowercaseType, WebContentReadingPolicy policy) const |
187 | { |
188 | if (!RuntimeEnabledFeatures::sharedFeatures().customPasteboardDataEnabled()) |
189 | return m_pasteboard->readString(lowercaseType); |
190 | |
191 | // StaticPasteboard is only used to stage data written by websites before being committed to the system pasteboard. |
192 | bool isSameOrigin = is<StaticPasteboard>(*m_pasteboard) || (!m_originIdentifier.isNull() && m_originIdentifier == m_pasteboard->readOrigin()); |
193 | if (isSameOrigin) { |
194 | String value = m_pasteboard->readStringInCustomData(lowercaseType); |
195 | if (!value.isNull()) |
196 | return value; |
197 | } |
198 | if (!Pasteboard::isSafeTypeForDOMToReadAndWrite(lowercaseType)) |
199 | return { }; |
200 | |
201 | if (!is<StaticPasteboard>(*m_pasteboard) && lowercaseType == "text/html" ) { |
202 | if (!document.frame()) |
203 | return { }; |
204 | WebContentMarkupReader reader { *document.frame() }; |
205 | m_pasteboard->read(reader, policy); |
206 | return reader.markup; |
207 | } |
208 | |
209 | if (!is<StaticPasteboard>(*m_pasteboard) && lowercaseType == "text/uri-list" ) { |
210 | return readURLsFromPasteboardAsString(*m_pasteboard, [] (auto&) { |
211 | return true; |
212 | }); |
213 | } |
214 | |
215 | return m_pasteboard->readString(lowercaseType); |
216 | } |
217 | |
218 | String DataTransfer::getData(Document& document, const String& type) const |
219 | { |
220 | return getDataForItem(document, normalizeType(type)); |
221 | } |
222 | |
223 | bool DataTransfer::shouldSuppressGetAndSetDataToAvoidExposingFilePaths() const |
224 | { |
225 | if (!forFileDrag() && !RuntimeEnabledFeatures::sharedFeatures().customPasteboardDataEnabled()) |
226 | return false; |
227 | return m_pasteboard->fileContentState() == Pasteboard::FileContentState::MayContainFilePaths; |
228 | } |
229 | |
230 | void DataTransfer::setData(const String& type, const String& data) |
231 | { |
232 | if (!canWriteData()) |
233 | return; |
234 | |
235 | if (shouldSuppressGetAndSetDataToAvoidExposingFilePaths()) |
236 | return; |
237 | |
238 | auto normalizedType = normalizeType(type); |
239 | setDataFromItemList(normalizedType, data); |
240 | if (m_itemList) |
241 | m_itemList->didSetStringData(normalizedType); |
242 | } |
243 | |
244 | void DataTransfer::setDataFromItemList(const String& type, const String& data) |
245 | { |
246 | ASSERT(canWriteData()); |
247 | RELEASE_ASSERT(is<StaticPasteboard>(*m_pasteboard)); |
248 | |
249 | if (!RuntimeEnabledFeatures::sharedFeatures().customPasteboardDataEnabled()) { |
250 | m_pasteboard->writeString(type, data); |
251 | return; |
252 | } |
253 | |
254 | String sanitizedData; |
255 | if (type == "text/html" ) |
256 | sanitizedData = sanitizeMarkup(data); |
257 | else if (type == "text/uri-list" ) { |
258 | auto url = URL({ }, data); |
259 | if (url.isValid()) |
260 | sanitizedData = url.string(); |
261 | } else if (type == "text/plain" ) |
262 | sanitizedData = data; // Nothing to sanitize. |
263 | |
264 | if (sanitizedData != data) |
265 | downcast<StaticPasteboard>(*m_pasteboard).writeStringInCustomData(type, data); |
266 | |
267 | if (Pasteboard::isSafeTypeForDOMToReadAndWrite(type) && !sanitizedData.isNull()) |
268 | m_pasteboard->writeString(type, sanitizedData); |
269 | } |
270 | |
271 | void DataTransfer::updateFileList() |
272 | { |
273 | ASSERT(canWriteData()); |
274 | |
275 | m_fileList->m_files = filesFromPasteboardAndItemList(); |
276 | } |
277 | |
278 | void DataTransfer::didAddFileToItemList() |
279 | { |
280 | ASSERT(canWriteData()); |
281 | if (!m_fileList) |
282 | return; |
283 | |
284 | auto& newItem = m_itemList->items().last(); |
285 | ASSERT(newItem->isFile()); |
286 | m_fileList->append(*newItem->file()); |
287 | } |
288 | |
289 | DataTransferItemList& DataTransfer::items() |
290 | { |
291 | if (!m_itemList) |
292 | m_itemList = std::make_unique<DataTransferItemList>(*this); |
293 | return *m_itemList; |
294 | } |
295 | |
296 | Vector<String> DataTransfer::types() const |
297 | { |
298 | return types(AddFilesType::Yes); |
299 | } |
300 | |
301 | Vector<String> DataTransfer::typesForItemList() const |
302 | { |
303 | return types(AddFilesType::No); |
304 | } |
305 | |
306 | Vector<String> DataTransfer::types(AddFilesType addFilesType) const |
307 | { |
308 | if (!canReadTypes()) |
309 | return { }; |
310 | |
311 | if (!RuntimeEnabledFeatures::sharedFeatures().customPasteboardDataEnabled()) { |
312 | auto types = m_pasteboard->typesForLegacyUnsafeBindings(); |
313 | ASSERT(!types.contains("Files" )); |
314 | if (m_pasteboard->fileContentState() != Pasteboard::FileContentState::NoFileOrImageData && addFilesType == AddFilesType::Yes) |
315 | types.append("Files" ); |
316 | return types; |
317 | } |
318 | |
319 | auto safeTypes = m_pasteboard->typesSafeForBindings(m_originIdentifier); |
320 | bool hasFileBackedItem = m_itemList && m_itemList->hasItems() && notFound != m_itemList->items().findMatching([] (const auto& item) { |
321 | return item->isFile(); |
322 | }); |
323 | |
324 | auto fileContentState = m_pasteboard->fileContentState(); |
325 | if (hasFileBackedItem || fileContentState != Pasteboard::FileContentState::NoFileOrImageData) { |
326 | Vector<String> types; |
327 | if (addFilesType == AddFilesType::Yes) |
328 | types.append("Files"_s ); |
329 | |
330 | if (fileContentState != Pasteboard::FileContentState::MayContainFilePaths) { |
331 | types.appendVector(WTFMove(safeTypes)); |
332 | return types; |
333 | } |
334 | |
335 | if (safeTypes.contains("text/uri-list" )) |
336 | types.append("text/uri-list"_s ); |
337 | if (safeTypes.contains("text/html" ) && RuntimeEnabledFeatures::sharedFeatures().customPasteboardDataEnabled()) |
338 | types.append("text/html"_s ); |
339 | return types; |
340 | } |
341 | |
342 | ASSERT(!safeTypes.contains("Files" )); |
343 | return safeTypes; |
344 | } |
345 | |
346 | Vector<Ref<File>> DataTransfer::filesFromPasteboardAndItemList() const |
347 | { |
348 | bool addedFilesFromPasteboard = false; |
349 | Vector<Ref<File>> files; |
350 | if ((!forDrag() || forFileDrag()) && m_pasteboard->fileContentState() != Pasteboard::FileContentState::NoFileOrImageData) { |
351 | WebCorePasteboardFileReader reader; |
352 | m_pasteboard->read(reader); |
353 | files = WTFMove(reader.files); |
354 | addedFilesFromPasteboard = !files.isEmpty(); |
355 | } |
356 | |
357 | bool itemListContainsItems = false; |
358 | if (m_itemList && m_itemList->hasItems()) { |
359 | for (auto& item : m_itemList->items()) { |
360 | if (auto file = item->file()) |
361 | files.append(file.releaseNonNull()); |
362 | } |
363 | itemListContainsItems = true; |
364 | } |
365 | |
366 | bool containsItemsAndFiles = itemListContainsItems && addedFilesFromPasteboard; |
367 | ASSERT_UNUSED(containsItemsAndFiles, !containsItemsAndFiles); |
368 | return files; |
369 | } |
370 | |
371 | FileList& DataTransfer::files() const |
372 | { |
373 | if (!canReadData()) { |
374 | if (m_fileList) |
375 | m_fileList->clear(); |
376 | else |
377 | m_fileList = FileList::create(); |
378 | return *m_fileList; |
379 | } |
380 | |
381 | if (!m_fileList) |
382 | m_fileList = FileList::create(filesFromPasteboardAndItemList()); |
383 | |
384 | return *m_fileList; |
385 | } |
386 | |
387 | struct PasteboardFileTypeReader final : PasteboardFileReader { |
388 | void readFilename(const String& filename) |
389 | { |
390 | types.add(File::contentTypeForFile(filename)); |
391 | } |
392 | |
393 | void readBuffer(const String&, const String& type, Ref<SharedBuffer>&&) |
394 | { |
395 | types.add(type); |
396 | } |
397 | |
398 | HashSet<String, ASCIICaseInsensitiveHash> types; |
399 | }; |
400 | |
401 | bool DataTransfer::hasFileOfType(const String& type) |
402 | { |
403 | ASSERT_WITH_SECURITY_IMPLICATION(canReadTypes()); |
404 | PasteboardFileTypeReader reader; |
405 | m_pasteboard->read(reader); |
406 | return reader.types.contains(type); |
407 | } |
408 | |
409 | bool DataTransfer::hasStringOfType(const String& type) |
410 | { |
411 | ASSERT_WITH_SECURITY_IMPLICATION(canReadTypes()); |
412 | |
413 | return !type.isNull() && types().contains(type); |
414 | } |
415 | |
416 | Ref<DataTransfer> DataTransfer::createForInputEvent(const String& plainText, const String& htmlText) |
417 | { |
418 | auto pasteboard = std::make_unique<StaticPasteboard>(); |
419 | pasteboard->writeString("text/plain"_s , plainText); |
420 | pasteboard->writeString("text/html"_s , htmlText); |
421 | return adoptRef(*new DataTransfer(StoreMode::Readonly, WTFMove(pasteboard), Type::InputEvent)); |
422 | } |
423 | |
424 | void DataTransfer::commitToPasteboard(Pasteboard& nativePasteboard) |
425 | { |
426 | ASSERT(is<StaticPasteboard>(*m_pasteboard) && !is<StaticPasteboard>(nativePasteboard)); |
427 | PasteboardCustomData customData = downcast<StaticPasteboard>(*m_pasteboard).takeCustomData(); |
428 | if (RuntimeEnabledFeatures::sharedFeatures().customPasteboardDataEnabled()) { |
429 | customData.origin = m_originIdentifier; |
430 | nativePasteboard.writeCustomData(customData); |
431 | return; |
432 | } |
433 | |
434 | for (auto& entry : customData.platformData) |
435 | nativePasteboard.writeString(entry.key, entry.value); |
436 | for (auto& entry : customData.sameOriginCustomData) |
437 | nativePasteboard.writeString(entry.key, entry.value); |
438 | } |
439 | |
440 | #if !ENABLE(DRAG_SUPPORT) |
441 | |
442 | String DataTransfer::dropEffect() const |
443 | { |
444 | return "none"_s ; |
445 | } |
446 | |
447 | void DataTransfer::setDropEffect(const String&) |
448 | { |
449 | } |
450 | |
451 | String DataTransfer::effectAllowed() const |
452 | { |
453 | return "uninitialized"_s ; |
454 | } |
455 | |
456 | void DataTransfer::setEffectAllowed(const String&) |
457 | { |
458 | } |
459 | |
460 | void DataTransfer::setDragImage(Element*, int, int) |
461 | { |
462 | } |
463 | |
464 | #else |
465 | |
466 | Ref<DataTransfer> DataTransfer::createForDrag() |
467 | { |
468 | return adoptRef(*new DataTransfer(StoreMode::ReadWrite, Pasteboard::createForDragAndDrop(), Type::DragAndDropData)); |
469 | } |
470 | |
471 | Ref<DataTransfer> DataTransfer::createForDragStartEvent(Document& document) |
472 | { |
473 | auto dataTransfer = adoptRef(*new DataTransfer(StoreMode::ReadWrite, std::make_unique<StaticPasteboard>(), Type::DragAndDropData)); |
474 | dataTransfer->m_originIdentifier = document.originIdentifierForPasteboard(); |
475 | return dataTransfer; |
476 | } |
477 | |
478 | Ref<DataTransfer> DataTransfer::createForDrop(Document& document, std::unique_ptr<Pasteboard>&& pasteboard, DragOperation sourceOperation, bool draggingFiles) |
479 | { |
480 | auto dataTransfer = adoptRef(*new DataTransfer(DataTransfer::StoreMode::Readonly, WTFMove(pasteboard), draggingFiles ? Type::DragAndDropFiles : Type::DragAndDropData)); |
481 | dataTransfer->setSourceOperation(sourceOperation); |
482 | dataTransfer->m_originIdentifier = document.originIdentifierForPasteboard(); |
483 | return dataTransfer; |
484 | } |
485 | |
486 | Ref<DataTransfer> DataTransfer::createForUpdatingDropTarget(Document& document, std::unique_ptr<Pasteboard>&& pasteboard, DragOperation sourceOperation, bool draggingFiles) |
487 | { |
488 | auto dataTransfer = adoptRef(*new DataTransfer(DataTransfer::StoreMode::Protected, WTFMove(pasteboard), draggingFiles ? Type::DragAndDropFiles : Type::DragAndDropData)); |
489 | dataTransfer->setSourceOperation(sourceOperation); |
490 | dataTransfer->m_originIdentifier = document.originIdentifierForPasteboard(); |
491 | return dataTransfer; |
492 | } |
493 | |
494 | void DataTransfer::setDragImage(Element* element, int x, int y) |
495 | { |
496 | if (!forDrag() || !canWriteData()) |
497 | return; |
498 | |
499 | CachedImage* image = nullptr; |
500 | if (is<HTMLImageElement>(element) && !element->isConnected()) |
501 | image = downcast<HTMLImageElement>(*element).cachedImage(); |
502 | |
503 | m_dragLocation = IntPoint(x, y); |
504 | |
505 | if (m_dragImageLoader && m_dragImage) |
506 | m_dragImageLoader->stopLoading(m_dragImage); |
507 | m_dragImage = image; |
508 | if (m_dragImage) { |
509 | if (!m_dragImageLoader) |
510 | m_dragImageLoader = std::make_unique<DragImageLoader>(this); |
511 | m_dragImageLoader->startLoading(m_dragImage); |
512 | } |
513 | |
514 | m_dragImageElement = image ? nullptr : element; |
515 | |
516 | updateDragImage(); |
517 | } |
518 | |
519 | void DataTransfer::updateDragImage() |
520 | { |
521 | // Don't allow setting the image if we haven't started dragging yet; we'll rely on the dragging code |
522 | // to install this drag image as part of getting the drag kicked off. |
523 | if (!m_shouldUpdateDragImage) |
524 | return; |
525 | |
526 | IntPoint computedHotSpot; |
527 | auto computedImage = DragImage { createDragImage(computedHotSpot) }; |
528 | if (!computedImage) |
529 | return; |
530 | |
531 | m_pasteboard->setDragImage(WTFMove(computedImage), computedHotSpot); |
532 | } |
533 | |
534 | RefPtr<Element> DataTransfer::dragImageElement() const |
535 | { |
536 | return m_dragImageElement; |
537 | } |
538 | |
539 | #if !PLATFORM(MAC) |
540 | |
541 | DragImageRef DataTransfer::createDragImage(IntPoint& location) const |
542 | { |
543 | location = m_dragLocation; |
544 | |
545 | if (m_dragImage) |
546 | return createDragImageFromImage(m_dragImage->image(), ImageOrientationDescription()); |
547 | |
548 | if (m_dragImageElement) { |
549 | if (Frame* frame = m_dragImageElement->document().frame()) |
550 | return createDragImageForNode(*frame, *m_dragImageElement); |
551 | } |
552 | |
553 | // We do not have enough information to create a drag image, use the default icon. |
554 | return nullptr; |
555 | } |
556 | |
557 | #endif |
558 | |
559 | DragImageLoader::DragImageLoader(DataTransfer* dataTransfer) |
560 | : m_dataTransfer(dataTransfer) |
561 | { |
562 | } |
563 | |
564 | void DragImageLoader::moveToDataTransfer(DataTransfer& newDataTransfer) |
565 | { |
566 | m_dataTransfer = &newDataTransfer; |
567 | } |
568 | |
569 | void DragImageLoader::startLoading(CachedResourceHandle<WebCore::CachedImage>& image) |
570 | { |
571 | // FIXME: Does this really trigger a load? Does it need to? |
572 | image->addClient(*this); |
573 | } |
574 | |
575 | void DragImageLoader::stopLoading(CachedResourceHandle<WebCore::CachedImage>& image) |
576 | { |
577 | image->removeClient(*this); |
578 | } |
579 | |
580 | void DragImageLoader::imageChanged(CachedImage*, const IntRect*) |
581 | { |
582 | m_dataTransfer->updateDragImage(); |
583 | } |
584 | |
585 | static DragOperation dragOpFromIEOp(const String& operation) |
586 | { |
587 | if (operation == "uninitialized" ) |
588 | return DragOperationEvery; |
589 | if (operation == "none" ) |
590 | return DragOperationNone; |
591 | if (operation == "copy" ) |
592 | return DragOperationCopy; |
593 | if (operation == "link" ) |
594 | return DragOperationLink; |
595 | if (operation == "move" ) |
596 | return (DragOperation)(DragOperationGeneric | DragOperationMove); |
597 | if (operation == "copyLink" ) |
598 | return (DragOperation)(DragOperationCopy | DragOperationLink); |
599 | if (operation == "copyMove" ) |
600 | return (DragOperation)(DragOperationCopy | DragOperationGeneric | DragOperationMove); |
601 | if (operation == "linkMove" ) |
602 | return (DragOperation)(DragOperationLink | DragOperationGeneric | DragOperationMove); |
603 | if (operation == "all" ) |
604 | return DragOperationEvery; |
605 | return DragOperationPrivate; // really a marker for "no conversion" |
606 | } |
607 | |
608 | static const char* IEOpFromDragOp(DragOperation operation) |
609 | { |
610 | bool isGenericMove = operation & (DragOperationGeneric | DragOperationMove); |
611 | |
612 | if ((isGenericMove && (operation & DragOperationCopy) && (operation & DragOperationLink)) || operation == DragOperationEvery) |
613 | return "all" ; |
614 | if (isGenericMove && (operation & DragOperationCopy)) |
615 | return "copyMove" ; |
616 | if (isGenericMove && (operation & DragOperationLink)) |
617 | return "linkMove" ; |
618 | if ((operation & DragOperationCopy) && (operation & DragOperationLink)) |
619 | return "copyLink" ; |
620 | if (isGenericMove) |
621 | return "move" ; |
622 | if (operation & DragOperationCopy) |
623 | return "copy" ; |
624 | if (operation & DragOperationLink) |
625 | return "link" ; |
626 | return "none" ; |
627 | } |
628 | |
629 | DragOperation DataTransfer::sourceOperation() const |
630 | { |
631 | DragOperation operation = dragOpFromIEOp(m_effectAllowed); |
632 | ASSERT(operation != DragOperationPrivate); |
633 | return operation; |
634 | } |
635 | |
636 | DragOperation DataTransfer::destinationOperation() const |
637 | { |
638 | DragOperation operation = dragOpFromIEOp(m_dropEffect); |
639 | ASSERT(operation == DragOperationCopy || operation == DragOperationNone || operation == DragOperationLink || operation == (DragOperation)(DragOperationGeneric | DragOperationMove) || operation == DragOperationEvery); |
640 | return operation; |
641 | } |
642 | |
643 | void DataTransfer::setSourceOperation(DragOperation operation) |
644 | { |
645 | ASSERT_ARG(operation, operation != DragOperationPrivate); |
646 | m_effectAllowed = IEOpFromDragOp(operation); |
647 | } |
648 | |
649 | void DataTransfer::setDestinationOperation(DragOperation operation) |
650 | { |
651 | ASSERT_ARG(operation, operation == DragOperationCopy || operation == DragOperationNone || operation == DragOperationLink || operation == DragOperationGeneric || operation == DragOperationMove || operation == (DragOperation)(DragOperationGeneric | DragOperationMove)); |
652 | m_dropEffect = IEOpFromDragOp(operation); |
653 | } |
654 | |
655 | String DataTransfer::dropEffect() const |
656 | { |
657 | return m_dropEffect == "uninitialized" ? "none"_s : m_dropEffect; |
658 | } |
659 | |
660 | void DataTransfer::setDropEffect(const String& effect) |
661 | { |
662 | if (!forDrag()) |
663 | return; |
664 | |
665 | if (effect != "none" && effect != "copy" && effect != "link" && effect != "move" ) |
666 | return; |
667 | |
668 | // FIXME: The spec allows this in all circumstances. There is probably no value |
669 | // in ignoring attempts to change it. |
670 | if (!canReadTypes()) |
671 | return; |
672 | |
673 | m_dropEffect = effect; |
674 | } |
675 | |
676 | String DataTransfer::effectAllowed() const |
677 | { |
678 | return m_effectAllowed; |
679 | } |
680 | |
681 | void DataTransfer::setEffectAllowed(const String& effect) |
682 | { |
683 | if (!forDrag()) |
684 | return; |
685 | |
686 | // Ignore any attempts to set it to an unknown value. |
687 | if (dragOpFromIEOp(effect) == DragOperationPrivate) |
688 | return; |
689 | |
690 | if (!canWriteData()) |
691 | return; |
692 | |
693 | m_effectAllowed = effect; |
694 | } |
695 | |
696 | void DataTransfer::moveDragState(Ref<DataTransfer>&& other) |
697 | { |
698 | RELEASE_ASSERT(is<StaticPasteboard>(other->pasteboard())); |
699 | // We clear the platform pasteboard here to ensure that the pasteboard doesn't contain any data |
700 | // that may have been written before starting the drag, and after ending the last drag session. |
701 | // After pushing the static pasteboard's contents to the platform, the pasteboard should only |
702 | // contain data that was in the static pasteboard. |
703 | m_pasteboard->clear(); |
704 | other->commitToPasteboard(*m_pasteboard); |
705 | |
706 | m_dropEffect = other->m_dropEffect; |
707 | m_effectAllowed = other->m_effectAllowed; |
708 | m_dragLocation = other->m_dragLocation; |
709 | m_dragImage = other->m_dragImage; |
710 | m_dragImageElement = WTFMove(other->m_dragImageElement); |
711 | m_dragImageLoader = WTFMove(other->m_dragImageLoader); |
712 | if (m_dragImageLoader) |
713 | m_dragImageLoader->moveToDataTransfer(*this); |
714 | m_fileList = WTFMove(other->m_fileList); |
715 | } |
716 | |
717 | bool DataTransfer::hasDragImage() const |
718 | { |
719 | return m_dragImage || m_dragImageElement; |
720 | } |
721 | |
722 | #endif // ENABLE(DRAG_SUPPORT) |
723 | |
724 | } // namespace WebCore |
725 | |