blob: c74778f0025517af1b8b6cbf490b66bc9b18c863 [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"
Arthur Sonzogni39396932023-04-24 09:41:3313#include "base/memory/raw_ptr.h"
asanka655d1112015-03-07 05:33:4114#include "base/run_loop.h"
avib896c712015-12-26 02:10:4315#include "build/build_config.h"
asanka655d1112015-03-07 05:33:4116#include "chrome/browser/platform_util_internal.h"
17#include "testing/gtest/include/gtest/gtest.h"
18
Yuta Hijikata8850b2d2025-03-06 02:02:4819#if BUILDFLAG(IS_CHROMEOS)
Alison Galebba2a512025-04-15 19:31:1220#include "base/json/json_string_value_serializer.h"
asanka655d1112015-03-07 05:33:4121#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 Choief8a4a42022-11-08 04:21:0526#include "chrome/browser/ash/fileapi/file_system_backend.h"
27#include "chrome/browser/ash/fileapi/file_system_backend_delegate.h"
asanka655d1112015-03-07 05:33:4128#include "chrome/browser/chrome_content_browser_client.h"
asanka655d1112015-03-07 05:33:4129#include "chrome/browser/extensions/extension_special_storage_policy.h"
30#include "chrome/test/base/browser_with_test_window_test.h"
Nancy Wang5dc47bc2022-03-25 17:55:4731#include "components/services/app_service/public/cpp/app_types.h"
Nancy Wang5dc47bc2022-03-25 17:55:4732#include "components/services/app_service/public/cpp/intent_filter.h"
asanka655d1112015-03-07 05:33:4133#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 Hijikata8850b2d2025-03-06 02:02:4848#if BUILDFLAG(IS_CHROMEOS)
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.
Yeunjoo Choi3d9ed38a2022-11-10 02:51:2464 additional_backends->push_back(std::make_unique<ash::FileSystemBackend>(
65 nullptr, // profile
66 nullptr, // file_system_provider_delegate
67 nullptr, // mtp_delegate
68 nullptr, // arc_content_delegate
69 nullptr, // arc_documents_provider_delegate
70 nullptr, // drivefs_delegate
71 nullptr, // smbfs_delegate
72 external_mount_points,
73 storage::ExternalMountPoints::GetSystemInstance()));
asanka655d1112015-03-07 05:33:4174 }
75};
76
77// Base test fixture class to be used on Chrome OS.
78class PlatformUtilTestBase : public BrowserWithTestWindowTest {
79 protected:
80 void SetUpPlatformFixture(const base::FilePath& test_directory) {
Peter Boström6b701822021-04-15 03:53:0881 content_browser_client_ =
82 std::make_unique<PlatformUtilTestContentBrowserClient>();
asanka655d1112015-03-07 05:33:4183 old_content_browser_client_ =
84 content::SetBrowserClientForTesting(content_browser_client_.get());
85
Peter Marshall78564032021-10-05 02:54:1886 app_service_test_.SetUp(GetProfile());
87 app_service_proxy_ =
88 apps::AppServiceProxyFactory::GetForProfile(GetProfile());
89 ASSERT_TRUE(app_service_proxy_);
90
asanka655d1112015-03-07 05:33:4191 // The test_directory needs to be mounted for it to be accessible.
Lukasz Anforowiczeda637d22021-05-20 00:32:2392 GetProfile()->GetMountPoints()->RegisterFileSystem(
93 "test", storage::kFileSystemTypeLocal, storage::FileSystemMountOption(),
94 test_directory);
asanka655d1112015-03-07 05:33:4195
96 // To test opening a file, we are going to register a mock extension that
97 // handles .txt files. The extension doesn't actually need to exist due to
98 // the DisableShellOperationsForTesting() call which prevents the extension
99 // from being invoked.
Alison Galebba2a512025-04-15 19:31:12100 std::string error;
101 int error_code = 0;
asanka655d1112015-03-07 05:33:41102
Alison Galebba2a512025-04-15 19:31:12103 std::string json_manifest =
104 "{"
105 " \"manifest_version\": 2,"
106 " \"name\": \"Test extension\","
107 " \"version\": \"0\","
108 " \"app\": { \"background\": { \"scripts\": [\"main.js\"] }},"
109 " \"file_handlers\": {"
110 " \"text\": {"
111 " \"extensions\": [ \"txt\" ],"
112 " \"title\": \"Text\""
113 " }"
114 " }"
115 "}";
116 JSONStringValueDeserializer json_string_deserializer(json_manifest);
117 std::unique_ptr<base::Value> manifest =
118 json_string_deserializer.Deserialize(&error_code, &error);
119 base::Value::Dict* manifest_dictionary = manifest->GetIfDict();
asanka655d1112015-03-07 05:33:41120 ASSERT_TRUE(manifest_dictionary);
121
122 scoped_refptr<extensions::Extension> extension =
123 extensions::Extension::Create(
124 test_directory.AppendASCII("invalid-extension"),
Gyuyoung Kimabc23382021-03-18 03:09:18125 extensions::mojom::ManifestLocation::kInvalidLocation,
126 *manifest_dictionary, extensions::Extension::NO_FLAGS, &error);
asanka655d1112015-03-07 05:33:41127 ASSERT_TRUE(error.empty()) << error;
Peter Marshall78564032021-10-05 02:54:18128
Nancy Wang5dc47bc2022-03-25 17:55:47129 std::vector<apps::AppPtr> apps;
130 auto app = std::make_unique<apps::App>(apps::AppType::kChromeApp,
131 "invalid-chrome-app");
132 app->handles_intents = true;
133 app->readiness = apps::Readiness::kReady;
Peter Marshall78564032021-10-05 02:54:18134 app->intent_filters =
Nancy Wang5dc47bc2022-03-25 17:55:47135 apps_util::CreateIntentFiltersForChromeApp(extension.get());
Peter Marshall78564032021-10-05 02:54:18136 apps.push_back(std::move(app));
Nancy Wang84765812023-10-17 04:11:03137 app_service_proxy_->OnApps(std::move(apps), apps::AppType::kChromeApp,
138 /*should_notify_initialized=*/false);
asanka655d1112015-03-07 05:33:41139 }
140
Dominick Ng51154652019-09-25 07:44:20141 void SetUp() override {
142 BrowserWithTestWindowTest::SetUp();
143 base::RunLoop().RunUntilIdle();
144 }
145
asanka655d1112015-03-07 05:33:41146 void TearDown() override {
147 content::ContentBrowserClient* content_browser_client =
148 content::SetBrowserClientForTesting(old_content_browser_client_);
149 old_content_browser_client_ = nullptr;
150 DCHECK_EQ(static_cast<content::ContentBrowserClient*>(
151 content_browser_client_.get()),
152 content_browser_client)
153 << "ContentBrowserClient changed during test.";
154 BrowserWithTestWindowTest::TearDown();
155 }
156
157 private:
dcheng4af48582016-04-19 00:29:35158 std::unique_ptr<content::ContentBrowserClient> content_browser_client_;
Bartek Nowierski8f579342024-01-08 06:45:15159 raw_ptr<content::ContentBrowserClient> old_content_browser_client_ = nullptr;
Peter Marshall78564032021-10-05 02:54:18160 apps::AppServiceTest app_service_test_;
Bartek Nowierski8f579342024-01-08 06:45:15161 raw_ptr<apps::AppServiceProxy, DanglingUntriaged> app_service_proxy_ =
162 nullptr;
asanka655d1112015-03-07 05:33:41163};
164
165#else
166
167// Test fixture used by all desktop platforms other than Chrome OS.
168class PlatformUtilTestBase : public testing::Test {
169 protected:
170 Profile* GetProfile() { return nullptr; }
171 void SetUpPlatformFixture(const base::FilePath&) {}
172
173 private:
Gabriel Charette798fde72019-08-20 22:24:04174 content::BrowserTaskEnvironment task_environment_;
asanka655d1112015-03-07 05:33:41175};
176
177#endif
178
179class PlatformUtilTest : public PlatformUtilTestBase {
180 public:
181 void SetUp() override {
182 ASSERT_NO_FATAL_FAILURE(PlatformUtilTestBase::SetUp());
183
184 static const char kTestFileData[] = "Cow says moo!";
asanka655d1112015-03-07 05:33:41185
Dominick Ng51154652019-09-25 07:44:20186 // This prevents platform_util from invoking any shell or external APIs
asanka655d1112015-03-07 05:33:41187 // during tests. Doing so may result in external applications being launched
188 // and intefering with tests.
189 internal::DisableShellOperationsForTesting();
190
191 ASSERT_TRUE(directory_.CreateUniqueTempDir());
192
193 // A valid file.
vabr8023d872016-09-15 08:12:22194 existing_file_ = directory_.GetPath().AppendASCII("test_file.txt");
Claudio DeSouza542fc002023-02-23 11:08:04195 ASSERT_TRUE(base::WriteFile(existing_file_, kTestFileData));
asanka655d1112015-03-07 05:33:41196
197 // A valid folder.
vabr8023d872016-09-15 08:12:22198 existing_folder_ = directory_.GetPath().AppendASCII("test_folder");
asanka655d1112015-03-07 05:33:41199 ASSERT_TRUE(base::CreateDirectory(existing_folder_));
200
201 // A non-existent path.
vabr8023d872016-09-15 08:12:22202 nowhere_ = directory_.GetPath().AppendASCII("nowhere");
asanka655d1112015-03-07 05:33:41203
vabr8023d872016-09-15 08:12:22204 SetUpPlatformFixture(directory_.GetPath());
asanka655d1112015-03-07 05:33:41205 }
206
207 OpenOperationResult CallOpenItem(const base::FilePath& path,
208 OpenItemType item_type) {
209 base::RunLoop run_loop;
210 OpenOperationResult result = OPEN_SUCCEEDED;
211 OpenOperationCallback callback =
Alexander Cooper71fa2b02020-07-16 17:49:36212 base::BindOnce(&OnOpenOperationDone, run_loop.QuitClosure(), &result);
213 OpenItem(GetProfile(), path, item_type, std::move(callback));
asanka655d1112015-03-07 05:33:41214 run_loop.Run();
215 return result;
216 }
217
218 base::FilePath existing_file_;
219 base::FilePath existing_folder_;
220 base::FilePath nowhere_;
221
222 protected:
223 base::ScopedTempDir directory_;
224
225 private:
dcheng4af48582016-04-19 00:29:35226 std::unique_ptr<base::RunLoop> run_loop_;
asanka655d1112015-03-07 05:33:41227
Alexander Cooperbc87af32020-07-15 15:59:45228 static void OnOpenOperationDone(base::OnceClosure closure,
asanka655d1112015-03-07 05:33:41229 OpenOperationResult* store_result,
230 OpenOperationResult result) {
231 *store_result = result;
Alexander Cooperbc87af32020-07-15 15:59:45232 std::move(closure).Run();
asanka655d1112015-03-07 05:33:41233 }
234};
235
236} // namespace
237
238TEST_F(PlatformUtilTest, OpenFile) {
239 EXPECT_EQ(OPEN_SUCCEEDED, CallOpenItem(existing_file_, OPEN_FILE));
240 EXPECT_EQ(OPEN_FAILED_INVALID_TYPE,
241 CallOpenItem(existing_folder_, OPEN_FILE));
242 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND, CallOpenItem(nowhere_, OPEN_FILE));
243}
244
Sam McNally663715c2019-09-24 20:12:24245TEST_F(PlatformUtilTest, OpenFolder) {
asanka655d1112015-03-07 05:33:41246 EXPECT_EQ(OPEN_SUCCEEDED, CallOpenItem(existing_folder_, OPEN_FOLDER));
247 EXPECT_EQ(OPEN_FAILED_INVALID_TYPE,
248 CallOpenItem(existing_file_, OPEN_FOLDER));
249 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND, CallOpenItem(nowhere_, OPEN_FOLDER));
250}
251
Xiaohan Wang55ae2c012022-01-20 21:49:11252#if BUILDFLAG(IS_POSIX)
asanka655d1112015-03-07 05:33:41253// Symbolic links are currently only supported on Posix. Windows technically
254// supports it as well, but not on Windows XP.
255class PlatformUtilPosixTest : public PlatformUtilTest {
256 public:
257 void SetUp() override {
258 ASSERT_NO_FATAL_FAILURE(PlatformUtilTest::SetUp());
259
vabr8023d872016-09-15 08:12:22260 symlink_to_file_ = directory_.GetPath().AppendASCII("l_file.txt");
asanka655d1112015-03-07 05:33:41261 ASSERT_TRUE(base::CreateSymbolicLink(existing_file_, symlink_to_file_));
vabr8023d872016-09-15 08:12:22262 symlink_to_folder_ = directory_.GetPath().AppendASCII("l_folder");
asanka655d1112015-03-07 05:33:41263 ASSERT_TRUE(base::CreateSymbolicLink(existing_folder_, symlink_to_folder_));
vabr8023d872016-09-15 08:12:22264 symlink_to_nowhere_ = directory_.GetPath().AppendASCII("l_nowhere");
asanka655d1112015-03-07 05:33:41265 ASSERT_TRUE(base::CreateSymbolicLink(nowhere_, symlink_to_nowhere_));
266 }
267
268 protected:
269 base::FilePath symlink_to_file_;
270 base::FilePath symlink_to_folder_;
271 base::FilePath symlink_to_nowhere_;
272};
Xiaohan Wang55ae2c012022-01-20 21:49:11273#endif // BUILDFLAG(IS_POSIX)
asanka655d1112015-03-07 05:33:41274
Yuta Hijikata8850b2d2025-03-06 02:02:48275#if BUILDFLAG(IS_CHROMEOS)
asanka655d1112015-03-07 05:33:41276// ChromeOS doesn't follow symbolic links in sandboxed filesystems. So all the
277// symbolic link tests should return PATH_NOT_FOUND.
278
Luciano Pachecoa25b7d42019-09-25 01:53:27279TEST_F(PlatformUtilPosixTest, OpenFileWithPosixSymlinksChromeOS) {
asanka655d1112015-03-07 05:33:41280 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND,
281 CallOpenItem(symlink_to_file_, OPEN_FILE));
282 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND,
283 CallOpenItem(symlink_to_folder_, OPEN_FILE));
284 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND,
285 CallOpenItem(symlink_to_nowhere_, OPEN_FILE));
286}
287
288TEST_F(PlatformUtilPosixTest, OpenFolderWithPosixSymlinksChromeOS) {
289 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND,
290 CallOpenItem(symlink_to_folder_, OPEN_FOLDER));
291 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND,
292 CallOpenItem(symlink_to_file_, OPEN_FOLDER));
293 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND,
294 CallOpenItem(symlink_to_nowhere_, OPEN_FOLDER));
295}
296
Luciano Pachecoa25b7d42019-09-25 01:53:27297TEST_F(PlatformUtilTest, OpenFileWithUnhandledFileType) {
asanka655d1112015-03-07 05:33:41298 base::FilePath unhandled_file =
vabr8023d872016-09-15 08:12:22299 directory_.GetPath().AppendASCII("myfile.filetype");
Claudio DeSouza542fc002023-02-23 11:08:04300 ASSERT_TRUE(base::WriteFile(unhandled_file, "cat"));
asanka655d1112015-03-07 05:33:41301 EXPECT_EQ(OPEN_FAILED_NO_HANLDER_FOR_FILE_TYPE,
302 CallOpenItem(unhandled_file, OPEN_FILE));
303}
Yuta Hijikata8850b2d2025-03-06 02:02:48304#endif // BUILDFLAG(IS_CHROMEOS)
asanka655d1112015-03-07 05:33:41305
Yuta Hijikata8850b2d2025-03-06 02:02:48306#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_CHROMEOS)
asanka655d1112015-03-07 05:33:41307// On all other Posix platforms, the symbolic link tests should work as
308// expected.
309
310TEST_F(PlatformUtilPosixTest, OpenFileWithPosixSymlinks) {
311 EXPECT_EQ(OPEN_SUCCEEDED, CallOpenItem(symlink_to_file_, OPEN_FILE));
312 EXPECT_EQ(OPEN_FAILED_INVALID_TYPE,
313 CallOpenItem(symlink_to_folder_, OPEN_FILE));
314 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND,
315 CallOpenItem(symlink_to_nowhere_, OPEN_FILE));
316}
317
318TEST_F(PlatformUtilPosixTest, OpenFolderWithPosixSymlinks) {
319 EXPECT_EQ(OPEN_SUCCEEDED, CallOpenItem(symlink_to_folder_, OPEN_FOLDER));
320 EXPECT_EQ(OPEN_FAILED_INVALID_TYPE,
321 CallOpenItem(symlink_to_file_, OPEN_FOLDER));
322 EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND,
323 CallOpenItem(symlink_to_nowhere_, OPEN_FOLDER));
324}
Yuta Hijikata8850b2d2025-03-06 02:02:48325#endif // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_CHROMEOS)
asanka655d1112015-03-07 05:33:41326
327} // namespace platform_util