| // Copyright 2020 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/renderer_host/drop_data_util.h" |
| |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/check.h" |
| #include "base/containers/span.h" |
| #include "base/files/file_path.h" |
| #include "base/optional.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "build/chromeos_buildflags.h" |
| #include "content/browser/file_system_access/native_file_system_manager_impl.h" |
| #include "mojo/public/cpp/bindings/pending_remote.h" |
| #include "services/network/public/mojom/referrer_policy.mojom-shared.h" |
| #include "storage/browser/file_system/external_mount_points.h" |
| #include "third_party/blink/public/mojom/file_system_access/file_system_access_drag_drop_token.mojom.h" |
| #include "third_party/blink/public/mojom/page/drag.mojom.h" |
| #include "ui/base/clipboard/clipboard_constants.h" |
| #include "ui/base/dragdrop/file_info/file_info.h" |
| #include "url/gurl.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| // On Chrome OS paths that exist on an external mount point need to be treated |
| // differently to make sure the native file system code accesses these paths via |
| // the correct file system backend. This method checks if this is the case, and |
| // updates `entry_path` to the path that should be used by the native file |
| // system implementation. |
| content::NativeFileSystemEntryFactory::PathType MaybeRemapPath( |
| base::FilePath* entry_path) { |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| base::FilePath virtual_path; |
| auto* external_mount_points = |
| storage::ExternalMountPoints::GetSystemInstance(); |
| if (external_mount_points->GetVirtualPath(*entry_path, &virtual_path)) { |
| *entry_path = std::move(virtual_path); |
| return content::NativeFileSystemEntryFactory::PathType::kExternal; |
| } |
| #endif |
| return content::NativeFileSystemEntryFactory::PathType::kLocal; |
| } |
| |
| } // namespace |
| |
| blink::mojom::DragDataPtr DropDataToDragData( |
| const DropData& drop_data, |
| NativeFileSystemManagerImpl* native_file_system_manager, |
| int child_id) { |
| // These fields are currently unused when dragging into Blink. |
| DCHECK(drop_data.download_metadata.empty()); |
| DCHECK(drop_data.file_contents.empty()); |
| DCHECK(drop_data.file_contents_content_disposition.empty()); |
| |
| std::vector<blink::mojom::DragItemPtr> items; |
| if (drop_data.text) { |
| blink::mojom::DragItemStringPtr item = blink::mojom::DragItemString::New(); |
| item->string_type = ui::kMimeTypeText; |
| item->string_data = *drop_data.text; |
| items.push_back(blink::mojom::DragItem::NewString(std::move(item))); |
| } |
| if (!drop_data.url.is_empty()) { |
| blink::mojom::DragItemStringPtr item = blink::mojom::DragItemString::New(); |
| item->string_type = ui::kMimeTypeURIList; |
| item->string_data = base::UTF8ToUTF16(drop_data.url.spec()); |
| item->title = drop_data.url_title; |
| items.push_back(blink::mojom::DragItem::NewString(std::move(item))); |
| } |
| if (drop_data.html) { |
| blink::mojom::DragItemStringPtr item = blink::mojom::DragItemString::New(); |
| item->string_type = ui::kMimeTypeHTML; |
| item->string_data = *drop_data.html; |
| item->base_url = drop_data.html_base_url; |
| items.push_back(blink::mojom::DragItem::NewString(std::move(item))); |
| } |
| for (const ui::FileInfo& file : drop_data.filenames) { |
| blink::mojom::DragItemFilePtr item = blink::mojom::DragItemFile::New(); |
| item->path = file.path; |
| item->display_name = file.display_name; |
| mojo::PendingRemote<blink::mojom::FileSystemAccessDragDropToken> |
| pending_token; |
| base::FilePath entry_path = file.path; |
| NativeFileSystemManagerImpl::PathType path_type = |
| MaybeRemapPath(&entry_path); |
| native_file_system_manager->CreateNativeFileSystemDragDropToken( |
| path_type, entry_path, child_id, |
| pending_token.InitWithNewPipeAndPassReceiver()); |
| item->file_system_access_token = std::move(pending_token); |
| |
| items.push_back(blink::mojom::DragItem::NewFile(std::move(item))); |
| } |
| for (const content::DropData::FileSystemFileInfo& file_system_file : |
| drop_data.file_system_files) { |
| blink::mojom::DragItemFileSystemFilePtr item = |
| blink::mojom::DragItemFileSystemFile::New(); |
| item->url = file_system_file.url; |
| item->size = file_system_file.size; |
| item->file_system_id = file_system_file.filesystem_id; |
| items.push_back(blink::mojom::DragItem::NewFileSystemFile(std::move(item))); |
| } |
| for (const std::pair<base::string16, base::string16> data : |
| drop_data.custom_data) { |
| blink::mojom::DragItemStringPtr item = blink::mojom::DragItemString::New(); |
| item->string_type = base::UTF16ToUTF8(data.first); |
| item->string_data = data.second; |
| items.push_back(blink::mojom::DragItem::NewString(std::move(item))); |
| } |
| |
| return blink::mojom::DragData::New( |
| std::move(items), |
| // While this shouldn't be a problem in production code, as the |
| // real file_system_id should never be empty if used in browser to |
| // renderer messages, some tests use this function to test renderer to |
| // browser messages, in which case the field is unused and this will hit |
| // a DCHECK. |
| drop_data.filesystem_id.empty() |
| ? base::nullopt |
| : base::Optional<std::string>( |
| base::UTF16ToUTF8(drop_data.filesystem_id)), |
| drop_data.referrer_policy); |
| } |
| |
| blink::mojom::DragDataPtr DropMetaDataToDragData( |
| const std::vector<DropData::Metadata>& drop_meta_data) { |
| std::vector<blink::mojom::DragItemPtr> items; |
| |
| for (const auto& meta_data_item : drop_meta_data) { |
| if (meta_data_item.kind == DropData::Kind::STRING) { |
| blink::mojom::DragItemStringPtr item = |
| blink::mojom::DragItemString::New(); |
| item->string_type = base::UTF16ToUTF8(meta_data_item.mime_type); |
| // Have to pass a dummy URL here instead of an empty URL because the |
| // DropData received by browser_plugins goes through a round trip: |
| // DropData::MetaData --> WebDragData-->DropData. In the end, DropData |
| // will contain an empty URL (which means no URL is dragged) if the URL in |
| // WebDragData is empty. |
| if (base::EqualsASCII(meta_data_item.mime_type, ui::kMimeTypeURIList)) { |
| item->string_data = base::UTF8ToUTF16("about:dragdrop-placeholder"); |
| } |
| items.push_back(blink::mojom::DragItem::NewString(std::move(item))); |
| continue; |
| } |
| |
| // TODO(hush): crbug.com/584789. Blink needs to support creating a file with |
| // just the mimetype. This is needed to drag files to WebView on Android |
| // platform. |
| if ((meta_data_item.kind == DropData::Kind::FILENAME) && |
| !meta_data_item.filename.empty()) { |
| blink::mojom::DragItemFilePtr item = blink::mojom::DragItemFile::New(); |
| item->path = meta_data_item.filename; |
| items.push_back(blink::mojom::DragItem::NewFile(std::move(item))); |
| continue; |
| } |
| |
| if (meta_data_item.kind == DropData::Kind::FILESYSTEMFILE) { |
| blink::mojom::DragItemFileSystemFilePtr item = |
| blink::mojom::DragItemFileSystemFile::New(); |
| item->url = meta_data_item.file_system_url; |
| items.push_back( |
| blink::mojom::DragItem::NewFileSystemFile(std::move(item))); |
| continue; |
| } |
| } |
| return blink::mojom::DragData::New(std::move(items), base::nullopt, |
| network::mojom::ReferrerPolicy::kDefault); |
| } |
| |
| DropData DragDataToDropData(const blink::mojom::DragData& drag_data) { |
| // This field should be empty when dragging from the renderer. |
| DCHECK(!drag_data.file_system_id); |
| |
| DropData result; |
| for (const blink::mojom::DragItemPtr& item : drag_data.items) { |
| switch (item->which()) { |
| case blink::mojom::DragItemDataView::Tag::STRING: { |
| const blink::mojom::DragItemStringPtr& string_item = item->get_string(); |
| std::string str_type = string_item->string_type; |
| if (str_type == ui::kMimeTypeText) { |
| result.text = string_item->string_data; |
| } else if (str_type == ui::kMimeTypeURIList) { |
| result.url = GURL(string_item->string_data); |
| if (string_item->title) |
| result.url_title = *string_item->title; |
| } else if (str_type == ui::kMimeTypeDownloadURL) { |
| result.download_metadata = string_item->string_data; |
| result.referrer_policy = drag_data.referrer_policy; |
| } else if (str_type == ui::kMimeTypeHTML) { |
| result.html = string_item->string_data; |
| if (string_item->base_url) |
| result.html_base_url = *string_item->base_url; |
| } else { |
| result.custom_data.emplace( |
| base::UTF8ToUTF16(string_item->string_type), |
| string_item->string_data); |
| } |
| break; |
| } |
| case blink::mojom::DragItemDataView::Tag::BINARY: { |
| DCHECK(result.file_contents.empty()); |
| |
| const blink::mojom::DragItemBinaryPtr& binary_item = item->get_binary(); |
| base::span<const uint8_t> contents = base::make_span(binary_item->data); |
| result.file_contents.assign(contents.begin(), contents.end()); |
| result.file_contents_source_url = binary_item->source_url; |
| result.file_contents_filename_extension = |
| binary_item->filename_extension.value(); |
| if (binary_item->content_disposition) { |
| result.file_contents_content_disposition = |
| *binary_item->content_disposition; |
| } |
| break; |
| } |
| case blink::mojom::DragItemDataView::Tag::FILE: { |
| const blink::mojom::DragItemFilePtr& file_item = item->get_file(); |
| // TODO(varunjain): This only works on chromeos. Support win/mac/gtk. |
| result.filenames.emplace_back(file_item->path, file_item->display_name); |
| break; |
| } |
| case blink::mojom::DragItemDataView::Tag::FILE_SYSTEM_FILE: { |
| const blink::mojom::DragItemFileSystemFilePtr& file_system_file_item = |
| item->get_file_system_file(); |
| // This field should be empty when dragging from the renderer. |
| DCHECK(!file_system_file_item->file_system_id); |
| |
| DropData::FileSystemFileInfo info; |
| info.url = file_system_file_item->url; |
| info.size = file_system_file_item->size; |
| info.filesystem_id = std::string(); |
| result.file_system_files.push_back(std::move(info)); |
| break; |
| } |
| } |
| } |
| return result; |
| } |
| |
| } // namespace content |