blob: 910de742f514f71b354a78e68c385c36de8e4cba [file] [log] [blame]
Avi Drissman4a8573c2022-09-09 19:35:541// Copyright 2015 The Chromium Authors
asanka655d1112015-03-07 05:33:412// 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/files/file_util.h"
10#include "base/files/scoped_temp_dir.h"
Avi Drissman9269d4ed2023-01-07 01:38:0611#include "base/functional/bind.h"
12#include "base/functional/callback.h"
asanka655d1112015-03-07 05:33:4113#include "base/run_loop.h"
avib896c712015-12-26 02:10:4314#include "build/build_config.h"
Yuta Hijikata235fc62b2020-12-08 03:48:3215#include "build/chromeos_buildflags.h"
asanka655d1112015-03-07 05:33:4116#include "chrome/browser/platform_util_internal.h"
17#include "testing/gtest/include/gtest/gtest.h"
18
Yuta Hijikata235fc62b2020-12-08 03:48:3219#if BUILDFLAG(IS_CHROMEOS_ASH)
asanka655d1112015-03-07 05:33:4120#include "base/json/json_string_value_serializer.h"
21#include "base/values.h"
Peter Marshall78564032021-10-05 02:54:1822#include "chrome/browser/apps/app_service/app_service_proxy.h"
23#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
24#include "chrome/browser/apps/app_service/app_service_test.h"
25#include "chrome/browser/apps/app_service/intent_util.h"
Yeunjoo Choie409e8e2021-07-06 11:09:3926#include "chrome/browser/ash/file_manager/app_id.h"
Yeunjoo Choief8a4a42022-11-08 04:21:0527#include "chrome/browser/ash/fileapi/file_system_backend.h"
28#include "chrome/browser/ash/fileapi/file_system_backend_delegate.h"
asanka655d1112015-03-07 05:33:4129#include "chrome/browser/chrome_content_browser_client.h"
asanka655d1112015-03-07 05:33:4130#include "chrome/browser/extensions/extension_special_storage_policy.h"
31#include "chrome/test/base/browser_with_test_window_test.h"
Nancy Wang5dc47bc2022-03-25 17:55:4732#include "components/services/app_service/public/cpp/app_types.h"
Nancy Wang5dc47bc2022-03-25 17:55:4733#include "components/services/app_service/public/cpp/intent_filter.h"
asanka655d1112015-03-07 05:33:4134#include "content/public/browser/browser_context.h"
35#include "content/public/common/content_client.h"
asanka655d1112015-03-07 05:33:4136#include "extensions/browser/extension_registry.h"
37#include "extensions/common/extension.h"
DongJun Kimfebb3c22019-10-21 02:08:0638#include "storage/browser/file_system/external_mount_points.h"
pwnall343665e72017-04-13 04:04:4039#include "storage/browser/test/mock_special_storage_policy.h"
DongJun Kimd6930ea2019-10-24 08:49:2540#include "storage/common/file_system/file_system_types.h"
asanka655d1112015-03-07 05:33:4141#else
Gabriel Charettec7108742019-08-23 03:31:4042#include "content/public/test/browser_task_environment.h"
asanka655d1112015-03-07 05:33:4143#endif
44
45namespace platform_util {
46
47namespace {
48
Yuta Hijikata235fc62b2020-12-08 03:48:3249#if BUILDFLAG(IS_CHROMEOS_ASH)
asanka655d1112015-03-07 05:33:4150
51// ChromeContentBrowserClient subclass that sets up a custom file system backend
52// that allows the test to grant file access to the file manager extension ID
53// without having to install the extension.
tfarina2176f4b2015-09-17 07:09:2654class PlatformUtilTestContentBrowserClient : public ChromeContentBrowserClient {
asanka655d1112015-03-07 05:33:4155 public:
56 void GetAdditionalFileSystemBackends(
57 content::BrowserContext* browser_context,
58 const base::FilePath& storage_partition_path,
avid6d88b912017-01-13 00:16:0059 std::vector<std::unique_ptr<storage::FileSystemBackend>>*
60 additional_backends) override {
asanka655d1112015-03-07 05:33:4161 storage::ExternalMountPoints* external_mount_points =
Lukasz Anforowiczeda637d22021-05-20 00:32:2362 browser_context->GetMountPoints();
asanka655d1112015-03-07 05:33:4163
64 // New FileSystemBackend that uses our MockSpecialStoragePolicy.
Yeunjoo Choi3d9ed38a2022-11-10 02:51:2465 additional_backends->push_back(std::make_unique<ash::FileSystemBackend>(
66 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
73 external_mount_points,
74 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);
Yoichi Osato5a16fac2022-11-25 10:07:16120 base::Value::Dict* manifest_dictionary = manifest->GetIfDict();
asanka655d1112015-03-07 05:33:41121 ASSERT_TRUE(manifest_dictionary);
122
123 scoped_refptr<extensions::Extension> extension =
124 extensions::Extension::Create(
125 test_directory.AppendASCII("invalid-extension"),
Gyuyoung Kimabc23382021-03-18 03:09:18126 extensions::mojom::ManifestLocation::kInvalidLocation,
127 *manifest_dictionary, extensions::Extension::NO_FLAGS, &error);
asanka655d1112015-03-07 05:33:41128 ASSERT_TRUE(error.empty()) << error;
Peter Marshall78564032021-10-05 02:54:18129
Nancy Wang5dc47bc2022-03-25 17:55:47130 std::vector<apps::AppPtr> apps;
131 auto app = std::make_unique<apps::App>(apps::AppType::kChromeApp,
132 "invalid-chrome-app");
133 app->handles_intents = true;
134 app->readiness = apps::Readiness::kReady;
Peter Marshall78564032021-10-05 02:54:18135 app->intent_filters =
Nancy Wang5dc47bc2022-03-25 17:55:47136 apps_util::CreateIntentFiltersForChromeApp(extension.get());
Peter Marshall78564032021-10-05 02:54:18137 apps.push_back(std::move(app));
Nancy Wang09c76da2022-07-26 00:21:45138 app_service_proxy_->AppRegistryCache().OnApps(
139 std::move(apps), apps::AppType::kChromeApp,
140 /*should_notify_initialized=*/false);
asanka655d1112015-03-07 05:33:41141 }
142
Dominick Ng51154652019-09-25 07:44:20143 void SetUp() override {
144 BrowserWithTestWindowTest::SetUp();
145 base::RunLoop().RunUntilIdle();
146 }
147
asanka655d1112015-03-07 05:33:41148 void TearDown() override {
149 content::ContentBrowserClient* content_browser_client =
150 content::SetBrowserClientForTesting(old_content_browser_client_);
151 old_content_browser_client_ = nullptr;
152 DCHECK_EQ(static_cast<content::ContentBrowserClient*>(
153 content_browser_client_.get()),
154 content_browser_client)
155 << "ContentBrowserClient changed during test.";
156 BrowserWithTestWindowTest::TearDown();
157 }
158
159 private:
dcheng4af48582016-04-19 00:29:35160 std::unique_ptr<content::ContentBrowserClient> content_browser_client_;
asanka655d1112015-03-07 05:33:41161 content::ContentBrowserClient* old_content_browser_client_ = nullptr;
Peter Marshall78564032021-10-05 02:54:18162 apps::AppServiceTest app_service_test_;
Dominick Ng5cb72ca2021-11-03 22:21:30163 apps::AppServiceProxy* app_service_proxy_ = nullptr;
asanka655d1112015-03-07 05:33:41164};
165
166#else
167
168// Test fixture used by all desktop platforms other than Chrome OS.
169class PlatformUtilTestBase : public testing::Test {
170 protected:
171 Profile* GetProfile() { return nullptr; }
172 void SetUpPlatformFixture(const base::FilePath&) {}
173
174 private:
Gabriel Charette798fde72019-08-20 22:24:04175 content::BrowserTaskEnvironment task_environment_;
asanka655d1112015-03-07 05:33:41176};
177
178#endif
179
180class PlatformUtilTest : public PlatformUtilTestBase {
181 public:
182 void SetUp() override {
183 ASSERT_NO_FATAL_FAILURE(PlatformUtilTestBase::SetUp());
184
185 static const char kTestFileData[] = "Cow says moo!";
asanka655d1112015-03-07 05:33:41186
Dominick Ng51154652019-09-25 07:44:20187 // This prevents platform_util from invoking any shell or external APIs
asanka655d1112015-03-07 05:33:41188 // during tests. Doing so may result in external applications being launched
189 // and intefering with tests.
190 internal::DisableShellOperationsForTesting();
191
192 ASSERT_TRUE(directory_.CreateUniqueTempDir());
193
194 // A valid file.
vabr8023d872016-09-15 08:12:22195 existing_file_ = directory_.GetPath().AppendASCII("test_file.txt");
Claudio DeSouza542fc002023-02-23 11:08:04196 ASSERT_TRUE(base::WriteFile(existing_file_, kTestFileData));
asanka655d1112015-03-07 05:33:41197
198 // A valid folder.
vabr8023d872016-09-15 08:12:22199 existing_folder_ = directory_.GetPath().AppendASCII("test_folder");
asanka655d1112015-03-07 05:33:41200 ASSERT_TRUE(base::CreateDirectory(existing_folder_));
201
202 // A non-existent path.
vabr8023d872016-09-15 08:12:22203 nowhere_ = directory_.GetPath().AppendASCII("nowhere");
asanka655d1112015-03-07 05:33:41204
vabr8023d872016-09-15 08:12:22205 SetUpPlatformFixture(directory_.GetPath());
asanka655d1112015-03-07 05:33:41206 }
207
208 OpenOperationResult CallOpenItem(const base::FilePath& path,
209 OpenItemType item_type) {
210 base::RunLoop run_loop;
211 OpenOperationResult result = OPEN_SUCCEEDED;
212 OpenOperationCallback callback =
Alexander Cooper71fa2b02020-07-16 17:49:36213 base::BindOnce(&OnOpenOperationDone, run_loop.QuitClosure(), &result);
214 OpenItem(GetProfile(), path, item_type, std::move(callback));
asanka655d1112015-03-07 05:33:41215 run_loop.Run();
216 return result;
217 }
218
219 base::FilePath existing_file_;
220 base::FilePath existing_folder_;
221 base::FilePath nowhere_;
222
223 protected:
224 base::ScopedTempDir directory_;
225
226 private:
dcheng4af48582016-04-19 00:29:35227 std::unique_ptr<base::RunLoop> run_loop_;
asanka655d1112015-03-07 05:33:41228
Alexander Cooperbc87af32020-07-15 15:59:45229 static void OnOpenOperationDone(base::OnceClosure closure,
asanka655d1112015-03-07 05:33:41230 OpenOperationResult* store_result,
231 OpenOperationResult result) {
232 *store_result = result;
Alexander Cooperbc87af32020-07-15 15:59:45233 std::move(closure).Run();
asanka655d1112015-03-07 05:33:41234 }
235};
236
237} // namespace
238
239TEST_F(PlatformUtilTest, OpenFile) {
240 EXPECT_EQ(OPEN_SUCCEEDED, CallOpenItem(existing_file_, OPEN_FILE));
241 EXPECT_EQ(OPEN_FAILED_INVALID_TYPE,
242 CallOpenItem(existing_folder_, OPEN_FILE));
243 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND, CallOpenItem(nowhere_, OPEN_FILE));
244}
245
Sam McNally663715c2019-09-24 20:12:24246TEST_F(PlatformUtilTest, OpenFolder) {
asanka655d1112015-03-07 05:33:41247 EXPECT_EQ(OPEN_SUCCEEDED, CallOpenItem(existing_folder_, OPEN_FOLDER));
248 EXPECT_EQ(OPEN_FAILED_INVALID_TYPE,
249 CallOpenItem(existing_file_, OPEN_FOLDER));
250 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND, CallOpenItem(nowhere_, OPEN_FOLDER));
251}
252
Xiaohan Wang55ae2c012022-01-20 21:49:11253#if BUILDFLAG(IS_POSIX)
asanka655d1112015-03-07 05:33:41254// Symbolic links are currently only supported on Posix. Windows technically
255// supports it as well, but not on Windows XP.
256class PlatformUtilPosixTest : public PlatformUtilTest {
257 public:
258 void SetUp() override {
259 ASSERT_NO_FATAL_FAILURE(PlatformUtilTest::SetUp());
260
vabr8023d872016-09-15 08:12:22261 symlink_to_file_ = directory_.GetPath().AppendASCII("l_file.txt");
asanka655d1112015-03-07 05:33:41262 ASSERT_TRUE(base::CreateSymbolicLink(existing_file_, symlink_to_file_));
vabr8023d872016-09-15 08:12:22263 symlink_to_folder_ = directory_.GetPath().AppendASCII("l_folder");
asanka655d1112015-03-07 05:33:41264 ASSERT_TRUE(base::CreateSymbolicLink(existing_folder_, symlink_to_folder_));
vabr8023d872016-09-15 08:12:22265 symlink_to_nowhere_ = directory_.GetPath().AppendASCII("l_nowhere");
asanka655d1112015-03-07 05:33:41266 ASSERT_TRUE(base::CreateSymbolicLink(nowhere_, symlink_to_nowhere_));
267 }
268
269 protected:
270 base::FilePath symlink_to_file_;
271 base::FilePath symlink_to_folder_;
272 base::FilePath symlink_to_nowhere_;
273};
Xiaohan Wang55ae2c012022-01-20 21:49:11274#endif // BUILDFLAG(IS_POSIX)
asanka655d1112015-03-07 05:33:41275
Yuta Hijikata235fc62b2020-12-08 03:48:32276#if BUILDFLAG(IS_CHROMEOS_ASH)
asanka655d1112015-03-07 05:33:41277// ChromeOS doesn't follow symbolic links in sandboxed filesystems. So all the
278// symbolic link tests should return PATH_NOT_FOUND.
279
Luciano Pachecoa25b7d42019-09-25 01:53:27280TEST_F(PlatformUtilPosixTest, OpenFileWithPosixSymlinksChromeOS) {
asanka655d1112015-03-07 05:33:41281 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND,
282 CallOpenItem(symlink_to_file_, OPEN_FILE));
283 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND,
284 CallOpenItem(symlink_to_folder_, OPEN_FILE));
285 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND,
286 CallOpenItem(symlink_to_nowhere_, OPEN_FILE));
287}
288
289TEST_F(PlatformUtilPosixTest, OpenFolderWithPosixSymlinksChromeOS) {
290 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND,
291 CallOpenItem(symlink_to_folder_, OPEN_FOLDER));
292 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND,
293 CallOpenItem(symlink_to_file_, OPEN_FOLDER));
294 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND,
295 CallOpenItem(symlink_to_nowhere_, OPEN_FOLDER));
296}
297
Luciano Pachecoa25b7d42019-09-25 01:53:27298TEST_F(PlatformUtilTest, OpenFileWithUnhandledFileType) {
asanka655d1112015-03-07 05:33:41299 base::FilePath unhandled_file =
vabr8023d872016-09-15 08:12:22300 directory_.GetPath().AppendASCII("myfile.filetype");
Claudio DeSouza542fc002023-02-23 11:08:04301 ASSERT_TRUE(base::WriteFile(unhandled_file, "cat"));
asanka655d1112015-03-07 05:33:41302 EXPECT_EQ(OPEN_FAILED_NO_HANLDER_FOR_FILE_TYPE,
303 CallOpenItem(unhandled_file, OPEN_FILE));
304}
Yuta Hijikata235fc62b2020-12-08 03:48:32305#endif // BUILDFLAG(IS_CHROMEOS_ASH)
asanka655d1112015-03-07 05:33:41306
Xiaohan Wang55ae2c012022-01-20 21:49:11307#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_CHROMEOS_ASH)
asanka655d1112015-03-07 05:33:41308// On all other Posix platforms, the symbolic link tests should work as
309// expected.
310
311TEST_F(PlatformUtilPosixTest, OpenFileWithPosixSymlinks) {
312 EXPECT_EQ(OPEN_SUCCEEDED, CallOpenItem(symlink_to_file_, OPEN_FILE));
313 EXPECT_EQ(OPEN_FAILED_INVALID_TYPE,
314 CallOpenItem(symlink_to_folder_, OPEN_FILE));
315 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND,
316 CallOpenItem(symlink_to_nowhere_, OPEN_FILE));
317}
318
319TEST_F(PlatformUtilPosixTest, OpenFolderWithPosixSymlinks) {
320 EXPECT_EQ(OPEN_SUCCEEDED, CallOpenItem(symlink_to_folder_, OPEN_FOLDER));
321 EXPECT_EQ(OPEN_FAILED_INVALID_TYPE,
322 CallOpenItem(symlink_to_file_, OPEN_FOLDER));
323 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND,
324 CallOpenItem(symlink_to_nowhere_, OPEN_FOLDER));
325}
Xiaohan Wang55ae2c012022-01-20 21:49:11326#endif // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_CHROMEOS_ASH)
asanka655d1112015-03-07 05:33:41327
328} // namespace platform_util