Index: third_party/WebKit/Source/core/clipboard/DataTransfer.cpp |
diff --git a/third_party/WebKit/Source/core/clipboard/DataTransfer.cpp b/third_party/WebKit/Source/core/clipboard/DataTransfer.cpp |
index 8aed56e7fa3c901f9ffe1ac96774b170f4086270..fc51051f87097a90d06565be99f0f27224e307d0 100644 |
--- a/third_party/WebKit/Source/core/clipboard/DataTransfer.cpp |
+++ b/third_party/WebKit/Source/core/clipboard/DataTransfer.cpp |
@@ -25,6 +25,7 @@ |
#include "core/clipboard/DataTransfer.h" |
+#include <memory> |
#include "core/HTMLNames.h" |
#include "core/clipboard/DataObject.h" |
#include "core/clipboard/DataTransferItem.h" |
@@ -34,19 +35,105 @@ |
#include "core/editing/serializers/Serialization.h" |
#include "core/fileapi/FileList.h" |
#include "core/frame/LocalFrame.h" |
+#include "core/frame/VisualViewport.h" |
#include "core/html/HTMLImageElement.h" |
#include "core/html/TextControlElement.h" |
#include "core/layout/LayoutImage.h" |
#include "core/layout/LayoutObject.h" |
#include "core/loader/resource/ImageResourceContent.h" |
+#include "core/page/ChromeClient.h" |
+#include "core/page/Page.h" |
+#include "core/paint/PaintInfo.h" |
+#include "core/paint/PaintLayer.h" |
+#include "core/paint/PaintLayerPainter.h" |
#include "platform/DragImage.h" |
#include "platform/clipboard/ClipboardMimeTypes.h" |
#include "platform/clipboard/ClipboardUtilities.h" |
+#include "platform/graphics/StaticBitmapImage.h" |
+#include "platform/graphics/paint/PaintRecordBuilder.h" |
#include "platform/network/mime/MIMETypeRegistry.h" |
-#include <memory> |
+#include "public/platform/WebScreenInfo.h" |
+#include "third_party/skia/include/core/SkSurface.h" |
namespace blink { |
+namespace { |
+ |
+class DraggedNodeImageBuilder { |
+ STACK_ALLOCATED(); |
+ |
+ public: |
+ DraggedNodeImageBuilder(const LocalFrame& local_frame, Node& node) |
+ : local_frame_(&local_frame), |
+ node_(&node) |
+#if DCHECK_IS_ON() |
+ , |
+ dom_tree_version_(node.GetDocument().DomTreeVersion()) |
+#endif |
+ { |
+ for (Node& descendant : NodeTraversal::InclusiveDescendantsOf(*node_)) |
+ descendant.SetDragged(true); |
+ } |
+ |
+ ~DraggedNodeImageBuilder() { |
+#if DCHECK_IS_ON() |
+ DCHECK_EQ(dom_tree_version_, node_->GetDocument().DomTreeVersion()); |
+#endif |
+ for (Node& descendant : NodeTraversal::InclusiveDescendantsOf(*node_)) |
+ descendant.SetDragged(false); |
+ } |
+ |
+ std::unique_ptr<DragImage> CreateImage() { |
+#if DCHECK_IS_ON() |
+ DCHECK_EQ(dom_tree_version_, node_->GetDocument().DomTreeVersion()); |
+#endif |
+ // Construct layout object for |m_node| with pseudo class "-webkit-drag" |
+ local_frame_->View()->UpdateAllLifecyclePhasesExceptPaint(); |
+ LayoutObject* const dragged_layout_object = node_->GetLayoutObject(); |
+ if (!dragged_layout_object) |
+ return nullptr; |
+ // Paint starting at the nearest stacking context, clipped to the object |
+ // itself. This will also paint the contents behind the object if the |
+ // object contains transparency and there are other elements in the same |
+ // stacking context which stacked below. |
+ PaintLayer* layer = dragged_layout_object->EnclosingLayer(); |
+ if (!layer->StackingNode()->IsStackingContext()) |
+ layer = layer->StackingNode()->AncestorStackingContextNode()->Layer(); |
+ IntRect absolute_bounding_box = |
+ dragged_layout_object->AbsoluteBoundingBoxRectIncludingDescendants(); |
+ FloatRect bounding_box = |
+ layer->GetLayoutObject() |
+ .AbsoluteToLocalQuad(FloatQuad(absolute_bounding_box), |
+ kUseTransforms) |
+ .BoundingBox(); |
+ PaintLayerPaintingInfo painting_info(layer, LayoutRect(bounding_box), |
+ kGlobalPaintFlattenCompositingLayers, |
+ LayoutSize()); |
+ PaintLayerFlags flags = kPaintLayerHaveTransparency | |
+ kPaintLayerAppliedTransform | |
+ kPaintLayerUncachedClipRects; |
+ PaintRecordBuilder builder( |
+ DataTransfer::DeviceSpaceBounds(bounding_box, *local_frame_)); |
+ PaintLayerPainter(*layer).Paint(builder.Context(), painting_info, flags); |
+ PropertyTreeState border_box_properties = PropertyTreeState::Root(); |
+ if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { |
+ border_box_properties = |
+ *layer->GetLayoutObject().LocalBorderBoxProperties(); |
+ } |
+ return DataTransfer::CreateDragImageForFrame( |
+ *local_frame_, 1.0f, |
+ LayoutObject::ShouldRespectImageOrientation(dragged_layout_object), |
+ bounding_box, builder, border_box_properties); |
+ } |
+ |
+ private: |
+ const Member<const LocalFrame> local_frame_; |
+ const Member<Node> node_; |
+#if DCHECK_IS_ON() |
+ const uint64_t dom_tree_version_; |
+#endif |
+}; |
+} // namespace |
static DragOperation ConvertEffectAllowedToDragOperation(const String& op) { |
// Values specified in |
// http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#dom-datatransfer-effectallowed |
@@ -255,6 +342,68 @@ void DataTransfer::SetDragImageElement(Node* node, const IntPoint& loc) { |
setDragImage(0, node, loc); |
} |
+// static |
+// Converts from bounds in CSS space to device space based on the given |
+// frame. |
+FloatRect DataTransfer::DeviceSpaceBounds(const FloatRect css_bounds, |
+ const LocalFrame& frame) { |
+ float device_scale_factor = frame.GetPage()->DeviceScaleFactorDeprecated(); |
+ float page_scale_factor = frame.GetPage()->GetVisualViewport().Scale(); |
+ FloatRect device_bounds(css_bounds); |
+ device_bounds.SetWidth(css_bounds.Width() * device_scale_factor * |
+ page_scale_factor); |
+ device_bounds.SetHeight(css_bounds.Height() * device_scale_factor * |
+ page_scale_factor); |
+ return device_bounds; |
+} |
+ |
+// static |
+// Returns a DragImage whose bitmap contains |contents|, positioned and scaled |
+// in device space. |
+std::unique_ptr<DragImage> DataTransfer::CreateDragImageForFrame( |
+ const LocalFrame& frame, |
+ float opacity, |
+ RespectImageOrientationEnum image_orientation, |
+ const FloatRect& css_bounds, |
+ PaintRecordBuilder& builder, |
+ const PropertyTreeState& property_tree_state) { |
+ float device_scale_factor = frame.GetPage()->DeviceScaleFactorDeprecated(); |
+ float page_scale_factor = frame.GetPage()->GetVisualViewport().Scale(); |
+ |
+ FloatRect device_bounds = DeviceSpaceBounds(css_bounds, frame); |
+ |
+ AffineTransform transform; |
+ transform.Scale(device_scale_factor * page_scale_factor); |
+ transform.Translate(-device_bounds.X(), -device_bounds.Y()); |
+ |
+ // Rasterize upfront, since DragImage::create() is going to do it anyway |
+ // (SkImage::asLegacyBitmap). |
+ SkSurfaceProps surface_props(0, kUnknown_SkPixelGeometry); |
+ sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul( |
+ device_bounds.Width(), device_bounds.Height(), &surface_props); |
+ if (!surface) |
+ return nullptr; |
+ |
+ SkiaPaintCanvas skia_paint_canvas(surface->getCanvas()); |
+ skia_paint_canvas.concat(AffineTransformToSkMatrix(transform)); |
+ builder.EndRecording(skia_paint_canvas, property_tree_state); |
+ |
+ RefPtr<Image> image = StaticBitmapImage::Create(surface->makeImageSnapshot()); |
+ float screen_device_scale_factor = |
+ frame.GetPage()->GetChromeClient().GetScreenInfo().device_scale_factor; |
+ |
+ return DragImage::Create(image.Get(), image_orientation, |
+ screen_device_scale_factor, kInterpolationHigh, |
+ opacity); |
+} |
+ |
+// static |
+std::unique_ptr<DragImage> DataTransfer::NodeImage(const LocalFrame& frame, |
+ Node& node) { |
+ DraggedNodeImageBuilder image_node(frame, node); |
+ return image_node.CreateImage(); |
+} |
+ |
std::unique_ptr<DragImage> DataTransfer::CreateDragImage( |
IntPoint& loc, |
LocalFrame* frame) const { |