0

Plumb origin through for drags.

This repurposes the data type used to tag drags as renderer-initiated by
also using it to store the origin of the source of the drag data.

Currently, opaque origins are not plumbed through end-to-end. An opaque
origin will still cause the drag to be treated as renderer-tainted;
however, when reading out the origin, a new unique opaque origin will be
created.

Bug: 1346429
Change-Id: I52467e30d590473ded1eb2dd15fb40ff8ceabb23
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4837192
Commit-Queue: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Avi Drissman <avi@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1202178}
This commit is contained in:
Daniel Cheng
2023-09-28 00:46:54 +00:00
committed by Chromium LUCI CQ
parent d4de0b3f31
commit 2cb89f90a3
38 changed files with 300 additions and 93 deletions

@ -70,6 +70,7 @@
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/rect.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace chrome {
namespace {
@ -345,10 +346,12 @@ class DragStartWaiter : public aura::client::DragDropClient {
// WaitUntilDragStart can take a long time to return (it returns only after
// the OS decides that the drag-and-drop has ended).
//
// Before returning populates |text|, |html| and other parameters with data
// that would have been passed to the OS). If the caller is not interested in
// this data, then the corresponding argument can be null.
void WaitUntilDragStart(std::string* text,
// Before returning populates `source_origin`, `text`, `html` and other
// parameters with data that would have been passed to the OS). If the caller
// is not interested in this data, then the corresponding argument can be
// null.
void WaitUntilDragStart(absl::optional<url::Origin>* source_origin,
std::string* text,
std::string* html,
int* operation,
gfx::Point* location_inside_web_contents) {
@ -357,6 +360,9 @@ class DragStartWaiter : public aura::client::DragDropClient {
// message_loop_runner_->Quit is only called from StartDragAndDrop.
DCHECK(drag_started_);
if (source_origin) {
*source_origin = source_origin_;
}
if (text)
*text = text_;
if (html)
@ -387,6 +393,7 @@ class DragStartWaiter : public aura::client::DragDropClient {
drag_started_ = true;
message_loop_runner_->Quit();
source_origin_ = data->GetRendererTaintedOrigin();
std::u16string text;
if (data->GetString(&text))
text_ = base::UTF16ToUTF8(text);
@ -448,6 +455,7 @@ class DragStartWaiter : public aura::client::DragDropClient {
// Data captured during the first intercepted StartDragAndDrop call.
bool drag_started_;
absl::optional<url::Origin> source_origin_;
std::string text_;
std::string html_;
int operation_;
@ -1264,12 +1272,16 @@ IN_PROC_BROWSER_TEST_P(DragAndDropBrowserTest, MAYBE_DragStartInFrame) {
// Verify data being passed to the OS.
{
absl::optional<url::Origin> source_origin;
std::string text;
std::string html;
int operation = 0;
gfx::Point location_inside_web_contents;
drag_start_waiter.WaitUntilDragStart(&text, &html, &operation,
drag_start_waiter.WaitUntilDragStart(&source_origin, &text, &html,
&operation,
&location_inside_web_contents);
ASSERT_TRUE(source_origin.has_value());
EXPECT_EQ(embedded_test_server()->GetOrigin(frame_site), source_origin);
EXPECT_EQ(embedded_test_server()->GetURL(frame_site,
"/drag_and_drop/cors-allowed.jpg"),
text);
@ -1402,7 +1414,8 @@ void DragAndDropBrowserTest::DragImageBetweenFrames_Start(
// The next step of the test (DragImageBetweenFrames_Step2) runs inside the
// nested drag-and-drop message loop - the call below won't return until the
// drag-and-drop has already ended.
drag_start_waiter.WaitUntilDragStart(nullptr, nullptr, nullptr, nullptr);
drag_start_waiter.WaitUntilDragStart(nullptr, nullptr, nullptr, nullptr,
nullptr);
DragImageBetweenFrames_Step3(&state);
}
@ -1634,7 +1647,8 @@ IN_PROC_BROWSER_TEST_P(DragAndDropBrowserTest,
// The next step of the test (DragImageFromDisappearingFrame_Step2) runs
// inside the nested drag-and-drop message loop - the call below won't return
// until the drag-and-drop has already ended.
drag_start_waiter.WaitUntilDragStart(nullptr, nullptr, nullptr, nullptr);
drag_start_waiter.WaitUntilDragStart(nullptr, nullptr, nullptr, nullptr,
nullptr);
DragImageFromDisappearingFrame_Step3(&state);
}
@ -1748,7 +1762,8 @@ IN_PROC_BROWSER_TEST_P(DragAndDropBrowserTest, MAYBE_CrossSiteDrag) {
// The next step of the test (CrossSiteDrag_Step2) runs inside the
// nested drag-and-drop message loop - the call below won't return until the
// drag-and-drop has already ended.
drag_start_waiter.WaitUntilDragStart(nullptr, nullptr, nullptr, nullptr);
drag_start_waiter.WaitUntilDragStart(nullptr, nullptr, nullptr, nullptr,
nullptr);
CrossSiteDrag_Step3(&state);
}
@ -1886,7 +1901,8 @@ IN_PROC_BROWSER_TEST_P(DragAndDropBrowserTest, MAYBE_CrossTabDrag) {
// The next step of the test (CrossTabDrag_Step2) runs inside the
// nested drag-and-drop message loop - the call below won't return until the
// drag-and-drop has already ended.
drag_start_waiter.WaitUntilDragStart(nullptr, nullptr, nullptr, nullptr);
drag_start_waiter.WaitUntilDragStart(nullptr, nullptr, nullptr, nullptr,
nullptr);
CrossTabDrag_Step3(&state);
}

@ -58,6 +58,7 @@ class CONTENT_EXPORT WebContentsNSViewBridge : public mojom::WebContentsNSView {
void MakeFirstResponder() override;
void TakeFocus(bool reverse) override;
void StartDrag(const content::DropData& drop_data,
const url::Origin& source_origin,
uint32_t operation_mask,
const gfx::ImageSkia& image,
const gfx::Vector2d& image_offset,

@ -98,6 +98,7 @@ void WebContentsNSViewBridge::TakeFocus(bool reverse) {
}
void WebContentsNSViewBridge::StartDrag(const content::DropData& drop_data,
const url::Origin& source_origin,
uint32_t operation_mask,
const gfx::ImageSkia& image,
const gfx::Vector2d& image_offset,
@ -105,6 +106,7 @@ void WebContentsNSViewBridge::StartDrag(const content::DropData& drop_data,
NSPoint offset = NSPointFromCGPoint(
gfx::PointAtOffsetFromOrigin(image_offset).ToCGPoint());
[ns_view_ startDragWithDropData:drop_data
sourceOrigin:source_origin
dragOperationMask:operation_mask
image:gfx::NSImageFromImageSkia(image)
offset:offset

@ -19,6 +19,10 @@ namespace remote_cocoa::mojom {
class WebContentsNSViewHost;
} // namespace remote_cocoa::mojom
namespace url {
class Origin;
}
@class WebDragSource;
CONTENT_EXPORT
@ -40,6 +44,7 @@ CONTENT_EXPORT
- (instancetype)initWithViewsHostableView:(ui::ViewsHostableView*)v;
- (void)registerDragTypes;
- (void)startDragWithDropData:(const content::DropData&)dropData
sourceOrigin:(const url::Origin&)sourceOrigin
dragOperationMask:(NSDragOperation)operationMask
image:(NSImage*)image
offset:(NSPoint)offset

@ -233,6 +233,7 @@ STATIC_ASSERT_ENUM(NSDragOperationMove, ui::DragDropTypes::DRAG_MOVE);
}
- (void)startDragWithDropData:(const DropData&)dropData
sourceOrigin:(const url::Origin&)sourceOrigin
dragOperationMask:(NSDragOperation)operationMask
image:(NSImage*)image
offset:(NSPoint)offset
@ -253,6 +254,7 @@ STATIC_ASSERT_ENUM(NSDragOperationMove, ui::DragDropTypes::DRAG_MOVE);
_dragSource = [[WebDragSource alloc] initWithHost:_host
dropData:dropData
sourceOrigin:sourceOrigin
isPrivileged:isPrivileged];
NSDraggingItem* draggingItem =
[[NSDraggingItem alloc] initWithPasteboardWriter:_dragSource];

@ -21,6 +21,10 @@ namespace remote_cocoa::mojom {
class WebContentsNSViewHost;
} // namespace remote_cocoa::mojom
namespace url {
class Origin;
}
// A class that handles managing the data for drags from the
// WebContentsViewCocoa.
CONTENT_EXPORT
@ -29,6 +33,7 @@ CONTENT_EXPORT
// Initialize a WebDragSource object for a drag.
- (instancetype)initWithHost:(remote_cocoa::mojom::WebContentsNSViewHost*)host
dropData:(const content::DropData&)dropData
sourceOrigin:(const url::Origin&)sourceOrigin
isPrivileged:(BOOL)privileged;
// Call when the WebContents is gone.

@ -33,6 +33,7 @@
#include "ui/base/clipboard/clipboard_constants.h"
#include "ui/base/clipboard/custom_data_helper.h"
#include "ui/base/cocoa/cocoa_base_utils.h"
#include "url/origin.h"
#include "url/url_constants.h"
@implementation WebDragSource {
@ -44,6 +45,9 @@
// The drop data.
content::DropData _dropData;
// The source origin the drop data came from.
url::Origin _sourceOrigin;
// Whether to mark the drag as having come from a privileged WebContents.
BOOL _privileged;
@ -60,10 +64,12 @@
- (instancetype)initWithHost:(remote_cocoa::mojom::WebContentsNSViewHost*)host
dropData:(const content::DropData&)dropData
sourceOrigin:(const url::Origin&)sourceOrigin
isPrivileged:(BOOL)privileged {
if ((self = [super init])) {
_host = host;
_dropData = dropData;
_sourceOrigin = sourceOrigin;
_privileged = privileged;
}
@ -295,9 +301,15 @@
return [NSData dataWithBytes:pickle.data() length:pickle.size()];
}
// Source origin of the drop data.
if ([type isEqualToString:ui::kUTTypeChromiumRendererInitiatedDrag]) {
return _sourceOrigin.opaque()
? [NSString string]
: base::SysUTF8ToNSString(_sourceOrigin.Serialize());
}
// Flavors used to tag.
if ([type isEqualToString:ui::kUTTypeChromiumInitiatedDrag] ||
[type isEqualToString:ui::kUTTypeChromiumRendererInitiatedDrag] ||
[type isEqualToString:ui::kUTTypeChromiumPrivilegedInitiatedDrag]) {
// The type _was_ promised and someone decided to call the bluff.
return [NSData data];

@ -8818,8 +8818,9 @@ void RenderFrameHostImpl::StartDragging(
const gfx::Rect& drag_obj_rect_in_dip,
blink::mojom::DragEventSourceInfoPtr event_info) {
GetRenderWidgetHost()->StartDragging(
std::move(drag_data), drag_operations_mask, unsafe_bitmap,
cursor_offset_in_dip, drag_obj_rect_in_dip, std::move(event_info));
std::move(drag_data), GetLastCommittedOrigin(), drag_operations_mask,
unsafe_bitmap, cursor_offset_in_dip, drag_obj_rect_in_dip,
std::move(event_info));
}
void RenderFrameHostImpl::CreateNewPopupWidget(

@ -33,6 +33,10 @@ class OverscrollRefreshHandler;
}
#endif
namespace url {
class Origin;
}
namespace content {
class RenderFrameHost;
class RenderWidgetHostImpl;
@ -80,6 +84,7 @@ class CONTENT_EXPORT RenderViewHostDelegateView {
// `blink::DragController::StartDrag()`.
virtual void StartDragging(
const DropData& drop_data,
const url::Origin& source_origin,
blink::DragOperationsMask allowed_ops,
const gfx::ImageSkia& image,
const gfx::Vector2d& cursor_offset,

@ -98,6 +98,7 @@ class MockDraggingRenderViewHostDelegateView
public:
~MockDraggingRenderViewHostDelegateView() override {}
void StartDragging(const DropData& drop_data,
const url::Origin& source_origin,
blink::DragOperationsMask allowed_ops,
const gfx::ImageSkia& image,
const gfx::Vector2d& cursor_offset,

@ -2697,6 +2697,7 @@ void RenderWidgetHostImpl::UpdateBrowserControlsState(
void RenderWidgetHostImpl::StartDragging(
blink::mojom::DragDataPtr drag_data,
const url::Origin& source_origin,
DragOperationsMask drag_operations_mask,
const SkBitmap& bitmap,
const gfx::Vector2d& cursor_offset_in_dip,
@ -2782,8 +2783,8 @@ void RenderWidgetHostImpl::StartDragging(
scaled_rect.Scale(scale);
rect = gfx::ToRoundedRect(scaled_rect);
#endif
view->StartDragging(filtered_data, drag_operations_mask, image, offset, rect,
*event_info, this);
view->StartDragging(filtered_data, source_origin, drag_operations_mask, image,
offset, rect, *event_info, this);
}
// static

@ -909,6 +909,7 @@ class CONTENT_EXPORT RenderWidgetHostImpl
bool animate);
void StartDragging(blink::mojom::DragDataPtr drag_data,
const url::Origin& source_origin,
blink::DragOperationsMask drag_operations_mask,
const SkBitmap& unsafe_bitmap,
const gfx::Vector2d& cursor_offset_in_dip,

@ -276,6 +276,7 @@ class MockRenderViewHostDelegateView : public RenderViewHostDelegateView {
// RenderViewHostDelegateView:
void StartDragging(const DropData& drop_data,
const url::Origin& source_origin,
blink::DragOperationsMask allowed_ops,
const gfx::ImageSkia& image,
const gfx::Vector2d& cursor_offset,
@ -2139,7 +2140,7 @@ TEST_F(RenderWidgetHostTest, RendererExitedNoDrag) {
DropDataToDragData(
drop_data, file_system_manager, process_->GetID(),
ChromeBlobStorageContext::GetFor(process_->GetBrowserContext())),
drag_operation, SkBitmap(), gfx::Vector2d(), gfx::Rect(),
url::Origin(), drag_operation, SkBitmap(), gfx::Vector2d(), gfx::Rect(),
blink::mojom::DragEventSourceInfo::New());
EXPECT_EQ(delegate_->mock_delegate_view()->start_dragging_count(), 1);
@ -2150,7 +2151,7 @@ TEST_F(RenderWidgetHostTest, RendererExitedNoDrag) {
DropDataToDragData(
drop_data, file_system_manager, process_->GetID(),
ChromeBlobStorageContext::GetFor(process_->GetBrowserContext())),
drag_operation, SkBitmap(), gfx::Vector2d(), gfx::Rect(),
url::Origin(), drag_operation, SkBitmap(), gfx::Vector2d(), gfx::Rect(),
blink::mojom::DragEventSourceInfo::New());
EXPECT_EQ(delegate_->mock_delegate_view()->start_dragging_count(), 1);
}

@ -353,6 +353,7 @@ void WebContentsViewAndroid::ShowPopupMenu(
void WebContentsViewAndroid::StartDragging(
const DropData& drop_data,
const url::Origin& source_origin,
blink::DragOperationsMask allowed_ops,
const gfx::ImageSkia& image,
const gfx::Vector2d& cursor_offset,

@ -102,6 +102,7 @@ class WebContentsViewAndroid : public WebContentsView,
bool allow_multiple_selection) override;
ui::OverscrollRefreshHandler* GetOverscrollRefreshHandler() const override;
void StartDragging(const DropData& drop_data,
const url::Origin& source_origin,
blink::DragOperationsMask allowed_ops,
const gfx::ImageSkia& image,
const gfx::Vector2d& cursor_offset,

@ -252,9 +252,10 @@ const ui::ClipboardFormatType& GetFileSystemFileFormatType() {
// Utility to fill a ui::OSExchangeDataProvider object from DropData.
void PrepareDragData(const DropData& drop_data,
const url::Origin source_origin,
ui::OSExchangeDataProvider* provider,
WebContentsImpl* web_contents) {
provider->MarkOriginatedFromRenderer();
provider->MarkRendererTaintedFromOrigin(source_origin);
#if BUILDFLAG(IS_WIN)
// Put download before file contents to prefer the download of a image over
// its thumbnail link.
@ -698,7 +699,15 @@ void WebContentsViewAura::SetDelegateForTesting(
void WebContentsViewAura::PrepareDropData(
DropData* drop_data,
const ui::OSExchangeData& data) const {
drop_data->did_originate_from_renderer = data.DidOriginateFromRenderer();
#if BUILDFLAG(IS_CHROMEOS_ASH)
// TODO(b/256022714): Using `IsRendererTainted()` breaks the Files app. Always
// setting this to false is currently believed to be safe-ish because ChromeOS
// separates URL and filename metadata and does not implement the DownloadURL
// protocol.
drop_data->did_originate_from_renderer = false;
#else
drop_data->did_originate_from_renderer = data.IsRendererTainted();
#endif
drop_data->is_from_privileged = data.IsFromPrivileged();
std::u16string plain_text;
@ -1147,6 +1156,7 @@ void WebContentsViewAura::ShowContextMenu(RenderFrameHost& render_frame_host,
void WebContentsViewAura::StartDragging(
const DropData& drop_data,
const url::Origin& source_origin,
blink::DragOperationsMask operations,
const gfx::ImageSkia& image,
const gfx::Vector2d& cursor_offset,
@ -1180,7 +1190,7 @@ void WebContentsViewAura::StartDragging(
selection_controller->HideAndDisallowShowingAutomatically();
std::unique_ptr<ui::OSExchangeDataProvider> provider =
ui::OSExchangeDataProviderFactory::CreateProvider();
PrepareDragData(drop_data, provider.get(), web_contents_);
PrepareDragData(drop_data, source_origin, provider.get(), web_contents_);
auto data = std::make_unique<ui::OSExchangeData>(std::move(provider));
data->SetSource(

@ -39,6 +39,10 @@ class DropTargetEvent;
class TouchSelectionController;
}
namespace url {
class Origin;
}
namespace content {
class GestureNavSimple;
class RenderWidgetHostImpl;
@ -211,6 +215,7 @@ class CONTENT_EXPORT WebContentsViewAura
void ShowContextMenu(RenderFrameHost& render_frame_host,
const ContextMenuParams& params) override;
void StartDragging(const DropData& drop_data,
const url::Origin& source_origin,
blink::DragOperationsMask operations,
const gfx::ImageSkia& image,
const gfx::Vector2d& cursor_offset,

@ -34,6 +34,7 @@
#include "ui/display/display_switches.h"
#include "ui/events/base_event_utils.h"
#include "ui/gfx/geometry/rect.h"
#include "url/origin.h"
#if BUILDFLAG(IS_WIN)
#include "ui/base/dragdrop/os_exchange_data_provider_win.h"
@ -387,7 +388,7 @@ TEST_F(WebContentsViewAuraTest, MAYBE_DragDropFilesOriginateFromRenderer) {
// Simulate the drag originating in the renderer process, in which case
// any file data should be filtered out (anchor drag scenario) except in
// CHROMEOS_ASH.
data->MarkOriginatedFromRenderer();
data->MarkRendererTaintedFromOrigin(url::Origin());
ui::DropTargetEvent event(*data.get(), kClientPt, kScreenPt,
ui::DragDropTypes::DRAG_COPY);
@ -407,7 +408,6 @@ TEST_F(WebContentsViewAuraTest, MAYBE_DragDropFilesOriginateFromRenderer) {
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
// CHROMEOS_ASH always returns false for DidOriginateFromRenderer().
ASSERT_FALSE(view->current_drag_data_->filenames.empty());
#else
ASSERT_TRUE(view->current_drag_data_->filenames.empty());
@ -440,7 +440,8 @@ TEST_F(WebContentsViewAuraTest, MAYBE_DragDropFilesOriginateFromRenderer) {
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
// CHROMEOS_ASH always returns false for DidOriginateFromRenderer().
// CHROMEOS_ASH never filters out files from a drop, even if the drag
// originated from a renderer, because otherwise, it breaks the Files app.
ASSERT_FALSE(drop_complete_data_->drop_data.filenames.empty());
#else
ASSERT_TRUE(drop_complete_data_->drop_data.filenames.empty());
@ -483,7 +484,7 @@ TEST_F(WebContentsViewAuraTest, MAYBE_DragDropImageFromRenderer) {
data->SetFileContents(filename, file_contents);
data->SetURL(url, url_title);
data->SetHtml(html, GURL());
data->MarkOriginatedFromRenderer();
data->MarkRendererTaintedFromOrigin(url::Origin());
ui::DropTargetEvent event(*data.get(), kClientPt, kScreenPt,
ui::DragDropTypes::DRAG_COPY);
@ -642,7 +643,7 @@ TEST_F(WebContentsViewAuraTest, DragDropVirtualFilesOriginateFromRenderer) {
// Simulate the drag originating in the renderer process, in which case
// any file data should be filtered out (anchor drag scenario).
data->MarkOriginatedFromRenderer();
data->MarkRendererTaintedFromOrigin(url::Origin());
ui::DropTargetEvent event(*data.get(), kClientPt, kScreenPt,
ui::DragDropTypes::DRAG_COPY);
@ -682,7 +683,7 @@ TEST_F(WebContentsViewAuraTest, DragDropVirtualFilesOriginateFromRenderer) {
TEST_F(WebContentsViewAuraTest, DragDropUrlData) {
WebContentsViewAura* view = GetView();
auto data = std::make_unique<ui::OSExchangeData>();
data->MarkOriginatedFromRenderer();
data->MarkRendererTaintedFromOrigin(url::Origin());
const std::string url_spec = "https://www.wikipedia.org/";
const GURL url(url_spec);
@ -761,7 +762,8 @@ TEST_F(WebContentsViewAuraTest, StartDragging) {
DropData drop_data;
drop_data.text.emplace(u"Hello World!");
view->StartDragging(drop_data, blink::DragOperationsMask::kDragOperationNone,
view->StartDragging(drop_data, url::Origin(),
blink::DragOperationsMask::kDragOperationNone,
gfx::ImageSkia(), gfx::Vector2d(), gfx::Rect(),
blink::mojom::DragEventSourceInfo(),
RenderWidgetHostImpl::From(rvh()->GetWidget()));
@ -832,7 +834,8 @@ TEST_F(WebContentsViewAuraTest, StartDragFromPrivilegedWebContents) {
view->drag_in_progress_ = true;
DropData drop_data;
view->StartDragging(drop_data, blink::DragOperationsMask::kDragOperationNone,
view->StartDragging(drop_data, url::Origin(),
blink::DragOperationsMask::kDragOperationNone,
gfx::ImageSkia(), gfx::Vector2d(), gfx::Rect(),
blink::mojom::DragEventSourceInfo(),
RenderWidgetHostImpl::From(rvh()->GetWidget()));

@ -203,6 +203,7 @@ void WebContentsViewChildFrame::ShowPopupMenu(
void WebContentsViewChildFrame::StartDragging(
const DropData& drop_data,
const url::Origin& source_origin,
DragOperationsMask ops,
const gfx::ImageSkia& image,
const gfx::Vector2d& cursor_offset,
@ -210,8 +211,8 @@ void WebContentsViewChildFrame::StartDragging(
const blink::mojom::DragEventSourceInfo& event_info,
RenderWidgetHostImpl* source_rwh) {
if (auto* view = GetOuterDelegateView()) {
view->StartDragging(drop_data, ops, image, cursor_offset, drag_obj_rect,
event_info, source_rwh);
view->StartDragging(drop_data, source_origin, ops, image, cursor_offset,
drag_obj_rect, event_info, source_rwh);
} else {
web_contents_->GetOuterWebContents()->SystemDragEnded(source_rwh);
}

@ -66,6 +66,7 @@ class WebContentsViewChildFrame : public WebContentsView,
void ShowContextMenu(RenderFrameHost& render_frame_host,
const ContextMenuParams& params) override;
void StartDragging(const DropData& drop_data,
const url::Origin& source_origin,
blink::DragOperationsMask allowed_ops,
const gfx::ImageSkia& image,
const gfx::Vector2d& cursor_offset,

@ -94,6 +94,7 @@ class WebContentsViewMac : public WebContentsView,
// RenderViewHostDelegateView:
void StartDragging(const DropData& drop_data,
const url::Origin& source_origin,
blink::DragOperationsMask allowed_operations,
const gfx::ImageSkia& image,
const gfx::Vector2d& cursor_offset,

@ -156,6 +156,7 @@ void WebContentsViewMac::UpdateWindowControlsOverlay(
void WebContentsViewMac::StartDragging(
const DropData& drop_data,
const url::Origin& source_origin,
DragOperationsMask allowed_operations,
const gfx::ImageSkia& image,
const gfx::Vector2d& cursor_offset,
@ -183,11 +184,11 @@ void WebContentsViewMac::StartDragging(
// TODO(crbug.com/1302094): The param `drag_obj_rect` is unused.
if (remote_ns_view_) {
remote_ns_view_->StartDrag(drop_data, mask, image, cursor_offset,
is_privileged);
remote_ns_view_->StartDrag(drop_data, source_origin, mask, image,
cursor_offset, is_privileged);
} else {
in_process_ns_view_bridge_->StartDrag(drop_data, mask, image, cursor_offset,
is_privileged);
in_process_ns_view_bridge_->StartDrag(drop_data, source_origin, mask, image,
cursor_offset, is_privileged);
}
}

@ -8,6 +8,7 @@
#include "content/public/common/drop_data.h"
#include "content/public/test/test_renderer_host.h"
#include "testing/gtest_mac.h"
#include "url/origin.h"
namespace content {
@ -19,6 +20,7 @@ TEST_F(WebDragSourceMacTest, DragInvalidlyEscapedBookmarklet) {
WebDragSource* source = [[WebDragSource alloc] initWithHost:nullptr
dropData:drop_data
sourceOrigin:url::Origin()
isPrivileged:NO];
// Test that asking for the data of an invalidly-escaped URL doesn't throw any

@ -8,6 +8,7 @@ import "content/public/common/drop_data.mojom";
import "mojo/public/mojom/base/file_path.mojom";
import "ui/gfx/geometry/mojom/geometry.mojom";
import "ui/gfx/image/mojom/image.mojom";
import "url/mojom/origin.mojom";
import "url/mojom/url.mojom";
// Interface through which a WebContentsViewMac communicates with its NSView in
@ -42,6 +43,7 @@ interface WebContentsNSView {
// to whether the WebContents initiating the drag is being used to implement
// a UI surface. See `WebContentsDelegate::IsPrivileged()` for more context.
StartDrag(content.mojom.DropData drop_data,
url.mojom.Origin source_origin,
uint32 operation_mask,
gfx.mojom.ImageSkia? image,
gfx.mojom.Vector2d image_offset,

@ -91,7 +91,15 @@ COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES)
extern NSString* const kUTTypeChromiumPrivilegedInitiatedDrag;
// Data type placed on dragging pasteboards when the drag is initiated from a
// renderer. There is never any data associated with this type.
// renderer. If the initiator has a tuple origin (e.g. https://example.com),
// the data is a string representation (i.e. the result of calling
// `url::Origin::Serialize()`). Otherwise, the initiator has an opaque origin
// and the data is the empty string.
//
// This format is intentionally chosen for safer backwards compatibility with
// previous versions of Chrome, which always set an empty string for the data.
// When newer versions of Chrome attempt to interpret this data as an origin,
// they will safely treat it as a unique opaque origin.
COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES)
extern NSString* const kUTTypeChromiumRendererInitiatedDrag;

@ -13,7 +13,7 @@
#include "ui/base/clipboard/clipboard_format_type.h"
#include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
#include "ui/base/dragdrop/os_exchange_data_provider_factory.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace ui {
@ -27,12 +27,16 @@ OSExchangeData::OSExchangeData(std::unique_ptr<OSExchangeDataProvider> provider)
OSExchangeData::~OSExchangeData() {
}
void OSExchangeData::MarkOriginatedFromRenderer() {
provider_->MarkOriginatedFromRenderer();
void OSExchangeData::MarkRendererTaintedFromOrigin(const url::Origin& origin) {
provider_->MarkRendererTaintedFromOrigin(origin);
}
bool OSExchangeData::DidOriginateFromRenderer() const {
return provider_->DidOriginateFromRenderer();
bool OSExchangeData::IsRendererTainted() const {
return provider_->IsRendererTainted();
}
absl::optional<url::Origin> OSExchangeData::GetRendererTaintedOrigin() const {
return provider_->GetRendererTaintedOrigin();
}
void OSExchangeData::MarkAsFromPrivileged() {

@ -16,6 +16,7 @@
#include "base/component_export.h"
#include "base/files/file_path.h"
#include "base/functional/callback_forward.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/base/dragdrop/os_exchange_data_provider.h"
class GURL;
@ -24,6 +25,10 @@ namespace base {
class Pickle;
}
namespace url {
class Origin;
}
namespace ui {
class ClipboardFormatType;
@ -75,11 +80,14 @@ class COMPONENT_EXPORT(UI_BASE) OSExchangeData {
const OSExchangeDataProvider& provider() const { return *provider_; }
OSExchangeDataProvider& provider() { return *provider_; }
// Marks drag data as tainted if it originates from the renderer. This is used
// to avoid granting privileges to a renderer when dragging in tainted data,
// since it could allow potential escalation of privileges.
void MarkOriginatedFromRenderer();
bool DidOriginateFromRenderer() const;
// Marks drag data as tainted by the renderer, with `origin` as the source of
// the data. This is used to:
// - avoid granting privileges to a renderer when dragging in tainted data,
// since it could allow potential escalation of privileges.
// - track the origin where the drag data came from.
void MarkRendererTaintedFromOrigin(const url::Origin& origin);
bool IsRendererTainted() const;
absl::optional<url::Origin> GetRendererTaintedOrigin() const;
// Marks drag data as from privileged WebContents. This is used to
// make sure non-privileged WebContents will not accept drop data from

@ -13,11 +13,13 @@
#include "base/component_export.h"
#include "base/files/file_path.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/base/clipboard/clipboard_format_type.h"
#include "ui/base/clipboard/file_info.h"
#include "ui/base/dragdrop/download_file_info.h"
#include "ui/base/dragdrop/download_file_interface.h"
#include "url/gurl.h"
#include "url/origin.h"
#if BUILDFLAG(IS_WIN)
#include "base/functional/callback_forward.h"
@ -48,8 +50,9 @@ class COMPONENT_EXPORT(UI_BASE_DATA_EXCHANGE) OSExchangeDataProvider {
virtual std::unique_ptr<OSExchangeDataProvider> Clone() const = 0;
virtual void MarkOriginatedFromRenderer() = 0;
virtual bool DidOriginateFromRenderer() const = 0;
virtual void MarkRendererTaintedFromOrigin(const url::Origin& origin) = 0;
virtual bool IsRendererTainted() const = 0;
virtual absl::optional<url::Origin> GetRendererTaintedOrigin() const = 0;
virtual void MarkAsFromPrivileged() = 0;
virtual bool IsFromPrivileged() const = 0;

@ -37,8 +37,9 @@ class COMPONENT_EXPORT(UI_BASE) OSExchangeDataProviderMac
CreateProviderWrappingPasteboard(NSPasteboard* pasteboard);
// Overridden from OSExchangeDataProvider:
void MarkOriginatedFromRenderer() override;
bool DidOriginateFromRenderer() const override;
void MarkRendererTaintedFromOrigin(const url::Origin& origin) override;
bool IsRendererTainted() const override;
absl::optional<url::Origin> GetRendererTaintedOrigin() const override;
void MarkAsFromPrivileged() override;
bool IsFromPrivileged() const override;
void SetString(const std::u16string& data) override;

@ -135,16 +135,35 @@ OSExchangeDataProviderMac::CreateProviderWrappingPasteboard(
return std::make_unique<WrappingProvider>(pasteboard);
}
void OSExchangeDataProviderMac::MarkOriginatedFromRenderer() {
[GetPasteboard() setData:[NSData data]
forType:kUTTypeChromiumRendererInitiatedDrag];
void OSExchangeDataProviderMac::MarkRendererTaintedFromOrigin(
const url::Origin& origin) {
NSString* string = origin.opaque()
? [NSString string]
: base::SysUTF8ToNSString(origin.Serialize());
[GetPasteboard() setString:string
forType:kUTTypeChromiumRendererInitiatedDrag];
}
bool OSExchangeDataProviderMac::DidOriginateFromRenderer() const {
bool OSExchangeDataProviderMac::IsRendererTainted() const {
return [GetPasteboard().types
containsObject:kUTTypeChromiumRendererInitiatedDrag];
}
absl::optional<url::Origin>
OSExchangeDataProviderMac::GetRendererTaintedOrigin() const {
NSString* item =
[GetPasteboard() stringForType:kUTTypeChromiumRendererInitiatedDrag];
if (!item) {
return absl::nullopt;
}
if (0 == [item length]) {
return url::Origin();
}
return url::Origin::Create(GURL(base::SysNSStringToUTF8(item)));
}
void OSExchangeDataProviderMac::MarkAsFromPrivileged() {
[GetPasteboard() setData:[NSData data]
forType:kUTTypeChromiumPrivilegedInitiatedDrag];

@ -11,7 +11,6 @@
#include "base/containers/contains.h"
#include "base/files/file_path.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "net/base/filename_util.h"
#include "ui/base/clipboard/clipboard_format_type.h"
@ -33,20 +32,23 @@ std::unique_ptr<OSExchangeDataProvider> OSExchangeDataProviderNonBacked::Clone()
return clone;
}
void OSExchangeDataProviderNonBacked::MarkOriginatedFromRenderer() {
// TODO(dcheng): Currently unneeded because ChromeOS Aura correctly separates
// URL and filename metadata, and does not implement the DownloadURL protocol.
#if !BUILDFLAG(IS_CHROMEOS_ASH)
originated_from_renderer_ = true;
#endif
void OSExchangeDataProviderNonBacked::MarkRendererTaintedFromOrigin(
const url::Origin& origin) {
tainted_by_renderer_origin_ = origin;
}
bool OSExchangeDataProviderNonBacked::DidOriginateFromRenderer() const {
#if BUILDFLAG(IS_CHROMEOS_ASH)
return false;
#else
return originated_from_renderer_;
#endif
bool OSExchangeDataProviderNonBacked::IsRendererTainted() const {
return tainted_by_renderer_origin_.has_value();
}
absl::optional<url::Origin>
OSExchangeDataProviderNonBacked::GetRendererTaintedOrigin() const {
// Platform-specific implementations of OSExchangeDataProvider do not
// roundtrip opaque origins, so match that behavior here.
if (tainted_by_renderer_origin_ && tainted_by_renderer_origin_->opaque()) {
return url::Origin();
}
return tainted_by_renderer_origin_;
}
void OSExchangeDataProviderNonBacked::MarkAsFromPrivileged() {
@ -280,10 +282,8 @@ void OSExchangeDataProviderNonBacked::CopyData(
provider->source_ =
source_ ? std::make_unique<DataTransferEndpoint>(*source_.get())
: nullptr;
provider->tainted_by_renderer_origin_ = tainted_by_renderer_origin_;
provider->is_from_privileged_ = is_from_privileged_;
#if !BUILDFLAG(IS_CHROMEOS_ASH)
provider->originated_from_renderer_ = originated_from_renderer_;
#endif
}
} // namespace ui

@ -10,13 +10,14 @@
#include "base/component_export.h"
#include "base/pickle.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/base/clipboard/file_info.h"
#include "ui/base/dragdrop/os_exchange_data_provider.h"
#include "ui/gfx/geometry/vector2d.h"
#include "ui/gfx/image/image_skia.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace base {
class FilePath;
@ -41,8 +42,9 @@ class COMPONENT_EXPORT(UI_BASE) OSExchangeDataProviderNonBacked
// Overridden from OSExchangeDataProvider:
std::unique_ptr<OSExchangeDataProvider> Clone() const override;
void MarkOriginatedFromRenderer() override;
bool DidOriginateFromRenderer() const override;
void MarkRendererTaintedFromOrigin(const url::Origin& origin) override;
bool IsRendererTainted() const override;
absl::optional<url::Origin> GetRendererTaintedOrigin() const override;
void MarkAsFromPrivileged() override;
bool IsFromPrivileged() const override;
void SetString(const std::u16string& data) override;
@ -125,10 +127,8 @@ class COMPONENT_EXPORT(UI_BASE) OSExchangeDataProviderNonBacked
std::u16string html_;
GURL base_url_;
#if !BUILDFLAG(IS_CHROMEOS_ASH)
// For marking data originating from the renderer.
bool originated_from_renderer_ = false;
#endif
absl::optional<url::Origin> tainted_by_renderer_origin_;
// For marking data originating by privileged WebContents.
bool is_from_privileged_ = false;

@ -45,9 +45,7 @@ TEST(OSExchangeDataProviderNonBackedTest, CloneTest) {
original.SetFileContents(base::FilePath(kFileContentsFileName),
std::string(kFileContents));
original.SetHtml(kHtml, GURL(kBaseUrl));
#if !BUILDFLAG(IS_CHROMEOS_ASH)
original.MarkOriginatedFromRenderer();
#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
original.MarkRendererTaintedFromOrigin(url::Origin());
GURL url("www.example.com");
original.SetSource(std::make_unique<DataTransferEndpoint>(url));
@ -82,9 +80,7 @@ TEST(OSExchangeDataProviderNonBackedTest, CloneTest) {
EXPECT_EQ(kHtml, copy_html);
EXPECT_EQ(GURL(kBaseUrl), copy_base_url);
#if !BUILDFLAG(IS_CHROMEOS_ASH)
EXPECT_TRUE(copy->DidOriginateFromRenderer());
#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
EXPECT_TRUE(copy->IsRendererTainted());
DataTransferEndpoint* data_endpoint = copy->GetSource();
EXPECT_TRUE(data_endpoint);

@ -30,6 +30,7 @@
#include "base/win/shlwapi.h"
#include "net/base/filename_util.h"
#include "skia/ext/skia_utils_win.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/clipboard/clipboard_format_type.h"
#include "ui/base/clipboard/clipboard_util_win.h"
@ -43,6 +44,7 @@
#include "ui/gfx/skbitmap_operations.h"
#include "ui/strings/grit/ui_strings.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace ui {
@ -295,16 +297,32 @@ std::unique_ptr<OSExchangeDataProvider> OSExchangeDataProviderWin::Clone()
return std::make_unique<OSExchangeDataProviderWin>(data_object());
}
void OSExchangeDataProviderWin::MarkOriginatedFromRenderer() {
STGMEDIUM storage = CreateStorageForString(std::string());
void OSExchangeDataProviderWin::MarkRendererTaintedFromOrigin(
const url::Origin& origin) {
STGMEDIUM storage = CreateStorageForString(
origin.opaque() ? std::string() : origin.Serialize());
data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium(
GetRendererTaintFormatType().ToFormatEtc(), storage));
}
bool OSExchangeDataProviderWin::DidOriginateFromRenderer() const {
bool OSExchangeDataProviderWin::IsRendererTainted() const {
return HasCustomFormat(GetRendererTaintFormatType());
}
absl::optional<url::Origin>
OSExchangeDataProviderWin::GetRendererTaintedOrigin() const {
STGMEDIUM medium;
FORMATETC format_etc = GetRendererTaintFormatType().ToFormatEtc();
if (FAILED(source_object_->GetData(&format_etc, &medium))) {
return absl::nullopt;
}
base::win::ScopedHGlobal<char*> data(medium.hGlobal);
if (data.Size() == 0) {
return url::Origin();
}
return url::Origin::Create(GURL(base::StringPiece(data.get(), data.Size())));
}
void OSExchangeDataProviderWin::MarkAsFromPrivileged() {
STGMEDIUM storage = CreateStorageForString(std::string());
data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium(

@ -147,8 +147,9 @@ class COMPONENT_EXPORT(UI_BASE) OSExchangeDataProviderWin
// OSExchangeDataProvider methods.
std::unique_ptr<OSExchangeDataProvider> Clone() const override;
void MarkOriginatedFromRenderer() override;
bool DidOriginateFromRenderer() const override;
void MarkRendererTaintedFromOrigin(const url::Origin& origin) override;
bool IsRendererTainted() const override;
absl::optional<url::Origin> GetRendererTaintedOrigin() const override;
void MarkAsFromPrivileged() override;
bool IsFromPrivileged() const override;
void SetString(const std::u16string& data) override;

@ -237,4 +237,53 @@ TEST_F(OSExchangeDataTest, TestHTML) {
}
#endif
TEST_F(OSExchangeDataTest, NotRendererTainted) {
const OSExchangeData copy([&] {
OSExchangeData data;
return data.provider().Clone();
}());
EXPECT_FALSE(copy.IsRendererTainted());
EXPECT_EQ(absl::nullopt, copy.GetRendererTaintedOrigin());
}
TEST_F(OSExchangeDataTest, RendererTaintedOpaqueOrigin) {
const url::Origin tuple_origin =
url::Origin::Create(GURL("https://www.google.com/"));
const url::Origin opaque_origin = tuple_origin.DeriveNewOpaqueOrigin();
ASSERT_TRUE(opaque_origin.opaque());
const OSExchangeData copy([&] {
OSExchangeData data;
data.MarkRendererTaintedFromOrigin(opaque_origin);
return data.provider().Clone();
}());
EXPECT_TRUE(copy.IsRendererTainted());
absl::optional<url::Origin> origin = copy.GetRendererTaintedOrigin();
EXPECT_TRUE(origin.has_value());
EXPECT_TRUE(origin->opaque());
// Currently, the actual value of an opaque origin is not actually serialized
// into OSExchangeData, so expect a random opaque origin to be read out.
EXPECT_NE(opaque_origin, origin);
// And there should be no precursor tuple.
EXPECT_FALSE(origin->GetTupleOrPrecursorTupleIfOpaque().IsValid());
}
TEST_F(OSExchangeDataTest, RendererTaintedTupleOrigin) {
const url::Origin tuple_origin =
url::Origin::Create(GURL("https://www.google.com/"));
const OSExchangeData copy([&] {
OSExchangeData data;
data.MarkRendererTaintedFromOrigin(tuple_origin);
return data.provider().Clone();
}());
EXPECT_TRUE(copy.IsRendererTainted());
absl::optional<url::Origin> origin = copy.GetRendererTaintedOrigin();
EXPECT_TRUE(origin.has_value());
EXPECT_EQ(tuple_origin, origin);
}
} // namespace ui

@ -85,17 +85,35 @@ std::unique_ptr<OSExchangeDataProvider> XOSExchangeDataProvider::Clone() const {
return std::move(ret);
}
void XOSExchangeDataProvider::MarkOriginatedFromRenderer() {
format_map_.Insert(
x11::GetAtom(kRendererTaint),
scoped_refptr<base::RefCountedMemory>(
base::MakeRefCounted<base::RefCountedString>(std::string())));
void XOSExchangeDataProvider::MarkRendererTaintedFromOrigin(
const url::Origin& origin) {
format_map_.Insert(x11::GetAtom(kRendererTaint),
base::MakeRefCounted<base::RefCountedString>(
origin.opaque() ? std::string() : origin.Serialize()));
}
bool XOSExchangeDataProvider::DidOriginateFromRenderer() const {
bool XOSExchangeDataProvider::IsRendererTainted() const {
return format_map_.find(x11::GetAtom(kRendererTaint)) != format_map_.end();
}
absl::optional<url::Origin> XOSExchangeDataProvider::GetRendererTaintedOrigin()
const {
auto it = format_map_.find(x11::GetAtom(kRendererTaint));
if (it == format_map_.end()) {
return absl::nullopt;
}
ui::SelectionData data(it->first, it->second);
std::string data_as_string;
data.AssignTo(&data_as_string);
if (data_as_string.empty()) {
return url::Origin();
}
return url::Origin::Create(GURL(data_as_string));
}
void XOSExchangeDataProvider::MarkAsFromPrivileged() {
format_map_.Insert(
x11::GetAtom(kFromPrivileged),

@ -62,8 +62,9 @@ class COMPONENT_EXPORT(UI_BASE_X) XOSExchangeDataProvider
// Overridden from OSExchangeDataProvider:
std::unique_ptr<OSExchangeDataProvider> Clone() const override;
void MarkOriginatedFromRenderer() override;
bool DidOriginateFromRenderer() const override;
void MarkRendererTaintedFromOrigin(const url::Origin& origin) override;
bool IsRendererTainted() const override;
absl::optional<url::Origin> GetRendererTaintedOrigin() const override;
void MarkAsFromPrivileged() override;
bool IsFromPrivileged() const override;
void SetString(const std::u16string& data) override;