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