blob: 78f2f64e686629457ae6dd3bb4224bd70c316dfb [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"
Andrew Williams1255f3922022-02-07 07:02:1314#include "base/guid.h"
Henrique Ferreiro0bb413bf2020-09-17 13:37:5415#include "base/strings/utf_string_conversions.h"
Yuta Hijikata035083112020-11-11 07:02:3316#include "build/chromeos_buildflags.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.
Austin Sullivanafefb722021-01-14 01:26:3942content::FileSystemAccessEntryFactory::PathType MaybeRemapPath(
Marijn Kruisselbrinkc1000472020-10-01 22:19:5443 base::FilePath* entry_path) {
Yuta Hijikata035083112020-11-11 07:02:3344#if BUILDFLAG(IS_CHROMEOS_ASH)
Marijn Kruisselbrinkc1000472020-10-01 22:19:5445 base::FilePath virtual_path;
46 auto* external_mount_points =
47 storage::ExternalMountPoints::GetSystemInstance();
48 if (external_mount_points->GetVirtualPath(*entry_path, &virtual_path)) {
49 *entry_path = std::move(virtual_path);
Austin Sullivanafefb722021-01-14 01:26:3950 return content::FileSystemAccessEntryFactory::PathType::kExternal;
Marijn Kruisselbrinkc1000472020-10-01 22:19:5451 }
52#endif
Austin Sullivanafefb722021-01-14 01:26:3953 return content::FileSystemAccessEntryFactory::PathType::kLocal;
Marijn Kruisselbrinkc1000472020-10-01 22:19:5454}
55
56} // namespace
57
Joel Hockeyd75d5062021-02-23 19:53:2558std::vector<blink::mojom::DataTransferFilePtr> FileInfosToDataTransferFiles(
59 const std::vector<ui::FileInfo>& filenames,
60 FileSystemAccessManagerImpl* file_system_access_manager,
61 int child_id) {
62 std::vector<blink::mojom::DataTransferFilePtr> result;
63 for (const ui::FileInfo& file_info : filenames) {
64 blink::mojom::DataTransferFilePtr file =
65 blink::mojom::DataTransferFile::New();
66 file->path = file_info.path;
67 file->display_name = file_info.display_name;
68 mojo::PendingRemote<blink::mojom::FileSystemAccessDataTransferToken>
69 pending_token;
70 base::FilePath entry_path = file_info.path;
71 FileSystemAccessManagerImpl::PathType path_type =
72 MaybeRemapPath(&entry_path);
73 file_system_access_manager->CreateFileSystemAccessDataTransferToken(
74 path_type, entry_path, child_id,
75 pending_token.InitWithNewPipeAndPassReceiver());
76 file->file_system_access_token = std::move(pending_token);
77 result.push_back(std::move(file));
78 }
79 return result;
80}
81
Andrew Williams1255f3922022-02-07 07:02:1382std::vector<blink::mojom::DragItemFileSystemFilePtr>
83FileSystemFileInfosToDragItemFileSystemFilePtr(
84 std::vector<DropData::FileSystemFileInfo> file_system_file_infos,
85 FileSystemAccessManagerImpl* file_system_access_manager,
86 scoped_refptr<content::ChromeBlobStorageContext> context) {
87 std::vector<blink::mojom::DragItemFileSystemFilePtr> result;
88 for (const content::DropData::FileSystemFileInfo& file_system_file :
89 file_system_file_infos) {
90 blink::mojom::DragItemFileSystemFilePtr item =
91 blink::mojom::DragItemFileSystemFile::New();
92 item->url = file_system_file.url;
93 item->size = file_system_file.size;
94 item->file_system_id = file_system_file.filesystem_id;
95
96 storage::FileSystemURL file_system_url =
97 file_system_access_manager->context()->CrackURLInFirstPartyContext(
98 file_system_file.url);
99 DCHECK(file_system_url.type() != storage::kFileSystemTypePersistent);
100 DCHECK(file_system_url.type() != storage::kFileSystemTypeTemporary);
101
102 std::string uuid = base::GenerateGUID();
103
104 std::string content_type;
105
106 base::FilePath::StringType extension = file_system_url.path().Extension();
107 if (!extension.empty()) {
108 std::string mime_type;
109 // TODO(https://crbug.com/155455): Historically for blobs created from
110 // file system URLs we've only considered well known content types to
111 // avoid leaking the presence of locally installed applications when
112 // creating blobs from files in the sandboxed file system. However, since
113 // this code path should only deal with real/"trusted" paths, we could
114 // consider taking platform defined mime type mappings into account here
115 // as well. Note that the approach used here must not block or else it
116 // can't be called from the UI thread (for example, calls to
117 // GetMimeTypeFromExtension can block).
118 if (net::GetWellKnownMimeTypeFromExtension(extension.substr(1),
119 &mime_type))
120 content_type = std::move(mime_type);
121 }
122 // TODO(https://crbug.com/962306): Consider some kind of fallback type when
123 // the above mime type detection fails.
124
125 mojo::PendingRemote<blink::mojom::Blob> blob_remote;
126 mojo::PendingReceiver<blink::mojom::Blob> blob_receiver =
127 blob_remote.InitWithNewPipeAndPassReceiver();
128
129 item->serialized_blob = blink::mojom::SerializedBlob::New(
130 uuid, content_type, item->size, std::move(blob_remote));
131
132 GetIOThreadTaskRunner({})->PostTask(
133 FROM_HERE,
134 base::BindOnce(
135 &ChromeBlobStorageContext::CreateFileSystemBlob, context,
136 base::WrapRefCounted(file_system_access_manager->context()),
137 std::move(blob_receiver), std::move(file_system_url),
138 std::move(uuid), std::move(content_type), item->size,
139 base::Time()));
140
141 result.push_back(std::move(item));
142 }
143 return result;
144}
145
Henrique Ferreiro0bb413bf2020-09-17 13:37:54146blink::mojom::DragDataPtr DropDataToDragData(
147 const DropData& drop_data,
Austin Sullivan559bb382021-01-26 00:52:43148 FileSystemAccessManagerImpl* file_system_access_manager,
Andrew Williams1255f3922022-02-07 07:02:13149 int child_id,
150 scoped_refptr<ChromeBlobStorageContext> chrome_blob_storage_context) {
Henrique Ferreiro0bb413bf2020-09-17 13:37:54151 // These fields are currently unused when dragging into Blink.
152 DCHECK(drop_data.download_metadata.empty());
Henrique Ferreiro0bb413bf2020-09-17 13:37:54153 DCHECK(drop_data.file_contents_content_disposition.empty());
154
155 std::vector<blink::mojom::DragItemPtr> items;
156 if (drop_data.text) {
157 blink::mojom::DragItemStringPtr item = blink::mojom::DragItemString::New();
158 item->string_type = ui::kMimeTypeText;
159 item->string_data = *drop_data.text;
160 items.push_back(blink::mojom::DragItem::NewString(std::move(item)));
161 }
162 if (!drop_data.url.is_empty()) {
163 blink::mojom::DragItemStringPtr item = blink::mojom::DragItemString::New();
164 item->string_type = ui::kMimeTypeURIList;
165 item->string_data = base::UTF8ToUTF16(drop_data.url.spec());
166 item->title = drop_data.url_title;
167 items.push_back(blink::mojom::DragItem::NewString(std::move(item)));
168 }
169 if (drop_data.html) {
170 blink::mojom::DragItemStringPtr item = blink::mojom::DragItemString::New();
171 item->string_type = ui::kMimeTypeHTML;
172 item->string_data = *drop_data.html;
173 item->base_url = drop_data.html_base_url;
174 items.push_back(blink::mojom::DragItem::NewString(std::move(item)));
175 }
Joel Hockeyd75d5062021-02-23 19:53:25176 std::vector<blink::mojom::DataTransferFilePtr> files =
177 FileInfosToDataTransferFiles(drop_data.filenames,
178 file_system_access_manager, child_id);
Andrew Williams1255f3922022-02-07 07:02:13179 for (auto& file : files) {
180 items.push_back(blink::mojom::DragItem::NewFile(std::move(file)));
Henrique Ferreiro0bb413bf2020-09-17 13:37:54181 }
Andrew Williams1255f3922022-02-07 07:02:13182
183 std::vector<blink::mojom::DragItemFileSystemFilePtr> file_system_files =
184 FileSystemFileInfosToDragItemFileSystemFilePtr(
185 drop_data.file_system_files, file_system_access_manager,
186 std::move(chrome_blob_storage_context));
187 for (auto& file_system_file : file_system_files) {
188 items.push_back(
189 blink::mojom::DragItem::NewFileSystemFile(std::move(file_system_file)));
Henrique Ferreiro0bb413bf2020-09-17 13:37:54190 }
Joel Hockeya81a94d2021-09-15 04:12:27191 if (drop_data.file_contents_source_url.is_valid()) {
192 blink::mojom::DragItemBinaryPtr item = blink::mojom::DragItemBinary::New();
193 item->data = mojo_base::BigBuffer(
194 base::as_bytes(base::make_span(drop_data.file_contents)));
Joel Hockey3acd3b02021-11-19 21:10:20195 item->is_image_accessible = drop_data.file_contents_image_accessible;
Joel Hockeya81a94d2021-09-15 04:12:27196 item->source_url = drop_data.file_contents_source_url;
197 item->filename_extension =
198 base::FilePath(drop_data.file_contents_filename_extension);
199 items.push_back(blink::mojom::DragItem::NewBinary(std::move(item)));
200 }
201 for (const std::pair<const std::u16string, std::u16string>& data :
Henrique Ferreiro0bb413bf2020-09-17 13:37:54202 drop_data.custom_data) {
203 blink::mojom::DragItemStringPtr item = blink::mojom::DragItemString::New();
204 item->string_type = base::UTF16ToUTF8(data.first);
205 item->string_data = data.second;
206 items.push_back(blink::mojom::DragItem::NewString(std::move(item)));
207 }
208
Henrique Ferreiroa6ea10e52020-10-27 12:21:25209 return blink::mojom::DragData::New(
210 std::move(items),
211 // While this shouldn't be a problem in production code, as the
212 // real file_system_id should never be empty if used in browser to
213 // renderer messages, some tests use this function to test renderer to
214 // browser messages, in which case the field is unused and this will hit
215 // a DCHECK.
216 drop_data.filesystem_id.empty()
Anton Bikineevf62d1bf2021-05-15 17:56:07217 ? absl::nullopt
218 : absl::optional<std::string>(
Henrique Ferreiroa6ea10e52020-10-27 12:21:25219 base::UTF16ToUTF8(drop_data.filesystem_id)),
220 drop_data.referrer_policy);
Henrique Ferreiro0bb413bf2020-09-17 13:37:54221}
222
Dave Tapuskad2a55202020-10-27 17:08:11223blink::mojom::DragDataPtr DropMetaDataToDragData(
224 const std::vector<DropData::Metadata>& drop_meta_data) {
225 std::vector<blink::mojom::DragItemPtr> items;
226
227 for (const auto& meta_data_item : drop_meta_data) {
228 if (meta_data_item.kind == DropData::Kind::STRING) {
229 blink::mojom::DragItemStringPtr item =
230 blink::mojom::DragItemString::New();
231 item->string_type = base::UTF16ToUTF8(meta_data_item.mime_type);
232 // Have to pass a dummy URL here instead of an empty URL because the
233 // DropData received by browser_plugins goes through a round trip:
234 // DropData::MetaData --> WebDragData-->DropData. In the end, DropData
235 // will contain an empty URL (which means no URL is dragged) if the URL in
236 // WebDragData is empty.
237 if (base::EqualsASCII(meta_data_item.mime_type, ui::kMimeTypeURIList)) {
Jan Wilken Dörrie2c470ea2021-03-22 22:26:24238 item->string_data = u"about:dragdrop-placeholder";
Dave Tapuskad2a55202020-10-27 17:08:11239 }
240 items.push_back(blink::mojom::DragItem::NewString(std::move(item)));
241 continue;
242 }
243
244 // TODO(hush): crbug.com/584789. Blink needs to support creating a file with
245 // just the mimetype. This is needed to drag files to WebView on Android
246 // platform.
247 if ((meta_data_item.kind == DropData::Kind::FILENAME) &&
248 !meta_data_item.filename.empty()) {
Joel Hockeyd75d5062021-02-23 19:53:25249 blink::mojom::DataTransferFilePtr item =
250 blink::mojom::DataTransferFile::New();
Dave Tapuskad2a55202020-10-27 17:08:11251 item->path = meta_data_item.filename;
252 items.push_back(blink::mojom::DragItem::NewFile(std::move(item)));
253 continue;
254 }
255
256 if (meta_data_item.kind == DropData::Kind::FILESYSTEMFILE) {
257 blink::mojom::DragItemFileSystemFilePtr item =
258 blink::mojom::DragItemFileSystemFile::New();
259 item->url = meta_data_item.file_system_url;
260 items.push_back(
261 blink::mojom::DragItem::NewFileSystemFile(std::move(item)));
262 continue;
263 }
Joel Hockeya81a94d2021-09-15 04:12:27264
265 if (meta_data_item.kind == DropData::Kind::BINARY) {
266 blink::mojom::DragItemBinaryPtr item =
267 blink::mojom::DragItemBinary::New();
268 item->source_url = meta_data_item.file_contents_url;
269 items.push_back(blink::mojom::DragItem::NewBinary(std::move(item)));
270 continue;
271 }
Dave Tapuskad2a55202020-10-27 17:08:11272 }
Anton Bikineevf62d1bf2021-05-15 17:56:07273 return blink::mojom::DragData::New(std::move(items), absl::nullopt,
Dave Tapuskad2a55202020-10-27 17:08:11274 network::mojom::ReferrerPolicy::kDefault);
275}
276
Henrique Ferreiro0bb413bf2020-09-17 13:37:54277DropData DragDataToDropData(const blink::mojom::DragData& drag_data) {
Henrique Ferreiroa6ea10e52020-10-27 12:21:25278 // This field should be empty when dragging from the renderer.
279 DCHECK(!drag_data.file_system_id);
Henrique Ferreiro0bb413bf2020-09-17 13:37:54280
Henrique Ferreiroa6ea10e52020-10-27 12:21:25281 DropData result;
Henrique Ferreiro0bb413bf2020-09-17 13:37:54282 for (const blink::mojom::DragItemPtr& item : drag_data.items) {
283 switch (item->which()) {
Daniel Cheng07383d492022-04-21 15:02:33284 case blink::mojom::DragItemDataView::Tag::kString: {
Henrique Ferreiro0bb413bf2020-09-17 13:37:54285 const blink::mojom::DragItemStringPtr& string_item = item->get_string();
286 std::string str_type = string_item->string_type;
287 if (str_type == ui::kMimeTypeText) {
288 result.text = string_item->string_data;
289 } else if (str_type == ui::kMimeTypeURIList) {
290 result.url = GURL(string_item->string_data);
291 if (string_item->title)
292 result.url_title = *string_item->title;
293 } else if (str_type == ui::kMimeTypeDownloadURL) {
294 result.download_metadata = string_item->string_data;
295 result.referrer_policy = drag_data.referrer_policy;
296 } else if (str_type == ui::kMimeTypeHTML) {
297 result.html = string_item->string_data;
298 if (string_item->base_url)
299 result.html_base_url = *string_item->base_url;
300 } else {
301 result.custom_data.emplace(
302 base::UTF8ToUTF16(string_item->string_type),
303 string_item->string_data);
304 }
305 break;
306 }
Daniel Cheng07383d492022-04-21 15:02:33307 case blink::mojom::DragItemDataView::Tag::kBinary: {
Henrique Ferreiro0bb413bf2020-09-17 13:37:54308 DCHECK(result.file_contents.empty());
309
310 const blink::mojom::DragItemBinaryPtr& binary_item = item->get_binary();
Ken Rockotc73e50a72020-10-27 00:32:23311 base::span<const uint8_t> contents = base::make_span(binary_item->data);
Henrique Ferreiro35b04f22020-10-05 09:18:39312 result.file_contents.assign(contents.begin(), contents.end());
Joel Hockey3acd3b02021-11-19 21:10:20313 result.file_contents_image_accessible =
314 binary_item->is_image_accessible;
Henrique Ferreiro0bb413bf2020-09-17 13:37:54315 result.file_contents_source_url = binary_item->source_url;
316 result.file_contents_filename_extension =
317 binary_item->filename_extension.value();
318 if (binary_item->content_disposition) {
319 result.file_contents_content_disposition =
320 *binary_item->content_disposition;
321 }
322 break;
323 }
Daniel Cheng07383d492022-04-21 15:02:33324 case blink::mojom::DragItemDataView::Tag::kFile: {
Joel Hockeyd75d5062021-02-23 19:53:25325 const blink::mojom::DataTransferFilePtr& file_item = item->get_file();
Henrique Ferreiro0bb413bf2020-09-17 13:37:54326 // TODO(varunjain): This only works on chromeos. Support win/mac/gtk.
327 result.filenames.emplace_back(file_item->path, file_item->display_name);
328 break;
329 }
Daniel Cheng07383d492022-04-21 15:02:33330 case blink::mojom::DragItemDataView::Tag::kFileSystemFile: {
Henrique Ferreiro0bb413bf2020-09-17 13:37:54331 const blink::mojom::DragItemFileSystemFilePtr& file_system_file_item =
332 item->get_file_system_file();
Henrique Ferreiroa6ea10e52020-10-27 12:21:25333 // This field should be empty when dragging from the renderer.
334 DCHECK(!file_system_file_item->file_system_id);
335
Henrique Ferreiro0bb413bf2020-09-17 13:37:54336 DropData::FileSystemFileInfo info;
337 info.url = file_system_file_item->url;
338 info.size = file_system_file_item->size;
Henrique Ferreiroa6ea10e52020-10-27 12:21:25339 info.filesystem_id = std::string();
Henrique Ferreiro0bb413bf2020-09-17 13:37:54340 result.file_system_files.push_back(std::move(info));
341 break;
342 }
343 }
344 }
345 return result;
346}
347
348} // namespace content