1 /*
2 * Copyright (C) 2006, 2007, 2008 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 #include "third_party/blink/renderer/core/clipboard/data_transfer.h"
27
28 #include <memory>
29
30 #include "base/optional.h"
31 #include "build/build_config.h"
32 #include "third_party/blink/public/common/widget/screen_info.h"
33 #include "third_party/blink/renderer/core/clipboard/clipboard_mime_types.h"
34 #include "third_party/blink/renderer/core/clipboard/clipboard_utilities.h"
35 #include "third_party/blink/renderer/core/clipboard/data_object.h"
36 #include "third_party/blink/renderer/core/clipboard/data_transfer_access_policy.h"
37 #include "third_party/blink/renderer/core/clipboard/data_transfer_item.h"
38 #include "third_party/blink/renderer/core/clipboard/data_transfer_item_list.h"
39 #include "third_party/blink/renderer/core/editing/ephemeral_range.h"
40 #include "third_party/blink/renderer/core/editing/frame_selection.h"
41 #include "third_party/blink/renderer/core/editing/serializers/serialization.h"
42 #include "third_party/blink/renderer/core/editing/visible_selection.h"
43 #include "third_party/blink/renderer/core/fileapi/file_list.h"
44 #include "third_party/blink/renderer/core/frame/local_frame.h"
45 #include "third_party/blink/renderer/core/frame/visual_viewport.h"
46 #include "third_party/blink/renderer/core/html/forms/text_control_element.h"
47 #include "third_party/blink/renderer/core/html/html_image_element.h"
48 #include "third_party/blink/renderer/core/layout/layout_image.h"
49 #include "third_party/blink/renderer/core/layout/layout_object.h"
50 #include "third_party/blink/renderer/core/loader/resource/image_resource_content.h"
51 #include "third_party/blink/renderer/core/page/chrome_client.h"
52 #include "third_party/blink/renderer/core/page/drag_image.h"
53 #include "third_party/blink/renderer/core/page/page.h"
54 #include "third_party/blink/renderer/core/paint/paint_info.h"
55 #include "third_party/blink/renderer/core/paint/paint_layer.h"
56 #include "third_party/blink/renderer/core/paint/paint_layer_painter.h"
57 #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
58 #include "third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h"
59 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
60 #include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h"
61 #include "third_party/blink/renderer/platform/network/mime/mime_type_registry.h"
62 #include "third_party/skia/include/core/SkSurface.h"
63
64 namespace blink {
65
66 namespace {
67
68 class DraggedNodeImageBuilder {
69 STACK_ALLOCATED();
70
71 public:
DraggedNodeImageBuilder(LocalFrame & local_frame,Node & node)72 DraggedNodeImageBuilder(LocalFrame& local_frame, Node& node)
73 : local_frame_(&local_frame),
74 node_(&node)
75 #if DCHECK_IS_ON()
76 ,
77 dom_tree_version_(node.GetDocument().DomTreeVersion())
78 #endif
79 {
80 for (Node& descendant : NodeTraversal::InclusiveDescendantsOf(*node_))
81 descendant.SetDragged(true);
82 }
83
~DraggedNodeImageBuilder()84 ~DraggedNodeImageBuilder() {
85 #if DCHECK_IS_ON()
86 DCHECK_EQ(dom_tree_version_, node_->GetDocument().DomTreeVersion());
87 #endif
88 for (Node& descendant : NodeTraversal::InclusiveDescendantsOf(*node_))
89 descendant.SetDragged(false);
90 }
91
CreateImage()92 std::unique_ptr<DragImage> CreateImage() {
93 #if DCHECK_IS_ON()
94 DCHECK_EQ(dom_tree_version_, node_->GetDocument().DomTreeVersion());
95 #endif
96 // Construct layout object for |node_| with pseudo class "-webkit-drag"
97 local_frame_->View()->UpdateAllLifecyclePhasesExceptPaint(
98 DocumentUpdateReason::kDragImage);
99 LayoutObject* const dragged_layout_object = node_->GetLayoutObject();
100 if (!dragged_layout_object)
101 return nullptr;
102 // Paint starting at the nearest stacking context, clipped to the object
103 // itself. This will also paint the contents behind the object if the
104 // object contains transparency and there are other elements in the same
105 // stacking context which stacked below.
106 PaintLayer* layer = dragged_layout_object->EnclosingLayer();
107 if (!layer->GetLayoutObject().IsStackingContext())
108 layer = layer->AncestorStackingContext();
109
110 IntRect absolute_bounding_box =
111 dragged_layout_object->AbsoluteBoundingBoxRectIncludingDescendants();
112 // TODO(chrishtr): consider using the root frame's visible rect instead
113 // of the local frame, to avoid over-clipping.
114 IntRect visible_rect(IntPoint(),
115 layer->GetLayoutObject().GetFrameView()->Size());
116 // If the absolute bounding box is large enough to be possibly a memory
117 // or IPC payload issue, clip it to the visible content rect.
118 if (absolute_bounding_box.Size().Area() > visible_rect.Size().Area()) {
119 absolute_bounding_box.Intersect(visible_rect);
120 }
121
122 FloatRect bounding_box =
123 layer->GetLayoutObject()
124 .AbsoluteToLocalQuad(FloatQuad(absolute_bounding_box))
125 .BoundingBox();
126 PaintLayerPaintingInfo painting_info(
127 layer, CullRect(EnclosingIntRect(bounding_box)),
128 kGlobalPaintFlattenCompositingLayers, PhysicalOffset());
129 PaintLayerFlags flags = kPaintLayerHaveTransparency;
130 PaintRecordBuilder builder;
131
132 dragged_layout_object->GetDocument().Lifecycle().AdvanceTo(
133 DocumentLifecycle::kInPaint);
134 PaintLayerPainter(*layer).Paint(builder.Context(), painting_info, flags);
135 dragged_layout_object->GetDocument().Lifecycle().AdvanceTo(
136 DocumentLifecycle::kPaintClean);
137
138 FloatPoint paint_offset = bounding_box.Location();
139 PropertyTreeState border_box_properties = layer->GetLayoutObject()
140 .FirstFragment()
141 .LocalBorderBoxProperties()
142 .Unalias();
143 // We paint in the containing transform node's space. Add the offset from
144 // the layer to this transform space.
145 paint_offset +=
146 FloatPoint(layer->GetLayoutObject().FirstFragment().PaintOffset());
147
148 return DataTransfer::CreateDragImageForFrame(
149 *local_frame_, 1.0f,
150 LayoutObject::ShouldRespectImageOrientation(dragged_layout_object),
151 bounding_box.Size(), paint_offset, builder, border_box_properties);
152 }
153
154 private:
155 LocalFrame* const local_frame_;
156 Node* const node_;
157 #if DCHECK_IS_ON()
158 const uint64_t dom_tree_version_;
159 #endif
160 };
161
162 } // namespace
163
ConvertEffectAllowedToDragOperation(const String & op)164 static base::Optional<DragOperation> ConvertEffectAllowedToDragOperation(
165 const String& op) {
166 // Values specified in
167 // https://html.spec.whatwg.org/multipage/dnd.html#dom-datatransfer-effectallowed
168 if (op == "uninitialized")
169 return kDragOperationEvery;
170 if (op == "none")
171 return kDragOperationNone;
172 if (op == "copy")
173 return kDragOperationCopy;
174 if (op == "link")
175 return kDragOperationLink;
176 if (op == "move")
177 return kDragOperationMove;
178 if (op == "copyLink")
179 return static_cast<DragOperation>(kDragOperationCopy | kDragOperationLink);
180 if (op == "copyMove")
181 return static_cast<DragOperation>(kDragOperationCopy | kDragOperationMove);
182 if (op == "linkMove")
183 return static_cast<DragOperation>(kDragOperationLink | kDragOperationMove);
184 if (op == "all")
185 return kDragOperationEvery;
186 return base::nullopt;
187 }
188
ConvertDragOperationToEffectAllowed(DragOperation op)189 static String ConvertDragOperationToEffectAllowed(DragOperation op) {
190 if (((op & kDragOperationMove) && (op & kDragOperationCopy) &&
191 (op & kDragOperationLink)) ||
192 (op == kDragOperationEvery))
193 return "all";
194 if ((op & kDragOperationMove) && (op & kDragOperationCopy))
195 return "copyMove";
196 if ((op & kDragOperationMove) && (op & kDragOperationLink))
197 return "linkMove";
198 if ((op & kDragOperationCopy) && (op & kDragOperationLink))
199 return "copyLink";
200 if (op & kDragOperationMove)
201 return "move";
202 if (op & kDragOperationCopy)
203 return "copy";
204 if (op & kDragOperationLink)
205 return "link";
206 return "none";
207 }
208
209 // We provide the IE clipboard types (URL and Text), and the clipboard types
210 // specified in the HTML spec. See
211 // https://html.spec.whatwg.org/multipage/dnd.html#the-datatransfer-interface
NormalizeType(const String & type,bool * convert_to_url=nullptr)212 static String NormalizeType(const String& type,
213 bool* convert_to_url = nullptr) {
214 String clean_type = type.StripWhiteSpace().LowerASCII();
215 if (clean_type == kMimeTypeText ||
216 clean_type.StartsWith(kMimeTypeTextPlainEtc))
217 return kMimeTypeTextPlain;
218 if (clean_type == kMimeTypeURL) {
219 if (convert_to_url)
220 *convert_to_url = true;
221 return kMimeTypeTextURIList;
222 }
223 return clean_type;
224 }
225
226 // static
Create()227 DataTransfer* DataTransfer::Create() {
228 DataTransfer* data = Create(
229 kCopyAndPaste, DataTransferAccessPolicy::kWritable, DataObject::Create());
230 data->drop_effect_ = "none";
231 data->effect_allowed_ = "none";
232 return data;
233 }
234
235 // static
Create(DataTransferType type,DataTransferAccessPolicy policy,DataObject * data_object)236 DataTransfer* DataTransfer::Create(DataTransferType type,
237 DataTransferAccessPolicy policy,
238 DataObject* data_object) {
239 return MakeGarbageCollected<DataTransfer>(type, policy, data_object);
240 }
241
242 DataTransfer::~DataTransfer() = default;
243
setDropEffect(const String & effect)244 void DataTransfer::setDropEffect(const String& effect) {
245 if (!IsForDragAndDrop())
246 return;
247
248 // The attribute must ignore any attempts to set it to a value other than
249 // none, copy, link, and move.
250 if (effect != "none" && effect != "copy" && effect != "link" &&
251 effect != "move")
252 return;
253
254 // The specification states that dropEffect can be changed at all times, even
255 // if the DataTransfer instance is protected or neutered.
256 //
257 // Allowing these changes seems inconsequential, but findDropZone() in
258 // EventHandler.cpp relies on being able to call setDropEffect during
259 // dragenter, when the DataTransfer policy is DataTransferTypesReadable.
260 drop_effect_ = effect;
261 }
262
setEffectAllowed(const String & effect)263 void DataTransfer::setEffectAllowed(const String& effect) {
264 if (!IsForDragAndDrop())
265 return;
266
267 if (!ConvertEffectAllowedToDragOperation(effect)) {
268 // This means that there was no conversion, and the effectAllowed that
269 // we are passed isn't a valid effectAllowed, so we should ignore it,
270 // and not set |effect_allowed_|.
271
272 // The attribute must ignore any attempts to set it to a value other than
273 // none, copy, copyLink, copyMove, link, linkMove, move, all, and
274 // uninitialized.
275 return;
276 }
277
278 if (CanWriteData())
279 effect_allowed_ = effect;
280 }
281
clearData(const String & type)282 void DataTransfer::clearData(const String& type) {
283 if (!CanWriteData())
284 return;
285
286 if (type.IsNull())
287 data_object_->ClearAll();
288 else
289 data_object_->ClearData(NormalizeType(type));
290 }
291
getData(const String & type) const292 String DataTransfer::getData(const String& type) const {
293 if (!CanReadData())
294 return String();
295
296 bool convert_to_url = false;
297 String data = data_object_->GetData(NormalizeType(type, &convert_to_url));
298 if (!convert_to_url)
299 return data;
300 return ConvertURIListToURL(data);
301 }
302
setData(const String & type,const String & data)303 void DataTransfer::setData(const String& type, const String& data) {
304 if (!CanWriteData())
305 return;
306
307 data_object_->SetData(NormalizeType(type), data);
308 }
309
hasDataStoreItemListChanged() const310 bool DataTransfer::hasDataStoreItemListChanged() const {
311 return data_store_item_list_changed_ || !CanReadTypes();
312 }
313
OnItemListChanged()314 void DataTransfer::OnItemListChanged() {
315 data_store_item_list_changed_ = true;
316 }
317
types()318 Vector<String> DataTransfer::types() {
319 if (!CanReadTypes())
320 return Vector<String>();
321
322 data_store_item_list_changed_ = false;
323 return data_object_->Types();
324 }
325
files() const326 FileList* DataTransfer::files() const {
327 auto* files = MakeGarbageCollected<FileList>();
328 if (!CanReadData())
329 return files;
330
331 for (uint32_t i = 0; i < data_object_->length(); ++i) {
332 if (data_object_->Item(i)->Kind() == DataObjectItem::kFileKind) {
333 Blob* blob = data_object_->Item(i)->GetAsFile();
334 if (auto* file = DynamicTo<File>(blob))
335 files->Append(file);
336 }
337 }
338
339 return files;
340 }
341
setDragImage(Element * image,int x,int y)342 void DataTransfer::setDragImage(Element* image, int x, int y) {
343 DCHECK(image);
344
345 if (!IsForDragAndDrop())
346 return;
347
348 IntPoint location(x, y);
349 auto* html_image_element = DynamicTo<HTMLImageElement>(image);
350 if (html_image_element && !image->isConnected())
351 SetDragImageResource(html_image_element->CachedImage(), location);
352 else
353 SetDragImageElement(image, location);
354 }
355
ClearDragImage()356 void DataTransfer::ClearDragImage() {
357 setDragImage(nullptr, nullptr, IntPoint());
358 }
359
SetDragImageResource(ImageResourceContent * img,const IntPoint & loc)360 void DataTransfer::SetDragImageResource(ImageResourceContent* img,
361 const IntPoint& loc) {
362 setDragImage(img, nullptr, loc);
363 }
364
SetDragImageElement(Node * node,const IntPoint & loc)365 void DataTransfer::SetDragImageElement(Node* node, const IntPoint& loc) {
366 setDragImage(nullptr, node, loc);
367 }
368
ClipByVisualViewport(const FloatRect & absolute_rect,const LocalFrame & frame)369 FloatRect DataTransfer::ClipByVisualViewport(const FloatRect& absolute_rect,
370 const LocalFrame& frame) {
371 IntRect viewport_in_root_frame =
372 EnclosingIntRect(frame.GetPage()->GetVisualViewport().VisibleRect());
373 FloatRect absolute_viewport =
374 FloatRect(frame.View()->ConvertFromRootFrame(viewport_in_root_frame));
375 return Intersection(absolute_viewport, absolute_rect);
376 }
377
378 // static
379 // Converts from size in CSS space to device space based on the given frame.
DeviceSpaceSize(const FloatSize & css_size,const LocalFrame & frame)380 FloatSize DataTransfer::DeviceSpaceSize(const FloatSize& css_size,
381 const LocalFrame& frame) {
382 float device_scale_factor = frame.GetPage()->DeviceScaleFactorDeprecated();
383 float page_scale_factor = frame.GetPage()->GetVisualViewport().Scale();
384 FloatSize device_size(css_size);
385 device_size.Scale(device_scale_factor * page_scale_factor);
386 return device_size;
387 }
388
389 // static
390 // Returns a DragImage whose bitmap contains |contents|, positioned and scaled
391 // in device space.
CreateDragImageForFrame(LocalFrame & frame,float opacity,RespectImageOrientationEnum image_orientation,const FloatSize & css_size,const FloatPoint & paint_offset,PaintRecordBuilder & builder,const PropertyTreeState & property_tree_state)392 std::unique_ptr<DragImage> DataTransfer::CreateDragImageForFrame(
393 LocalFrame& frame,
394 float opacity,
395 RespectImageOrientationEnum image_orientation,
396 const FloatSize& css_size,
397 const FloatPoint& paint_offset,
398 PaintRecordBuilder& builder,
399 const PropertyTreeState& property_tree_state) {
400 float device_scale_factor = frame.GetPage()->DeviceScaleFactorDeprecated();
401 float page_scale_factor = frame.GetPage()->GetVisualViewport().Scale();
402
403 FloatSize device_size = DeviceSpaceSize(css_size, frame);
404 AffineTransform transform;
405 FloatSize paint_offset_size =
406 DeviceSpaceSize(FloatSize(paint_offset.X(), paint_offset.Y()), frame);
407 transform.Translate(-paint_offset_size.Width(), -paint_offset_size.Height());
408 transform.Scale(device_scale_factor * page_scale_factor);
409
410 // Rasterize upfront, since DragImage::create() is going to do it anyway
411 // (SkImage::asLegacyBitmap).
412 SkSurfaceProps surface_props(0, kUnknown_SkPixelGeometry);
413 sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(
414 device_size.Width(), device_size.Height(), &surface_props);
415 if (!surface)
416 return nullptr;
417
418 SkiaPaintCanvas skia_paint_canvas(surface->getCanvas());
419 skia_paint_canvas.concat(AffineTransformToSkMatrix(transform));
420 builder.EndRecording(skia_paint_canvas, property_tree_state);
421
422 scoped_refptr<Image> image =
423 UnacceleratedStaticBitmapImage::Create(surface->makeImageSnapshot());
424 ChromeClient& chrome_client = frame.GetPage()->GetChromeClient();
425 float screen_device_scale_factor =
426 chrome_client.GetScreenInfo(frame).device_scale_factor;
427
428 return DragImage::Create(image.get(), image_orientation,
429 screen_device_scale_factor, kInterpolationDefault,
430 opacity);
431 }
432
433 // static
NodeImage(LocalFrame & frame,Node & node)434 std::unique_ptr<DragImage> DataTransfer::NodeImage(LocalFrame& frame,
435 Node& node) {
436 DraggedNodeImageBuilder image_node(frame, node);
437 return image_node.CreateImage();
438 }
439
CreateDragImage(IntPoint & loc,LocalFrame * frame) const440 std::unique_ptr<DragImage> DataTransfer::CreateDragImage(
441 IntPoint& loc,
442 LocalFrame* frame) const {
443 if (drag_image_element_) {
444 loc = drag_loc_;
445
446 return NodeImage(*frame, *drag_image_element_);
447 }
448 if (drag_image_) {
449 loc = drag_loc_;
450 return DragImage::Create(drag_image_->GetImage());
451 }
452 return nullptr;
453 }
454
GetImageResourceContent(Element * element)455 static ImageResourceContent* GetImageResourceContent(Element* element) {
456 // Attempt to pull ImageResourceContent from element
457 DCHECK(element);
458 if (auto* image = DynamicTo<LayoutImage>(element->GetLayoutObject())) {
459 if (image->CachedImage() && !image->CachedImage()->ErrorOccurred())
460 return image->CachedImage();
461 }
462 return nullptr;
463 }
464
WriteImageToDataObject(DataObject * data_object,Element * element,const KURL & image_url)465 static void WriteImageToDataObject(DataObject* data_object,
466 Element* element,
467 const KURL& image_url) {
468 // Shove image data into a DataObject for use as a file
469 ImageResourceContent* cached_image = GetImageResourceContent(element);
470 if (!cached_image || !cached_image->GetImage() || !cached_image->IsLoaded())
471 return;
472
473 Image* image = cached_image->GetImage();
474 scoped_refptr<SharedBuffer> image_buffer = image->Data();
475 if (!image_buffer || !image_buffer->size())
476 return;
477
478 data_object->AddSharedBuffer(
479 image_buffer, image_url, image->FilenameExtension(),
480 cached_image->GetResponse().HttpHeaderFields().Get(
481 http_names::kContentDisposition));
482 }
483
DeclareAndWriteDragImage(Element * element,const KURL & link_url,const KURL & image_url,const String & title)484 void DataTransfer::DeclareAndWriteDragImage(Element* element,
485 const KURL& link_url,
486 const KURL& image_url,
487 const String& title) {
488 if (!data_object_)
489 return;
490
491 data_object_->SetURLAndTitle(link_url.IsValid() ? link_url : image_url,
492 title);
493
494 // Write the bytes in the image to the file format.
495 WriteImageToDataObject(data_object_.Get(), element, image_url);
496
497 // Put img tag on the clipboard referencing the image
498 data_object_->SetData(kMimeTypeTextHTML,
499 CreateMarkup(element, kIncludeNode, kResolveAllURLs));
500 }
501
WriteURL(Node * node,const KURL & url,const String & title)502 void DataTransfer::WriteURL(Node* node, const KURL& url, const String& title) {
503 if (!data_object_)
504 return;
505 DCHECK(!url.IsEmpty());
506
507 data_object_->SetURLAndTitle(url, title);
508
509 // The URL can also be used as plain text.
510 data_object_->SetData(kMimeTypeTextPlain, url.GetString());
511
512 // The URL can also be used as an HTML fragment.
513 data_object_->SetHTMLAndBaseURL(
514 CreateMarkup(node, kIncludeNode, kResolveAllURLs), url);
515 }
516
WriteSelection(const FrameSelection & selection)517 void DataTransfer::WriteSelection(const FrameSelection& selection) {
518 if (!data_object_)
519 return;
520
521 if (!EnclosingTextControl(
522 selection.ComputeVisibleSelectionInDOMTreeDeprecated().Start())) {
523 data_object_->SetHTMLAndBaseURL(selection.SelectedHTMLForClipboard(),
524 selection.GetFrame()->GetDocument()->Url());
525 }
526
527 String str = selection.SelectedTextForClipboard();
528 #if defined(OS_WIN)
529 ReplaceNewlinesWithWindowsStyleNewlines(str);
530 #endif
531 ReplaceNBSPWithSpace(str);
532 data_object_->SetData(kMimeTypeTextPlain, str);
533 }
534
SetAccessPolicy(DataTransferAccessPolicy policy)535 void DataTransfer::SetAccessPolicy(DataTransferAccessPolicy policy) {
536 // once you go numb, can never go back
537 DCHECK(policy_ != DataTransferAccessPolicy::kNumb ||
538 policy == DataTransferAccessPolicy::kNumb);
539 policy_ = policy;
540 }
541
CanReadTypes() const542 bool DataTransfer::CanReadTypes() const {
543 return policy_ == DataTransferAccessPolicy::kReadable ||
544 policy_ == DataTransferAccessPolicy::kTypesReadable ||
545 policy_ == DataTransferAccessPolicy::kWritable;
546 }
547
CanReadData() const548 bool DataTransfer::CanReadData() const {
549 return policy_ == DataTransferAccessPolicy::kReadable ||
550 policy_ == DataTransferAccessPolicy::kWritable;
551 }
552
CanWriteData() const553 bool DataTransfer::CanWriteData() const {
554 return policy_ == DataTransferAccessPolicy::kWritable;
555 }
556
CanSetDragImage() const557 bool DataTransfer::CanSetDragImage() const {
558 return policy_ == DataTransferAccessPolicy::kImageWritable ||
559 policy_ == DataTransferAccessPolicy::kWritable;
560 }
561
SourceOperation() const562 DragOperation DataTransfer::SourceOperation() const {
563 base::Optional<DragOperation> op =
564 ConvertEffectAllowedToDragOperation(effect_allowed_);
565 DCHECK(op);
566 return *op;
567 }
568
DestinationOperation() const569 DragOperation DataTransfer::DestinationOperation() const {
570 base::Optional<DragOperation> op =
571 ConvertEffectAllowedToDragOperation(drop_effect_);
572 DCHECK(op == kDragOperationCopy || op == kDragOperationNone ||
573 op == kDragOperationLink || op == kDragOperationMove ||
574 op == kDragOperationEvery);
575 return *op;
576 }
577
SetSourceOperation(DragOperation op)578 void DataTransfer::SetSourceOperation(DragOperation op) {
579 DCHECK_NE(op, kDragOperationPrivate);
580 effect_allowed_ = ConvertDragOperationToEffectAllowed(op);
581 }
582
SetDestinationOperation(DragOperation op)583 void DataTransfer::SetDestinationOperation(DragOperation op) {
584 DCHECK(op == kDragOperationCopy || op == kDragOperationNone ||
585 op == kDragOperationLink || op == kDragOperationMove);
586 drop_effect_ = ConvertDragOperationToEffectAllowed(op);
587 }
588
HasDropZoneType(const String & keyword)589 bool DataTransfer::HasDropZoneType(const String& keyword) {
590 if (keyword.StartsWith("file:"))
591 return HasFileOfType(keyword.Substring(5));
592
593 if (keyword.StartsWith("string:"))
594 return HasStringOfType(keyword.Substring(7));
595
596 return false;
597 }
598
items()599 DataTransferItemList* DataTransfer::items() {
600 // TODO: According to the spec, we are supposed to return the same collection
601 // of items each time. We now return a wrapper that always wraps the *same*
602 // set of items, so JS shouldn't be able to tell, but we probably still want
603 // to fix this.
604 return MakeGarbageCollected<DataTransferItemList>(this, data_object_);
605 }
606
GetDataObject() const607 DataObject* DataTransfer::GetDataObject() const {
608 return data_object_;
609 }
610
DataTransfer(DataTransferType type,DataTransferAccessPolicy policy,DataObject * data_object)611 DataTransfer::DataTransfer(DataTransferType type,
612 DataTransferAccessPolicy policy,
613 DataObject* data_object)
614 : policy_(policy),
615 drop_effect_("uninitialized"),
616 effect_allowed_("uninitialized"),
617 transfer_type_(type),
618 data_object_(data_object),
619 data_store_item_list_changed_(true) {
620 data_object_->AddObserver(this);
621 }
622
setDragImage(ImageResourceContent * image,Node * node,const IntPoint & loc)623 void DataTransfer::setDragImage(ImageResourceContent* image,
624 Node* node,
625 const IntPoint& loc) {
626 if (!CanSetDragImage())
627 return;
628
629 drag_image_ = image;
630 drag_loc_ = loc;
631 drag_image_element_ = node;
632 }
633
HasFileOfType(const String & type) const634 bool DataTransfer::HasFileOfType(const String& type) const {
635 if (!CanReadTypes())
636 return false;
637
638 for (uint32_t i = 0; i < data_object_->length(); ++i) {
639 if (data_object_->Item(i)->Kind() == DataObjectItem::kFileKind) {
640 Blob* blob = data_object_->Item(i)->GetAsFile();
641 if (blob && blob->IsFile() &&
642 DeprecatedEqualIgnoringCase(blob->type(), type))
643 return true;
644 }
645 }
646 return false;
647 }
648
HasStringOfType(const String & type) const649 bool DataTransfer::HasStringOfType(const String& type) const {
650 if (!CanReadTypes())
651 return false;
652
653 return data_object_->Types().Contains(type);
654 }
655
ConvertDropZoneOperationToDragOperation(const String & drag_operation)656 DragOperation ConvertDropZoneOperationToDragOperation(
657 const String& drag_operation) {
658 if (drag_operation == "copy")
659 return kDragOperationCopy;
660 if (drag_operation == "move")
661 return kDragOperationMove;
662 if (drag_operation == "link")
663 return kDragOperationLink;
664 return kDragOperationNone;
665 }
666
ConvertDragOperationToDropZoneOperation(DragOperation operation)667 String ConvertDragOperationToDropZoneOperation(DragOperation operation) {
668 switch (operation) {
669 case kDragOperationCopy:
670 return String("copy");
671 case kDragOperationMove:
672 return String("move");
673 case kDragOperationLink:
674 return String("link");
675 default:
676 return String("copy");
677 }
678 }
679
Trace(Visitor * visitor) const680 void DataTransfer::Trace(Visitor* visitor) const {
681 visitor->Trace(data_object_);
682 visitor->Trace(drag_image_);
683 visitor->Trace(drag_image_element_);
684 ScriptWrappable::Trace(visitor);
685 }
686
687 } // namespace blink
688