Make DataOffer::SetDropData handle filesystem:// URLs.
Below are the steps for converting Chrome-specific filesystem:// URLs into ARC URLs.
1) Extract filesystem URL(s) from pickle.
-> "filesystem:chrome-extension://hhao.../external/drive-.../root/...."
2) Crack URL with FileSystemContext.
-> "/special/drive-.../root/...."
3) Convert to Arc URL
-> "content://org.chromium.arc.chromecontentprovider/externalfile%3Adrive-.../root/...."
Bug: chromium:767982
Test: out/Default/exo_unittests --gtest_filter="DataOffer.*"
Change-Id: I9fc803f69acd0d3573c25668ca5786d7e6002501
Reviewed-on: https://chromium-review.googlesource.com/867807
Commit-Queue: Satoshi Niwa <[email protected]>
Reviewed-by: David Reveman <[email protected]>
Reviewed-by: Ryo Hashimoto <[email protected]>
Cr-Commit-Position: refs/heads/master@{#532756}
diff --git a/chrome/browser/exo_parts.cc b/chrome/browser/exo_parts.cc
index 64528ee..6383a0c 100644
--- a/chrome/browser/exo_parts.cc
+++ b/chrome/browser/exo_parts.cc
@@ -14,20 +14,44 @@
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
+#include "chrome/browser/chromeos/file_manager/app_id.h"
+#include "chrome/browser/chromeos/file_manager/fileapi_util.h"
#include "chrome/browser/chromeos/file_manager/path_util.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/ui/ash/ash_util.h"
#include "chrome/common/chrome_switches.h"
#include "components/exo/display.h"
#include "components/exo/file_helper.h"
#include "components/exo/wayland/server.h"
#include "components/exo/wm_helper.h"
+#include "components/user_manager/user_manager.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/common/drop_data.h"
+#include "storage/browser/fileapi/file_system_context.h"
+#include "storage/browser/fileapi/file_system_url.h"
#include "ui/arc/notification/arc_notification_surface_manager_impl.h"
namespace {
constexpr char kMimeTypeArcUriList[] = "application/x-arc-uri-list";
+storage::FileSystemContext* GetFileSystemContext() {
+ // Obtains the primary profile.
+ if (!user_manager::UserManager::IsInitialized())
+ return nullptr;
+ const user_manager::User* primary_user =
+ user_manager::UserManager::Get()->GetPrimaryUser();
+ if (!primary_user)
+ return nullptr;
+ Profile* primary_profile =
+ chromeos::ProfileHelper::Get()->GetProfileByUser(primary_user);
+ if (!primary_profile)
+ return nullptr;
+
+ return file_manager::util::GetFileSystemContextForExtensionId(
+ primary_profile, file_manager::kFileManagerAppId);
+}
+
class ChromeFileHelper : public exo::FileHelper {
public:
ChromeFileHelper() {}
@@ -42,10 +66,30 @@
GURL* out) override {
return file_manager::util::ConvertPathToArcUrl(path, out);
}
- bool GetUrlFromFileSystemUrl(const std::string& app_id,
- const GURL& url,
- GURL* out) override {
- return false;
+ bool GetUrlsFromPickle(const std::string& app_id,
+ const base::Pickle& pickle,
+ std::vector<GURL>* out_urls) override {
+ storage::FileSystemContext* file_system_context = GetFileSystemContext();
+ if (!file_system_context)
+ return false;
+
+ std::vector<content::DropData::FileSystemFileInfo> file_system_files;
+ if (!content::DropData::FileSystemFileInfo::ReadFileSystemFilesFromPickle(
+ pickle, &file_system_files))
+ return false;
+
+ for (const auto& file_system_file : file_system_files) {
+ const storage::FileSystemURL file_system_url =
+ file_system_context->CrackURL(file_system_file.url);
+ GURL out_url;
+ // TODO(niwa): Check that app_id is an Arc app once the caller
+ // (exo::DataOffer) starts filling app_id.
+ if (file_manager::util::ConvertPathToArcUrl(file_system_url.path(),
+ &out_url)) {
+ out_urls->push_back(out_url);
+ }
+ }
+ return !out_urls->empty();
}
};
diff --git a/components/exo/data_device_unittest.cc b/components/exo/data_device_unittest.cc
index e721cfd..dd77d1c 100644
--- a/components/exo/data_device_unittest.cc
+++ b/components/exo/data_device_unittest.cc
@@ -114,9 +114,9 @@
GURL* out) override {
return true;
}
- bool GetUrlFromFileSystemUrl(const std::string& app_id,
- const GURL& url,
- GURL* out) override {
+ bool GetUrlsFromPickle(const std::string& app_id,
+ const base::Pickle& pickle,
+ std::vector<GURL>* out_urls) override {
return false;
}
diff --git a/components/exo/data_offer.cc b/components/exo/data_offer.cc
index 13fca50..c0239ae 100644
--- a/components/exo/data_offer.cc
+++ b/components/exo/data_offer.cc
@@ -6,6 +6,7 @@
#include "base/files/file_util.h"
#include "base/memory/ref_counted_memory.h"
+#include "base/pickle.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task_scheduler/post_task.h"
#include "components/exo/data_offer_delegate.h"
@@ -52,6 +53,49 @@
DLOG(ERROR) << "Failed to write drop data";
}
+// Gets a comma-separated list of urls extracted from |data|->file.
+bool GetUrlListFromDataFile(FileHelper* file_helper,
+ const ui::OSExchangeData& data,
+ base::string16* url_list_string) {
+ if (!data.HasFile())
+ return false;
+ std::vector<ui::FileInfo> files;
+ if (data.GetFilenames(&files)) {
+ for (const auto& info : files) {
+ GURL url;
+ // TODO(niwa): Need to fill the correct app_id.
+ if (file_helper->GetUrlFromPath(/* app_id */ "", info.path, &url)) {
+ if (!url_list_string->empty())
+ *url_list_string += base::UTF8ToUTF16(kUriListSeparator);
+ *url_list_string += base::UTF8ToUTF16(url.spec());
+ }
+ }
+ }
+ return !url_list_string->empty();
+}
+
+// Gets a comma-separated list of urls extracted from |data|->pickle.
+bool GetUrlListFromDataPickle(FileHelper* file_helper,
+ const ui::OSExchangeData& data,
+ base::string16* url_list_string) {
+ static const char kFormatString[] = "chromium/x-file-system-files";
+ CR_DEFINE_STATIC_LOCAL(ui::Clipboard::FormatType, formatType,
+ (ui::Clipboard::GetFormatType(kFormatString)));
+ base::Pickle pickle;
+ if (!data.GetPickledData(formatType, &pickle))
+ return false;
+ std::vector<GURL> app_urls;
+ // TODO(niwa): Need to fill the correct app_id.
+ if (file_helper->GetUrlsFromPickle(/* app_id */ "", pickle, &app_urls)) {
+ for (const GURL& app_url : app_urls) {
+ if (!url_list_string->empty())
+ *url_list_string += base::UTF8ToUTF16(kUriListSeparator);
+ *url_list_string += base::UTF8ToUTF16(app_url.spec());
+ }
+ }
+ return !url_list_string->empty();
+}
+
} // namespace
DataOffer::DataOffer(DataOfferDelegate* delegate) : delegate_(delegate) {}
@@ -102,30 +146,21 @@
void DataOffer::SetDropData(FileHelper* file_helper,
const ui::OSExchangeData& data) {
DCHECK_EQ(0u, data_.size());
- if (data.HasString()) {
+
+ base::string16 url_list_string;
+ bool found_urls =
+ GetUrlListFromDataFile(file_helper, data, &url_list_string) ||
+ GetUrlListFromDataPickle(file_helper, data, &url_list_string);
+ if (found_urls) {
+ data_.emplace(file_helper->GetMimeTypeForUriList(),
+ RefCountedString16::TakeString(std::move(url_list_string)));
+ } else if (data.HasString()) {
base::string16 string_content;
if (data.GetString(&string_content)) {
data_.emplace(std::string(ui::Clipboard::kMimeTypeText),
RefCountedString16::TakeString(std::move(string_content)));
}
}
- if (data.HasFile()) {
- std::vector<ui::FileInfo> files;
- if (data.GetFilenames(&files)) {
- base::string16 url_list;
- for (const auto& info : files) {
- GURL url;
- // TODO(hirono): Need to fill the corret app_id.
- if (file_helper->GetUrlFromPath(/* app_id */ "", info.path, &url)) {
- if (!url_list.empty())
- url_list += base::UTF8ToUTF16(kUriListSeparator);
- url_list += base::UTF8ToUTF16(url.spec());
- }
- }
- data_.emplace(file_helper->GetMimeTypeForUriList(),
- RefCountedString16::TakeString(std::move(url_list)));
- }
- }
for (const auto& pair : data_) {
delegate_->OnOffer(pair.first);
}
diff --git a/components/exo/data_offer_unittest.cc b/components/exo/data_offer_unittest.cc
index 0c8976ec..59508d3 100644
--- a/components/exo/data_offer_unittest.cc
+++ b/components/exo/data_offer_unittest.cc
@@ -77,10 +77,13 @@
*out = GURL("file://" + path.AsUTF8Unsafe());
return true;
}
- bool GetUrlFromFileSystemUrl(const std::string& app_id,
- const GURL& url,
- GURL* out) override {
- return false;
+ bool GetUrlsFromPickle(const std::string& app_id,
+ const base::Pickle& pickle,
+ std::vector<GURL>* out_urls) override {
+ // TODO(niwa): Check app_id once we start filling app_id in DataOffer.
+ out_urls->push_back(
+ GURL("content://org.chromium.arc.chromecontentprovider/path/to/file1"));
+ return true;
}
private:
@@ -165,6 +168,26 @@
EXPECT_EQ("text/uri-list", delegate.mime_types()[0]);
}
+TEST_F(DataOfferTest, SetPickleDropData) {
+ TestDataOfferDelegate delegate;
+ DataOffer data_offer(&delegate);
+
+ TestFileHelper file_helper;
+ ui::OSExchangeData data;
+
+ base::Pickle pickle;
+ pickle.WriteUInt32(1); // num files
+ pickle.WriteString("filesystem:chrome-extension://path/to/file1");
+ pickle.WriteInt64(1000); // file size
+ pickle.WriteString("id"); // filesystem id
+ data.SetPickledData(
+ ui::Clipboard::GetFormatType("chromium/x-file-system-files"), pickle);
+ data_offer.SetDropData(&file_helper, data);
+
+ EXPECT_EQ(1u, delegate.mime_types().size());
+ EXPECT_EQ("text/uri-list", delegate.mime_types()[0]);
+}
+
TEST_F(DataOfferTest, ReceiveString) {
TestDataOfferDelegate delegate;
DataOffer data_offer(&delegate);
@@ -203,6 +226,35 @@
EXPECT_EQ(base::ASCIIToUTF16("file:///test/downloads/file"), result);
}
+TEST_F(DataOfferTest, ReceiveUriListFromPickle) {
+ TestDataOfferDelegate delegate;
+ DataOffer data_offer(&delegate);
+
+ TestFileHelper file_helper;
+ ui::OSExchangeData data;
+
+ base::Pickle pickle;
+ pickle.WriteUInt32(1); // num files
+ pickle.WriteString("filesystem:chrome-extension://path/to/file1");
+ pickle.WriteInt64(1000); // file size
+ pickle.WriteString("id"); // filesystem id
+ data.SetPickledData(
+ ui::Clipboard::GetFormatType("chromium/x-file-system-files"), pickle);
+ data_offer.SetDropData(&file_helper, data);
+
+ base::ScopedFD read_pipe;
+ base::ScopedFD write_pipe;
+ CreatePipe(&read_pipe, &write_pipe);
+
+ data_offer.Receive("text/uri-list", std::move(write_pipe));
+ base::string16 result;
+ ASSERT_TRUE(ReadString16(std::move(read_pipe), &result));
+ EXPECT_EQ(
+ base::ASCIIToUTF16(
+ "content://org.chromium.arc.chromecontentprovider/path/to/file1"),
+ result);
+}
+
TEST_F(DataOfferTest, SetClipboardData) {
TestDataOfferDelegate delegate;
DataOffer data_offer(&delegate);
diff --git a/components/exo/display_unittest.cc b/components/exo/display_unittest.cc
index cf56b55..bd0836d 100644
--- a/components/exo/display_unittest.cc
+++ b/components/exo/display_unittest.cc
@@ -232,9 +232,9 @@
GURL* out) override {
return true;
}
- bool GetUrlFromFileSystemUrl(const std::string& app_id,
- const GURL& url,
- GURL* out) override {
+ bool GetUrlsFromPickle(const std::string& app_id,
+ const base::Pickle& pickle,
+ std::vector<GURL>* out_urls) override {
return false;
}
};
diff --git a/components/exo/file_helper.h b/components/exo/file_helper.h
index 28702db..b1dfb5ba 100644
--- a/components/exo/file_helper.h
+++ b/components/exo/file_helper.h
@@ -33,11 +33,12 @@
const base::FilePath& path,
GURL* out) = 0;
- // Converts filesystem:// URL to something that applications can understand.
- // e.g. content:// URI for Android apps.
- virtual bool GetUrlFromFileSystemUrl(const std::string& app_id,
- const GURL& file_system_url,
- GURL* out) = 0;
+ // Takes in |pickle| constructed by the web contents view, reads filesystem
+ // URLs from it and converts the URLs to something that applications can
+ // understand. e.g. content:// URI for Android apps.
+ virtual bool GetUrlsFromPickle(const std::string& app_id,
+ const base::Pickle& pickle,
+ std::vector<GURL>* out_urls) = 0;
};
} // namespace exo