blob: bd00349bce45043591eb574a8ffb0ae69be77521 [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2020 The Chromium Authors
Henrique Ferreiro0bb413bf2020-09-17 13:37:542// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Joel Hockeyd75d5062021-02-23 19:53:255#include "content/browser/renderer_host/data_transfer_util.h"
Henrique Ferreiro0bb413bf2020-09-17 13:37:546
7#include <string>
8#include <utility>
9#include <vector>
10
Henrique Ferreiroa6ea10e52020-10-27 12:21:2511#include "base/check.h"
Ken Rockotc73e50a72020-10-27 00:32:2312#include "base/containers/span.h"
Henrique Ferreiro0bb413bf2020-09-17 13:37:5413#include "base/files/file_path.h"
Lei Zhangf40ac9a612025-06-02 22:35:1014#include "base/strings/string_util.h"
Henrique Ferreiro0bb413bf2020-09-17 13:37:5415#include "base/strings/utf_string_conversions.h"
Claudio DeSouza45a33692023-04-19 12:22:1916#include "base/uuid.h"
Andrew Williams1255f3922022-02-07 07:02:1317#include "content/browser/blob_storage/chrome_blob_storage_context.h"
Austin Sullivan559bb382021-01-26 00:52:4318#include "content/browser/file_system_access/file_system_access_manager_impl.h"
Michael Thiessen2d864d92023-04-06 15:20:5619#include "content/public/browser/browser_thread.h"
Henrique Ferreiro0bb413bf2020-09-17 13:37:5420#include "mojo/public/cpp/bindings/pending_remote.h"
Andrew Williams1255f3922022-02-07 07:02:1321#include "net/base/mime_util.h"
Henrique Ferreiro0bb413bf2020-09-17 13:37:5422#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
Andrew Williams1255f3922022-02-07 07:02:1323#include "storage/browser/blob/blob_storage_context.h"
Marijn Kruisselbrinkc1000472020-10-01 22:19:5424#include "storage/browser/file_system/external_mount_points.h"
Andrew Williams1255f3922022-02-07 07:02:1325#include "storage/browser/file_system/file_system_context.h"
26#include "third_party/blink/public/mojom/blob/serialized_blob.mojom.h"
Sharon Yang2abe1982021-10-01 15:56:3427#include "third_party/blink/public/mojom/drag/drag.mojom.h"
Joel Hockeyda00f2b72021-02-17 09:39:5228#include "third_party/blink/public/mojom/file_system_access/file_system_access_data_transfer_token.mojom.h"
Henrique Ferreiro0bb413bf2020-09-17 13:37:5429#include "ui/base/clipboard/clipboard_constants.h"
Joel Hockey5192d0a82021-01-30 18:01:5830#include "ui/base/clipboard/file_info.h"
Henrique Ferreiro0bb413bf2020-09-17 13:37:5431#include "url/gurl.h"
32
33namespace content {
34
Marijn Kruisselbrinkc1000472020-10-01 22:19:5435namespace {
36
37// On Chrome OS paths that exist on an external mount point need to be treated
Austin Sullivan559bb382021-01-26 00:52:4338// differently to make sure the File System Access code accesses these paths via
Marijn Kruisselbrinkc1000472020-10-01 22:19:5439// the correct file system backend. This method checks if this is the case, and
Austin Sullivan559bb382021-01-26 00:52:4340// updates `entry_path` to the path that should be used by the File System
41// Access implementation.
Joel Hockey460290902024-10-08 22:49:2042content::PathType MaybeRemapPath(base::FilePath* entry_path) {
Georg Neis35ff854b2024-12-17 02:02:0843#if BUILDFLAG(IS_CHROMEOS)
Marijn Kruisselbrinkc1000472020-10-01 22:19:5444 base::FilePath virtual_path;
45 auto* external_mount_points =
46 storage::ExternalMountPoints::GetSystemInstance();
47 if (external_mount_points->GetVirtualPath(*entry_path, &virtual_path)) {
48 *entry_path = std::move(virtual_path);
Joel Hockey460290902024-10-08 22:49:2049 return content::PathType::kExternal;
Marijn Kruisselbrinkc1000472020-10-01 22:19:5450 }
51#endif
Joel Hockey460290902024-10-08 22:49:2052 return content::PathType::kLocal;
Marijn Kruisselbrinkc1000472020-10-01 22:19:5453}
54
55} // namespace
56
Joel Hockeyd75d5062021-02-23 19:53:2557std::vector<blink::mojom::DataTransferFilePtr> FileInfosToDataTransferFiles(
58 const std::vector<ui::FileInfo>& filenames,
59 FileSystemAccessManagerImpl* file_system_access_manager,
60 int child_id) {
61 std::vector<blink::mojom::DataTransferFilePtr> result;
62 for (const ui::FileInfo& file_info : filenames) {
63 blink::mojom::DataTransferFilePtr file =
64 blink::mojom::DataTransferFile::New();
65 file->path = file_info.path;
66 file->display_name = file_info.display_name;
67 mojo::PendingRemote<blink::mojom::FileSystemAccessDataTransferToken>
68 pending_token;
69 base::FilePath entry_path = file_info.path;
Joel Hockey460290902024-10-08 22:49:2070 content::PathType path_type = MaybeRemapPath(&entry_path);
71 base::FilePath display_name = !file_info.display_name.empty()
72 ? file_info.display_name
73 : entry_path.BaseName();
Joel Hockeyed3d0b92025-03-11 01:48:4374 if (entry_path.empty() || display_name.empty()) {
75 continue;
76 }
Joel Hockeyd75d5062021-02-23 19:53:2577 file_system_access_manager->CreateFileSystemAccessDataTransferToken(
Joel Hockey460290902024-10-08 22:49:2078 content::PathInfo(path_type, entry_path, display_name.AsUTF8Unsafe()),
79 child_id, pending_token.InitWithNewPipeAndPassReceiver());
Joel Hockeyd75d5062021-02-23 19:53:2580 file->file_system_access_token = std::move(pending_token);
81 result.push_back(std::move(file));
82 }
83 return result;
84}
85
Andrew Williams1255f3922022-02-07 07:02:1386std::vector<blink::mojom::DragItemFileSystemFilePtr>
87FileSystemFileInfosToDragItemFileSystemFilePtr(
88 std::vector<DropData::FileSystemFileInfo> file_system_file_infos,
89 FileSystemAccessManagerImpl* file_system_access_manager,
90 scoped_refptr<content::ChromeBlobStorageContext> context) {
91 std::vector<blink::mojom::DragItemFileSystemFilePtr> result;
92 for (const content::DropData::FileSystemFileInfo& file_system_file :
93 file_system_file_infos) {
94 blink::mojom::DragItemFileSystemFilePtr item =
95 blink::mojom::DragItemFileSystemFile::New();
96 item->url = file_system_file.url;
97 item->size = file_system_file.size;
98 item->file_system_id = file_system_file.filesystem_id;
99
100 storage::FileSystemURL file_system_url =
101 file_system_access_manager->context()->CrackURLInFirstPartyContext(
102 file_system_file.url);
103 DCHECK(file_system_url.type() != storage::kFileSystemTypePersistent);
104 DCHECK(file_system_url.type() != storage::kFileSystemTypeTemporary);
105
Claudio DeSouza45a33692023-04-19 12:22:19106 std::string uuid = base::Uuid::GenerateRandomV4().AsLowercaseString();
Andrew Williams1255f3922022-02-07 07:02:13107
108 std::string content_type;
109
Michael Maltsevd9382b6692025-03-18 02:30:47110 std::string mime_type;
111 // TODO(crbug.com/40291155): Historically for blobs created from
112 // file system URLs we've only considered well known content types to
113 // avoid leaking the presence of locally installed applications when
114 // creating blobs from files in the sandboxed file system. However, since
115 // this code path should only deal with real/"trusted" paths, we could
116 // consider taking platform defined mime type mappings into account here
117 // as well. Note that the approach used here must not block or else it
118 // can't be called from the UI thread (for example, calls to
119 // GetMimeTypeFromExtension can block).
120 if (net::GetWellKnownMimeTypeFromFile(file_system_url.path(), &mime_type)) {
121 content_type = std::move(mime_type);
Andrew Williams1255f3922022-02-07 07:02:13122 }
Alison Gale770f3fc2024-04-27 00:39:58123 // TODO(crbug.com/41458368): Consider some kind of fallback type when
Andrew Williams1255f3922022-02-07 07:02:13124 // the above mime type detection fails.
125
126 mojo::PendingRemote<blink::mojom::Blob> blob_remote;
127 mojo::PendingReceiver<blink::mojom::Blob> blob_receiver =
128 blob_remote.InitWithNewPipeAndPassReceiver();
129
130 item->serialized_blob = blink::mojom::SerializedBlob::New(
131 uuid, content_type, item->size, std::move(blob_remote));
132
133 GetIOThreadTaskRunner({})->PostTask(
134 FROM_HERE,
135 base::BindOnce(
136 &ChromeBlobStorageContext::CreateFileSystemBlob, context,
137 base::WrapRefCounted(file_system_access_manager->context()),
138 std::move(blob_receiver), std::move(file_system_url),
139 std::move(uuid), std::move(content_type), item->size,
140 base::Time()));
141
142 result.push_back(std::move(item));
143 }
144 return result;
145}
146
Henrique Ferreiro0bb413bf2020-09-17 13:37:54147blink::mojom::DragDataPtr DropDataToDragData(
148 const DropData& drop_data,
Austin Sullivan559bb382021-01-26 00:52:43149 FileSystemAccessManagerImpl* file_system_access_manager,
Andrew Williams1255f3922022-02-07 07:02:13150 int child_id,
151 scoped_refptr<ChromeBlobStorageContext> chrome_blob_storage_context) {
Henrique Ferreiro0bb413bf2020-09-17 13:37:54152 // These fields are currently unused when dragging into Blink.
153 DCHECK(drop_data.download_metadata.empty());
Henrique Ferreiro0bb413bf2020-09-17 13:37:54154 DCHECK(drop_data.file_contents_content_disposition.empty());
155
156 std::vector<blink::mojom::DragItemPtr> items;
157 if (drop_data.text) {
158 blink::mojom::DragItemStringPtr item = blink::mojom::DragItemString::New();
Avi Drissman4bbdb2002025-04-03 01:43:44159 item->string_type = ui::kMimeTypePlainText;
Henrique Ferreiro0bb413bf2020-09-17 13:37:54160 item->string_data = *drop_data.text;
161 items.push_back(blink::mojom::DragItem::NewString(std::move(item)));
162 }
163 if (!drop_data.url.is_empty()) {
164 blink::mojom::DragItemStringPtr item = blink::mojom::DragItemString::New();
Avi Drissman4bbdb2002025-04-03 01:43:44165 item->string_type = ui::kMimeTypeUriList;
Henrique Ferreiro0bb413bf2020-09-17 13:37:54166 item->string_data = base::UTF8ToUTF16(drop_data.url.spec());
167 item->title = drop_data.url_title;
168 items.push_back(blink::mojom::DragItem::NewString(std::move(item)));
169 }
170 if (drop_data.html) {
171 blink::mojom::DragItemStringPtr item = blink::mojom::DragItemString::New();
Avi Drissman4bbdb2002025-04-03 01:43:44172 item->string_type = ui::kMimeTypeHtml;
Henrique Ferreiro0bb413bf2020-09-17 13:37:54173 item->string_data = *drop_data.html;
174 item->base_url = drop_data.html_base_url;
175 items.push_back(blink::mojom::DragItem::NewString(std::move(item)));
176 }
Joel Hockeyd75d5062021-02-23 19:53:25177 std::vector<blink::mojom::DataTransferFilePtr> files =
178 FileInfosToDataTransferFiles(drop_data.filenames,
179 file_system_access_manager, child_id);
Andrew Williams1255f3922022-02-07 07:02:13180 for (auto& file : files) {
181 items.push_back(blink::mojom::DragItem::NewFile(std::move(file)));
Henrique Ferreiro0bb413bf2020-09-17 13:37:54182 }
Andrew Williams1255f3922022-02-07 07:02:13183
184 std::vector<blink::mojom::DragItemFileSystemFilePtr> file_system_files =
185 FileSystemFileInfosToDragItemFileSystemFilePtr(
186 drop_data.file_system_files, file_system_access_manager,
187 std::move(chrome_blob_storage_context));
188 for (auto& file_system_file : file_system_files) {
189 items.push_back(
190 blink::mojom::DragItem::NewFileSystemFile(std::move(file_system_file)));
Henrique Ferreiro0bb413bf2020-09-17 13:37:54191 }
Joel Hockeya81a94d2021-09-15 04:12:27192 if (drop_data.file_contents_source_url.is_valid()) {
193 blink::mojom::DragItemBinaryPtr item = blink::mojom::DragItemBinary::New();
Peter Kasting5f6928c2024-11-29 21:25:11194 item->data =
195 mojo_base::BigBuffer(base::as_byte_span(drop_data.file_contents));
Joel Hockey3acd3b02021-11-19 21:10:20196 item->is_image_accessible = drop_data.file_contents_image_accessible;
Joel Hockeya81a94d2021-09-15 04:12:27197 item->source_url = drop_data.file_contents_source_url;
198 item->filename_extension =
199 base::FilePath(drop_data.file_contents_filename_extension);
200 items.push_back(blink::mojom::DragItem::NewBinary(std::move(item)));
201 }
202 for (const std::pair<const std::u16string, std::u16string>& data :
Henrique Ferreiro0bb413bf2020-09-17 13:37:54203 drop_data.custom_data) {
204 blink::mojom::DragItemStringPtr item = blink::mojom::DragItemString::New();
205 item->string_type = base::UTF16ToUTF8(data.first);
206 item->string_data = data.second;
207 items.push_back(blink::mojom::DragItem::NewString(std::move(item)));
208 }
209
Henrique Ferreiroa6ea10e52020-10-27 12:21:25210 return blink::mojom::DragData::New(
211 std::move(items),
212 // While this shouldn't be a problem in production code, as the
213 // real file_system_id should never be empty if used in browser to
214 // renderer messages, some tests use this function to test renderer to
215 // browser messages, in which case the field is unused and this will hit
216 // a DCHECK.
217 drop_data.filesystem_id.empty()
Arthur Sonzognic686e8f2024-01-11 08:36:37218 ? std::nullopt
219 : std::optional<std::string>(
Henrique Ferreiroa6ea10e52020-10-27 12:21:25220 base::UTF16ToUTF8(drop_data.filesystem_id)),
Roger Tawa2f912ee02023-09-28 13:24:56221 /*force_default_action=*/!drop_data.document_is_handling_drag,
Henrique Ferreiroa6ea10e52020-10-27 12:21:25222 drop_data.referrer_policy);
Henrique Ferreiro0bb413bf2020-09-17 13:37:54223}
224
Dave Tapuskad2a55202020-10-27 17:08:11225blink::mojom::DragDataPtr DropMetaDataToDragData(
226 const std::vector<DropData::Metadata>& drop_meta_data) {
227 std::vector<blink::mojom::DragItemPtr> items;
228
229 for (const auto& meta_data_item : drop_meta_data) {
230 if (meta_data_item.kind == DropData::Kind::STRING) {
231 blink::mojom::DragItemStringPtr item =
232 blink::mojom::DragItemString::New();
233 item->string_type = base::UTF16ToUTF8(meta_data_item.mime_type);
234 // Have to pass a dummy URL here instead of an empty URL because the
235 // DropData received by browser_plugins goes through a round trip:
236 // DropData::MetaData --> WebDragData-->DropData. In the end, DropData
237 // will contain an empty URL (which means no URL is dragged) if the URL in
238 // WebDragData is empty.
Avi Drissman4bbdb2002025-04-03 01:43:44239 if (base::EqualsASCII(meta_data_item.mime_type, ui::kMimeTypeUriList)) {
Jan Wilken Dörrie2c470ea2021-03-22 22:26:24240 item->string_data = u"about:dragdrop-placeholder";
Dave Tapuskad2a55202020-10-27 17:08:11241 }
242 items.push_back(blink::mojom::DragItem::NewString(std::move(item)));
243 continue;
244 }
245
246 // TODO(hush): crbug.com/584789. Blink needs to support creating a file with
247 // just the mimetype. This is needed to drag files to WebView on Android
248 // platform.
249 if ((meta_data_item.kind == DropData::Kind::FILENAME) &&
250 !meta_data_item.filename.empty()) {
Joel Hockeyd75d5062021-02-23 19:53:25251 blink::mojom::DataTransferFilePtr item =
252 blink::mojom::DataTransferFile::New();
Dave Tapuskad2a55202020-10-27 17:08:11253 item->path = meta_data_item.filename;
254 items.push_back(blink::mojom::DragItem::NewFile(std::move(item)));
255 continue;
256 }
257
258 if (meta_data_item.kind == DropData::Kind::FILESYSTEMFILE) {
259 blink::mojom::DragItemFileSystemFilePtr item =
260 blink::mojom::DragItemFileSystemFile::New();
261 item->url = meta_data_item.file_system_url;
262 items.push_back(
263 blink::mojom::DragItem::NewFileSystemFile(std::move(item)));
264 continue;
265 }
Joel Hockeya81a94d2021-09-15 04:12:27266
267 if (meta_data_item.kind == DropData::Kind::BINARY) {
268 blink::mojom::DragItemBinaryPtr item =
269 blink::mojom::DragItemBinary::New();
270 item->source_url = meta_data_item.file_contents_url;
271 items.push_back(blink::mojom::DragItem::NewBinary(std::move(item)));
272 continue;
273 }
Dave Tapuskad2a55202020-10-27 17:08:11274 }
Arthur Sonzognic686e8f2024-01-11 08:36:37275 return blink::mojom::DragData::New(std::move(items), std::nullopt,
Roger Tawa2f912ee02023-09-28 13:24:56276 /*force_default_action=*/false,
Dave Tapuskad2a55202020-10-27 17:08:11277 network::mojom::ReferrerPolicy::kDefault);
278}
279
Henrique Ferreiro0bb413bf2020-09-17 13:37:54280DropData DragDataToDropData(const blink::mojom::DragData& drag_data) {
Henrique Ferreiroa6ea10e52020-10-27 12:21:25281 // This field should be empty when dragging from the renderer.
282 DCHECK(!drag_data.file_system_id);
Henrique Ferreiro0bb413bf2020-09-17 13:37:54283
Henrique Ferreiroa6ea10e52020-10-27 12:21:25284 DropData result;
Henrique Ferreiro0bb413bf2020-09-17 13:37:54285 for (const blink::mojom::DragItemPtr& item : drag_data.items) {
286 switch (item->which()) {
Daniel Cheng07383d492022-04-21 15:02:33287 case blink::mojom::DragItemDataView::Tag::kString: {
Henrique Ferreiro0bb413bf2020-09-17 13:37:54288 const blink::mojom::DragItemStringPtr& string_item = item->get_string();
289 std::string str_type = string_item->string_type;
Avi Drissman4bbdb2002025-04-03 01:43:44290 if (str_type == ui::kMimeTypePlainText) {
Henrique Ferreiro0bb413bf2020-09-17 13:37:54291 result.text = string_item->string_data;
Avi Drissman4bbdb2002025-04-03 01:43:44292 } else if (str_type == ui::kMimeTypeUriList) {
Henrique Ferreiro0bb413bf2020-09-17 13:37:54293 result.url = GURL(string_item->string_data);
294 if (string_item->title)
295 result.url_title = *string_item->title;
Avi Drissman4bbdb2002025-04-03 01:43:44296 } else if (str_type == ui::kMimeTypeDownloadUrl) {
Henrique Ferreiro0bb413bf2020-09-17 13:37:54297 result.download_metadata = string_item->string_data;
298 result.referrer_policy = drag_data.referrer_policy;
Avi Drissman4bbdb2002025-04-03 01:43:44299 } else if (str_type == ui::kMimeTypeHtml) {
Henrique Ferreiro0bb413bf2020-09-17 13:37:54300 result.html = string_item->string_data;
301 if (string_item->base_url)
302 result.html_base_url = *string_item->base_url;
303 } else {
304 result.custom_data.emplace(
305 base::UTF8ToUTF16(string_item->string_type),
306 string_item->string_data);
307 }
308 break;
309 }
Daniel Cheng07383d492022-04-21 15:02:33310 case blink::mojom::DragItemDataView::Tag::kBinary: {
Henrique Ferreiro0bb413bf2020-09-17 13:37:54311 DCHECK(result.file_contents.empty());
312
313 const blink::mojom::DragItemBinaryPtr& binary_item = item->get_binary();
Peter Kasting5f6928c2024-11-29 21:25:11314 base::span<const uint8_t> contents(binary_item->data);
Henrique Ferreiro35b04f22020-10-05 09:18:39315 result.file_contents.assign(contents.begin(), contents.end());
Joel Hockey3acd3b02021-11-19 21:10:20316 result.file_contents_image_accessible =
317 binary_item->is_image_accessible;
Henrique Ferreiro0bb413bf2020-09-17 13:37:54318 result.file_contents_source_url = binary_item->source_url;
319 result.file_contents_filename_extension =
320 binary_item->filename_extension.value();
321 if (binary_item->content_disposition) {
322 result.file_contents_content_disposition =
323 *binary_item->content_disposition;
324 }
325 break;
326 }
Daniel Cheng07383d492022-04-21 15:02:33327 case blink::mojom::DragItemDataView::Tag::kFile: {
Joel Hockeyd75d5062021-02-23 19:53:25328 const blink::mojom::DataTransferFilePtr& file_item = item->get_file();
Henrique Ferreiro0bb413bf2020-09-17 13:37:54329 // TODO(varunjain): This only works on chromeos. Support win/mac/gtk.
330 result.filenames.emplace_back(file_item->path, file_item->display_name);
331 break;
332 }
Daniel Cheng07383d492022-04-21 15:02:33333 case blink::mojom::DragItemDataView::Tag::kFileSystemFile: {
Henrique Ferreiro0bb413bf2020-09-17 13:37:54334 const blink::mojom::DragItemFileSystemFilePtr& file_system_file_item =
335 item->get_file_system_file();
Henrique Ferreiroa6ea10e52020-10-27 12:21:25336 // This field should be empty when dragging from the renderer.
337 DCHECK(!file_system_file_item->file_system_id);
338
Henrique Ferreiro0bb413bf2020-09-17 13:37:54339 DropData::FileSystemFileInfo info;
340 info.url = file_system_file_item->url;
341 info.size = file_system_file_item->size;
Henrique Ferreiroa6ea10e52020-10-27 12:21:25342 info.filesystem_id = std::string();
Henrique Ferreiro0bb413bf2020-09-17 13:37:54343 result.file_system_files.push_back(std::move(info));
344 break;
345 }
346 }
347 }
348 return result;
349}
350
351} // namespace content