blob: 823b0d58a3c2175f8b8039429ab0d6a3b57d42b6 [file] [log] [blame]
asanka655d1112015-03-07 05:33:411// Copyright 2015 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/platform_util.h"
6
dcheng4af48582016-04-19 00:29:357#include <memory>
8
asanka655d1112015-03-07 05:33:419#include "base/bind.h"
10#include "base/callback.h"
Lei Zhang1f46798c2021-05-26 01:35:1811#include "base/cxx17_backports.h"
asanka655d1112015-03-07 05:33:4112#include "base/files/file_util.h"
13#include "base/files/scoped_temp_dir.h"
asanka655d1112015-03-07 05:33:4114#include "base/run_loop.h"
avib896c712015-12-26 02:10:4315#include "build/build_config.h"
Yuta Hijikata235fc62b2020-12-08 03:48:3216#include "build/chromeos_buildflags.h"
asanka655d1112015-03-07 05:33:4117#include "chrome/browser/platform_util_internal.h"
18#include "testing/gtest/include/gtest/gtest.h"
19
Yuta Hijikata235fc62b2020-12-08 03:48:3220#if BUILDFLAG(IS_CHROMEOS_ASH)
asanka655d1112015-03-07 05:33:4121#include "base/json/json_string_value_serializer.h"
22#include "base/values.h"
Peter Marshall78564032021-10-05 02:54:1823#include "chrome/browser/apps/app_service/app_service_proxy.h"
24#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
25#include "chrome/browser/apps/app_service/app_service_test.h"
26#include "chrome/browser/apps/app_service/intent_util.h"
Yeunjoo Choie409e8e2021-07-06 11:09:3927#include "chrome/browser/ash/file_manager/app_id.h"
asanka655d1112015-03-07 05:33:4128#include "chrome/browser/chrome_content_browser_client.h"
asanka655d1112015-03-07 05:33:4129#include "chrome/browser/chromeos/fileapi/file_system_backend.h"
hashimotoa53e7e82016-10-26 06:30:4730#include "chrome/browser/chromeos/fileapi/file_system_backend_delegate.h"
asanka655d1112015-03-07 05:33:4131#include "chrome/browser/extensions/extension_special_storage_policy.h"
32#include "chrome/test/base/browser_with_test_window_test.h"
33#include "content/public/browser/browser_context.h"
34#include "content/public/common/content_client.h"
asanka655d1112015-03-07 05:33:4135#include "extensions/browser/extension_registry.h"
36#include "extensions/common/extension.h"
DongJun Kimfebb3c22019-10-21 02:08:0637#include "storage/browser/file_system/external_mount_points.h"
pwnall343665e72017-04-13 04:04:4038#include "storage/browser/test/mock_special_storage_policy.h"
DongJun Kimd6930ea2019-10-24 08:49:2539#include "storage/common/file_system/file_system_types.h"
asanka655d1112015-03-07 05:33:4140#else
Gabriel Charettec7108742019-08-23 03:31:4041#include "content/public/test/browser_task_environment.h"
asanka655d1112015-03-07 05:33:4142#endif
43
44namespace platform_util {
45
46namespace {
47
Yuta Hijikata235fc62b2020-12-08 03:48:3248#if BUILDFLAG(IS_CHROMEOS_ASH)
asanka655d1112015-03-07 05:33:4149
50// ChromeContentBrowserClient subclass that sets up a custom file system backend
51// that allows the test to grant file access to the file manager extension ID
52// without having to install the extension.
tfarina2176f4b2015-09-17 07:09:2653class PlatformUtilTestContentBrowserClient : public ChromeContentBrowserClient {
asanka655d1112015-03-07 05:33:4154 public:
55 void GetAdditionalFileSystemBackends(
56 content::BrowserContext* browser_context,
57 const base::FilePath& storage_partition_path,
avid6d88b912017-01-13 00:16:0058 std::vector<std::unique_ptr<storage::FileSystemBackend>>*
59 additional_backends) override {
asanka655d1112015-03-07 05:33:4160 storage::ExternalMountPoints* external_mount_points =
Lukasz Anforowiczeda637d22021-05-20 00:32:2361 browser_context->GetMountPoints();
asanka655d1112015-03-07 05:33:4162
63 // New FileSystemBackend that uses our MockSpecialStoragePolicy.
avid6d88b912017-01-13 00:16:0064 additional_backends->push_back(
Jeremy Romanec48d7a2018-03-01 17:35:0965 std::make_unique<chromeos::FileSystemBackend>(
David Black8f8c1eb2020-12-09 04:02:2366 nullptr, // profile
67 nullptr, // file_system_provider_delegate
68 nullptr, // mtp_delegate
69 nullptr, // arc_content_delegate
70 nullptr, // arc_documents_provider_delegate
71 nullptr, // drivefs_delegate
72 nullptr, // smbfs_delegate
Anand K. Mistry7694c362020-03-17 23:33:3473 external_mount_points,
avid6d88b912017-01-13 00:16:0074 storage::ExternalMountPoints::GetSystemInstance()));
asanka655d1112015-03-07 05:33:4175 }
76};
77
78// Base test fixture class to be used on Chrome OS.
79class PlatformUtilTestBase : public BrowserWithTestWindowTest {
80 protected:
81 void SetUpPlatformFixture(const base::FilePath& test_directory) {
Peter Boström6b701822021-04-15 03:53:0882 content_browser_client_ =
83 std::make_unique<PlatformUtilTestContentBrowserClient>();
asanka655d1112015-03-07 05:33:4184 old_content_browser_client_ =
85 content::SetBrowserClientForTesting(content_browser_client_.get());
86
Peter Marshall78564032021-10-05 02:54:1887 app_service_test_.SetUp(GetProfile());
88 app_service_proxy_ =
89 apps::AppServiceProxyFactory::GetForProfile(GetProfile());
90 ASSERT_TRUE(app_service_proxy_);
91
asanka655d1112015-03-07 05:33:4192 // The test_directory needs to be mounted for it to be accessible.
Lukasz Anforowiczeda637d22021-05-20 00:32:2393 GetProfile()->GetMountPoints()->RegisterFileSystem(
94 "test", storage::kFileSystemTypeLocal, storage::FileSystemMountOption(),
95 test_directory);
asanka655d1112015-03-07 05:33:4196
97 // To test opening a file, we are going to register a mock extension that
98 // handles .txt files. The extension doesn't actually need to exist due to
99 // the DisableShellOperationsForTesting() call which prevents the extension
100 // from being invoked.
101 std::string error;
102 int error_code = 0;
103
104 std::string json_manifest =
105 "{"
106 " \"manifest_version\": 2,"
107 " \"name\": \"Test extension\","
108 " \"version\": \"0\","
109 " \"app\": { \"background\": { \"scripts\": [\"main.js\"] }},"
110 " \"file_handlers\": {"
111 " \"text\": {"
112 " \"extensions\": [ \"txt\" ],"
113 " \"title\": \"Text\""
114 " }"
115 " }"
116 "}";
117 JSONStringValueDeserializer json_string_deserializer(json_manifest);
dcheng4af48582016-04-19 00:29:35118 std::unique_ptr<base::Value> manifest =
olli.raulaba045252015-10-16 06:16:40119 json_string_deserializer.Deserialize(&error_code, &error);
asanka655d1112015-03-07 05:33:41120 base::DictionaryValue* manifest_dictionary;
121
122 manifest->GetAsDictionary(&manifest_dictionary);
123 ASSERT_TRUE(manifest_dictionary);
124
125 scoped_refptr<extensions::Extension> extension =
126 extensions::Extension::Create(
127 test_directory.AppendASCII("invalid-extension"),
Gyuyoung Kimabc23382021-03-18 03:09:18128 extensions::mojom::ManifestLocation::kInvalidLocation,
129 *manifest_dictionary, extensions::Extension::NO_FLAGS, &error);
asanka655d1112015-03-07 05:33:41130 ASSERT_TRUE(error.empty()) << error;
Peter Marshall78564032021-10-05 02:54:18131
132 std::vector<apps::mojom::AppPtr> apps;
133 auto app = apps::mojom::App::New();
134 app->app_id = "invalid-chrome-app";
135 app->app_type = apps::mojom::AppType::kExtension;
136 app->show_in_launcher = apps::mojom::OptionalBool::kTrue;
137 app->readiness = apps::mojom::Readiness::kReady;
138 app->intent_filters =
139 apps_util::CreateChromeAppIntentFilters(extension.get());
140 apps.push_back(std::move(app));
141 app_service_proxy_->AppRegistryCache().OnApps(
142 std::move(apps), apps::mojom::AppType::kExtension,
143 /*should_notify_initialized=*/false);
144 app_service_test_.WaitForAppService();
asanka655d1112015-03-07 05:33:41145 }
146
Dominick Ng51154652019-09-25 07:44:20147 void SetUp() override {
148 BrowserWithTestWindowTest::SetUp();
149 base::RunLoop().RunUntilIdle();
150 }
151
asanka655d1112015-03-07 05:33:41152 void TearDown() override {
153 content::ContentBrowserClient* content_browser_client =
154 content::SetBrowserClientForTesting(old_content_browser_client_);
155 old_content_browser_client_ = nullptr;
156 DCHECK_EQ(static_cast<content::ContentBrowserClient*>(
157 content_browser_client_.get()),
158 content_browser_client)
159 << "ContentBrowserClient changed during test.";
160 BrowserWithTestWindowTest::TearDown();
161 }
162
163 private:
dcheng4af48582016-04-19 00:29:35164 std::unique_ptr<content::ContentBrowserClient> content_browser_client_;
asanka655d1112015-03-07 05:33:41165 content::ContentBrowserClient* old_content_browser_client_ = nullptr;
Peter Marshall78564032021-10-05 02:54:18166 apps::AppServiceTest app_service_test_;
167 apps::AppServiceProxyChromeOs* app_service_proxy_ = nullptr;
asanka655d1112015-03-07 05:33:41168};
169
170#else
171
172// Test fixture used by all desktop platforms other than Chrome OS.
173class PlatformUtilTestBase : public testing::Test {
174 protected:
175 Profile* GetProfile() { return nullptr; }
176 void SetUpPlatformFixture(const base::FilePath&) {}
177
178 private:
Gabriel Charette798fde72019-08-20 22:24:04179 content::BrowserTaskEnvironment task_environment_;
asanka655d1112015-03-07 05:33:41180};
181
182#endif
183
184class PlatformUtilTest : public PlatformUtilTestBase {
185 public:
186 void SetUp() override {
187 ASSERT_NO_FATAL_FAILURE(PlatformUtilTestBase::SetUp());
188
189 static const char kTestFileData[] = "Cow says moo!";
Avi Drissman5f0fb8c2018-12-25 23:20:49190 const int kTestFileDataLength = base::size(kTestFileData) - 1;
asanka655d1112015-03-07 05:33:41191
Dominick Ng51154652019-09-25 07:44:20192 // This prevents platform_util from invoking any shell or external APIs
asanka655d1112015-03-07 05:33:41193 // during tests. Doing so may result in external applications being launched
194 // and intefering with tests.
195 internal::DisableShellOperationsForTesting();
196
197 ASSERT_TRUE(directory_.CreateUniqueTempDir());
198
199 // A valid file.
vabr8023d872016-09-15 08:12:22200 existing_file_ = directory_.GetPath().AppendASCII("test_file.txt");
asanka655d1112015-03-07 05:33:41201 ASSERT_EQ(
202 kTestFileDataLength,
203 base::WriteFile(existing_file_, kTestFileData, kTestFileDataLength));
204
205 // A valid folder.
vabr8023d872016-09-15 08:12:22206 existing_folder_ = directory_.GetPath().AppendASCII("test_folder");
asanka655d1112015-03-07 05:33:41207 ASSERT_TRUE(base::CreateDirectory(existing_folder_));
208
209 // A non-existent path.
vabr8023d872016-09-15 08:12:22210 nowhere_ = directory_.GetPath().AppendASCII("nowhere");
asanka655d1112015-03-07 05:33:41211
vabr8023d872016-09-15 08:12:22212 SetUpPlatformFixture(directory_.GetPath());
asanka655d1112015-03-07 05:33:41213 }
214
215 OpenOperationResult CallOpenItem(const base::FilePath& path,
216 OpenItemType item_type) {
217 base::RunLoop run_loop;
218 OpenOperationResult result = OPEN_SUCCEEDED;
219 OpenOperationCallback callback =
Alexander Cooper71fa2b02020-07-16 17:49:36220 base::BindOnce(&OnOpenOperationDone, run_loop.QuitClosure(), &result);
221 OpenItem(GetProfile(), path, item_type, std::move(callback));
asanka655d1112015-03-07 05:33:41222 run_loop.Run();
223 return result;
224 }
225
226 base::FilePath existing_file_;
227 base::FilePath existing_folder_;
228 base::FilePath nowhere_;
229
230 protected:
231 base::ScopedTempDir directory_;
232
233 private:
dcheng4af48582016-04-19 00:29:35234 std::unique_ptr<base::RunLoop> run_loop_;
asanka655d1112015-03-07 05:33:41235
Alexander Cooperbc87af32020-07-15 15:59:45236 static void OnOpenOperationDone(base::OnceClosure closure,
asanka655d1112015-03-07 05:33:41237 OpenOperationResult* store_result,
238 OpenOperationResult result) {
239 *store_result = result;
Alexander Cooperbc87af32020-07-15 15:59:45240 std::move(closure).Run();
asanka655d1112015-03-07 05:33:41241 }
242};
243
244} // namespace
245
246TEST_F(PlatformUtilTest, OpenFile) {
247 EXPECT_EQ(OPEN_SUCCEEDED, CallOpenItem(existing_file_, OPEN_FILE));
248 EXPECT_EQ(OPEN_FAILED_INVALID_TYPE,
249 CallOpenItem(existing_folder_, OPEN_FILE));
250 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND, CallOpenItem(nowhere_, OPEN_FILE));
251}
252
Sam McNally663715c2019-09-24 20:12:24253TEST_F(PlatformUtilTest, OpenFolder) {
asanka655d1112015-03-07 05:33:41254 EXPECT_EQ(OPEN_SUCCEEDED, CallOpenItem(existing_folder_, OPEN_FOLDER));
255 EXPECT_EQ(OPEN_FAILED_INVALID_TYPE,
256 CallOpenItem(existing_file_, OPEN_FOLDER));
257 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND, CallOpenItem(nowhere_, OPEN_FOLDER));
258}
259
260#if defined(OS_POSIX)
261// Symbolic links are currently only supported on Posix. Windows technically
262// supports it as well, but not on Windows XP.
263class PlatformUtilPosixTest : public PlatformUtilTest {
264 public:
265 void SetUp() override {
266 ASSERT_NO_FATAL_FAILURE(PlatformUtilTest::SetUp());
267
vabr8023d872016-09-15 08:12:22268 symlink_to_file_ = directory_.GetPath().AppendASCII("l_file.txt");
asanka655d1112015-03-07 05:33:41269 ASSERT_TRUE(base::CreateSymbolicLink(existing_file_, symlink_to_file_));
vabr8023d872016-09-15 08:12:22270 symlink_to_folder_ = directory_.GetPath().AppendASCII("l_folder");
asanka655d1112015-03-07 05:33:41271 ASSERT_TRUE(base::CreateSymbolicLink(existing_folder_, symlink_to_folder_));
vabr8023d872016-09-15 08:12:22272 symlink_to_nowhere_ = directory_.GetPath().AppendASCII("l_nowhere");
asanka655d1112015-03-07 05:33:41273 ASSERT_TRUE(base::CreateSymbolicLink(nowhere_, symlink_to_nowhere_));
274 }
275
276 protected:
277 base::FilePath symlink_to_file_;
278 base::FilePath symlink_to_folder_;
279 base::FilePath symlink_to_nowhere_;
280};
281#endif // OS_POSIX
282
Yuta Hijikata235fc62b2020-12-08 03:48:32283#if BUILDFLAG(IS_CHROMEOS_ASH)
asanka655d1112015-03-07 05:33:41284// ChromeOS doesn't follow symbolic links in sandboxed filesystems. So all the
285// symbolic link tests should return PATH_NOT_FOUND.
286
Luciano Pachecoa25b7d42019-09-25 01:53:27287TEST_F(PlatformUtilPosixTest, OpenFileWithPosixSymlinksChromeOS) {
asanka655d1112015-03-07 05:33:41288 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND,
289 CallOpenItem(symlink_to_file_, OPEN_FILE));
290 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND,
291 CallOpenItem(symlink_to_folder_, OPEN_FILE));
292 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND,
293 CallOpenItem(symlink_to_nowhere_, OPEN_FILE));
294}
295
296TEST_F(PlatformUtilPosixTest, OpenFolderWithPosixSymlinksChromeOS) {
297 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND,
298 CallOpenItem(symlink_to_folder_, OPEN_FOLDER));
299 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND,
300 CallOpenItem(symlink_to_file_, OPEN_FOLDER));
301 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND,
302 CallOpenItem(symlink_to_nowhere_, OPEN_FOLDER));
303}
304
Luciano Pachecoa25b7d42019-09-25 01:53:27305TEST_F(PlatformUtilTest, OpenFileWithUnhandledFileType) {
asanka655d1112015-03-07 05:33:41306 base::FilePath unhandled_file =
vabr8023d872016-09-15 08:12:22307 directory_.GetPath().AppendASCII("myfile.filetype");
asanka655d1112015-03-07 05:33:41308 ASSERT_EQ(3, base::WriteFile(unhandled_file, "cat", 3));
309 EXPECT_EQ(OPEN_FAILED_NO_HANLDER_FOR_FILE_TYPE,
310 CallOpenItem(unhandled_file, OPEN_FILE));
311}
Yuta Hijikata235fc62b2020-12-08 03:48:32312#endif // BUILDFLAG(IS_CHROMEOS_ASH)
asanka655d1112015-03-07 05:33:41313
Yuta Hijikata235fc62b2020-12-08 03:48:32314#if defined(OS_POSIX) && !BUILDFLAG(IS_CHROMEOS_ASH)
asanka655d1112015-03-07 05:33:41315// On all other Posix platforms, the symbolic link tests should work as
316// expected.
317
318TEST_F(PlatformUtilPosixTest, OpenFileWithPosixSymlinks) {
319 EXPECT_EQ(OPEN_SUCCEEDED, CallOpenItem(symlink_to_file_, OPEN_FILE));
320 EXPECT_EQ(OPEN_FAILED_INVALID_TYPE,
321 CallOpenItem(symlink_to_folder_, OPEN_FILE));
322 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND,
323 CallOpenItem(symlink_to_nowhere_, OPEN_FILE));
324}
325
326TEST_F(PlatformUtilPosixTest, OpenFolderWithPosixSymlinks) {
327 EXPECT_EQ(OPEN_SUCCEEDED, CallOpenItem(symlink_to_folder_, OPEN_FOLDER));
328 EXPECT_EQ(OPEN_FAILED_INVALID_TYPE,
329 CallOpenItem(symlink_to_file_, OPEN_FOLDER));
330 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND,
331 CallOpenItem(symlink_to_nowhere_, OPEN_FOLDER));
332}
333#endif // OS_POSIX && !OS_CHROMEOS
334
335} // namespace platform_util