1 // Copyright 2013 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/chromeos/file_manager/file_tasks.h"
6 
7 #include <algorithm>
8 #include <set>
9 #include <utility>
10 
11 #include "base/bind.h"
12 #include "base/command_line.h"
13 #include "base/run_loop.h"
14 #include "base/values.h"
15 #include "chrome/browser/chromeos/crostini/crostini_mime_types_service.h"
16 #include "chrome/browser/chromeos/crostini/crostini_mime_types_service_factory.h"
17 #include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
18 #include "chrome/browser/chromeos/crostini/crostini_test_helper.h"
19 #include "chrome/browser/chromeos/crostini/fake_crostini_features.h"
20 #include "chrome/browser/chromeos/drive/file_system_util.h"
21 #include "chrome/browser/chromeos/file_manager/app_id.h"
22 #include "chrome/browser/chromeos/file_manager/path_util.h"
23 #include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
24 #include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h"
25 #include "chrome/browser/extensions/extension_service.h"
26 #include "chrome/browser/extensions/test_extension_system.h"
27 #include "chrome/common/chrome_features.h"
28 #include "chrome/common/pref_names.h"
29 #include "chrome/test/base/testing_profile.h"
30 #include "chromeos/dbus/dbus_thread_manager.h"
31 #include "chromeos/dbus/fake_concierge_client.h"
32 #include "components/prefs/pref_registry_simple.h"
33 #include "components/prefs/pref_service.h"
34 #include "components/prefs/testing_pref_service.h"
35 #include "components/services/app_service/public/cpp/file_handler.h"
36 #include "components/services/app_service/public/cpp/file_handler_info.h"
37 #include "content/public/test/browser_task_environment.h"
38 #include "extensions/browser/entry_info.h"
39 #include "extensions/browser/extension_prefs.h"
40 #include "extensions/browser/extension_registry.h"
41 #include "extensions/browser/extension_system.h"
42 #include "extensions/common/constants.h"
43 #include "extensions/common/extension_builder.h"
44 #include "extensions/common/manifest.h"
45 #include "google_apis/drive/drive_api_parser.h"
46 #include "net/base/escape.h"
47 #include "storage/browser/file_system/external_mount_points.h"
48 #include "testing/gtest/include/gtest/gtest.h"
49 #include "third_party/blink/public/common/features.h"
50 #include "url/gurl.h"
51 
52 using extensions::api::file_manager_private::Verb;
53 
54 namespace file_manager {
55 namespace file_tasks {
56 namespace {
57 
58 // Registers the default task preferences. Used for testing
59 // ChooseAndSetDefaultTask().
RegisterDefaultTaskPreferences(TestingPrefServiceSimple * pref_service)60 void RegisterDefaultTaskPreferences(TestingPrefServiceSimple* pref_service) {
61   DCHECK(pref_service);
62 
63   pref_service->registry()->RegisterDictionaryPref(
64       prefs::kDefaultTasksByMimeType);
65   pref_service->registry()->RegisterDictionaryPref(
66       prefs::kDefaultTasksBySuffix);
67 }
68 
69 // Updates the default task preferences per the given dictionary values. Used
70 // for testing ChooseAndSetDefaultTask.
UpdateDefaultTaskPreferences(TestingPrefServiceSimple * pref_service,const base::DictionaryValue & mime_types,const base::DictionaryValue & suffixes)71 void UpdateDefaultTaskPreferences(TestingPrefServiceSimple* pref_service,
72                                   const base::DictionaryValue& mime_types,
73                                   const base::DictionaryValue& suffixes) {
74   DCHECK(pref_service);
75 
76   pref_service->Set(prefs::kDefaultTasksByMimeType, mime_types);
77   pref_service->Set(prefs::kDefaultTasksBySuffix, suffixes);
78 }
79 
80 }  // namespace
81 
TEST(FileManagerFileTasksTest,FullTaskDescriptor_WithIconAndDefault)82 TEST(FileManagerFileTasksTest, FullTaskDescriptor_WithIconAndDefault) {
83   FullTaskDescriptor full_descriptor(
84       TaskDescriptor("app-id", TASK_TYPE_FILE_BROWSER_HANDLER, "action-id"),
85       "task title", Verb::VERB_OPEN_WITH, GURL("http://example.com/icon.png"),
86       true /* is_default */, false /* is_generic_file_handler */,
87       false /* is_file_extension_match */);
88 
89   const std::string task_id =
90       TaskDescriptorToId(full_descriptor.task_descriptor());
91   EXPECT_EQ("app-id|file|action-id", task_id);
92   EXPECT_EQ("http://example.com/icon.png", full_descriptor.icon_url().spec());
93   EXPECT_EQ("task title", full_descriptor.task_title());
94   EXPECT_EQ(Verb::VERB_OPEN_WITH, full_descriptor.task_verb());
95   EXPECT_TRUE(full_descriptor.is_default());
96 }
97 
TEST(FileManagerFileTasksTest,MakeTaskID)98 TEST(FileManagerFileTasksTest, MakeTaskID) {
99   EXPECT_EQ("app-id|file|action-id",
100             MakeTaskID("app-id", TASK_TYPE_FILE_BROWSER_HANDLER, "action-id"));
101   EXPECT_EQ("app-id|app|action-id",
102             MakeTaskID("app-id", TASK_TYPE_FILE_HANDLER, "action-id"));
103 }
104 
TEST(FileManagerFileTasksTest,TaskDescriptorToId)105 TEST(FileManagerFileTasksTest, TaskDescriptorToId) {
106   EXPECT_EQ("app-id|file|action-id",
107             TaskDescriptorToId(TaskDescriptor("app-id",
108                                               TASK_TYPE_FILE_BROWSER_HANDLER,
109                                               "action-id")));
110 }
111 
TEST(FileManagerFileTasksTest,ParseTaskID_FileBrowserHandler)112 TEST(FileManagerFileTasksTest, ParseTaskID_FileBrowserHandler) {
113   TaskDescriptor task;
114   EXPECT_TRUE(ParseTaskID("app-id|file|action-id", &task));
115   EXPECT_EQ("app-id", task.app_id);
116   EXPECT_EQ(TASK_TYPE_FILE_BROWSER_HANDLER, task.task_type);
117   EXPECT_EQ("action-id", task.action_id);
118 }
119 
TEST(FileManagerFileTasksTest,ParseTaskID_FileHandler)120 TEST(FileManagerFileTasksTest, ParseTaskID_FileHandler) {
121   TaskDescriptor task;
122   EXPECT_TRUE(ParseTaskID("app-id|app|action-id", &task));
123   EXPECT_EQ("app-id", task.app_id);
124   EXPECT_EQ(TASK_TYPE_FILE_HANDLER, task.task_type);
125   EXPECT_EQ("action-id", task.action_id);
126 }
127 
TEST(FileManagerFileTasksTest,ParseTaskID_Legacy)128 TEST(FileManagerFileTasksTest, ParseTaskID_Legacy) {
129   TaskDescriptor task;
130   // A legacy task ID only has two parts. The task type should be
131   // TASK_TYPE_FILE_BROWSER_HANDLER.
132   EXPECT_TRUE(ParseTaskID("app-id|action-id", &task));
133   EXPECT_EQ("app-id", task.app_id);
134   EXPECT_EQ(TASK_TYPE_FILE_BROWSER_HANDLER, task.task_type);
135   EXPECT_EQ("action-id", task.action_id);
136 }
137 
TEST(FileManagerFileTasksTest,ParseTaskID_Invalid)138 TEST(FileManagerFileTasksTest, ParseTaskID_Invalid) {
139   TaskDescriptor task;
140   EXPECT_FALSE(ParseTaskID("invalid", &task));
141 }
142 
TEST(FileManagerFileTasksTest,ParseTaskID_UnknownTaskType)143 TEST(FileManagerFileTasksTest, ParseTaskID_UnknownTaskType) {
144   TaskDescriptor task;
145   EXPECT_FALSE(ParseTaskID("app-id|unknown|action-id", &task));
146 }
147 
148 // Test that the right task is chosen from multiple choices per mime types
149 // and file extensions.
TEST(FileManagerFileTasksTest,ChooseAndSetDefaultTask_MultipleTasks)150 TEST(FileManagerFileTasksTest, ChooseAndSetDefaultTask_MultipleTasks) {
151   TestingPrefServiceSimple pref_service;
152   RegisterDefaultTaskPreferences(&pref_service);
153 
154   // Text.app and Nice.app were found for "foo.txt".
155   TaskDescriptor text_app_task("text-app-id",
156                                TASK_TYPE_FILE_HANDLER,
157                                "action-id");
158   TaskDescriptor nice_app_task("nice-app-id",
159                                TASK_TYPE_FILE_HANDLER,
160                                "action-id");
161   std::vector<FullTaskDescriptor> tasks;
162   tasks.emplace_back(
163       text_app_task, "Text.app", Verb::VERB_OPEN_WITH,
164       GURL("http://example.com/text_app.png"), false /* is_default */,
165       false /* is_generic_file_handler */, false /* is_file_extension_match */);
166   tasks.emplace_back(
167       nice_app_task, "Nice.app", Verb::VERB_ADD_TO,
168       GURL("http://example.com/nice_app.png"), false /* is_default */,
169       false /* is_generic_file_handler */, false /* is_file_extension_match */);
170   std::vector<extensions::EntryInfo> entries;
171   entries.emplace_back(base::FilePath::FromUTF8Unsafe("foo.txt"), "text/plain",
172                        false);
173 
174   // None of them should be chosen as default, as nothing is set in the
175   // preferences.
176   ChooseAndSetDefaultTask(pref_service, entries, &tasks);
177   EXPECT_FALSE(tasks[0].is_default());
178   EXPECT_FALSE(tasks[1].is_default());
179 
180   // Set Text.app as default for "text/plain" in the preferences.
181   base::DictionaryValue empty;
182   base::DictionaryValue mime_types;
183   mime_types.SetKey("text/plain",
184                     base::Value(TaskDescriptorToId(text_app_task)));
185   UpdateDefaultTaskPreferences(&pref_service, mime_types, empty);
186 
187   // Text.app should be chosen as default.
188   ChooseAndSetDefaultTask(pref_service, entries, &tasks);
189   EXPECT_TRUE(tasks[0].is_default());
190   EXPECT_FALSE(tasks[1].is_default());
191 
192   // Change it back to non-default for testing further.
193   tasks[0].set_is_default(false);
194 
195   // Clear the preferences and make sure none of them are default.
196   UpdateDefaultTaskPreferences(&pref_service, empty, empty);
197   ChooseAndSetDefaultTask(pref_service, entries, &tasks);
198   EXPECT_FALSE(tasks[0].is_default());
199   EXPECT_FALSE(tasks[1].is_default());
200 
201   // Set Nice.app as default for ".txt" in the preferences.
202   base::DictionaryValue suffixes;
203   suffixes.SetKey(".txt", base::Value(TaskDescriptorToId(nice_app_task)));
204   UpdateDefaultTaskPreferences(&pref_service, empty, suffixes);
205 
206   // Now Nice.app should be chosen as default.
207   ChooseAndSetDefaultTask(pref_service, entries, &tasks);
208   EXPECT_FALSE(tasks[0].is_default());
209   EXPECT_TRUE(tasks[1].is_default());
210 }
211 
212 // Test that internal file browser handler of the Files app is chosen as
213 // default even if nothing is set in the preferences.
TEST(FileManagerFileTasksTest,ChooseAndSetDefaultTask_FallbackFileBrowser)214 TEST(FileManagerFileTasksTest, ChooseAndSetDefaultTask_FallbackFileBrowser) {
215   TestingPrefServiceSimple pref_service;
216   RegisterDefaultTaskPreferences(&pref_service);
217 
218   // The internal file browser handler of the Files app was found for "foo.txt".
219   TaskDescriptor files_app_task(kFileManagerAppId,
220                                 TASK_TYPE_FILE_BROWSER_HANDLER,
221                                 "view-in-browser");
222   std::vector<FullTaskDescriptor> tasks;
223   tasks.emplace_back(
224       files_app_task, "View in browser", Verb::VERB_OPEN_WITH,
225       GURL("http://example.com/some_icon.png"), false /* is_default */,
226       false /* is_generic_file_handler */, false /* is_file_extension_match */);
227   std::vector<extensions::EntryInfo> entries;
228   entries.emplace_back(base::FilePath::FromUTF8Unsafe("foo.txt"), "text/plain",
229                        false);
230 
231   // The internal file browser handler should be chosen as default, as it's a
232   // fallback file browser handler.
233   ChooseAndSetDefaultTask(pref_service, entries, &tasks);
234   EXPECT_TRUE(tasks[0].is_default());
235 }
236 
237 // Test that Text.app is chosen as default instead of the Files app
238 // even if nothing is set in the preferences.
TEST(FileManagerFileTasksTest,ChooseAndSetDefaultTask_FallbackTextApp)239 TEST(FileManagerFileTasksTest, ChooseAndSetDefaultTask_FallbackTextApp) {
240   TestingPrefServiceSimple pref_service;
241   RegisterDefaultTaskPreferences(&pref_service);
242 
243   // Define the browser handler of the Files app for "foo.txt".
244   TaskDescriptor files_app_task(
245       kFileManagerAppId, TASK_TYPE_FILE_BROWSER_HANDLER, "view-in-browser");
246   // Define the text editor app for "foo.txt".
247   TaskDescriptor text_app_task(kTextEditorAppId, TASK_TYPE_FILE_HANDLER,
248                                "Text");
249   std::vector<FullTaskDescriptor> tasks;
250   tasks.emplace_back(
251       files_app_task, "View in browser", Verb::VERB_OPEN_WITH,
252       GURL("http://example.com/some_icon.png"), false /* is_default */,
253       false /* is_generic_file_handler */, false /* is_file_extension_match */);
254   tasks.emplace_back(
255       text_app_task, "Text", Verb::VERB_OPEN_WITH,
256       GURL("chrome://extension-icon/mmfbcljfglbokpmkimbfghdkjmjhdgbg/16/1"),
257       false /* is_default */, false /* is_generic_file_handler */,
258       false /* is_file_extension_match */);
259   std::vector<extensions::EntryInfo> entries;
260   entries.emplace_back(base::FilePath::FromUTF8Unsafe("foo.txt"), "text/plain",
261                        false);
262 
263   // The text editor app should be chosen as default, as it's a fallback file
264   // browser handler.
265   ChooseAndSetDefaultTask(pref_service, entries, &tasks);
266   EXPECT_TRUE(tasks[1].is_default());
267 }
268 
269 // Test that browser is chosen as default for HTML files instead of the Text
270 // app even if nothing is set in the preferences.
TEST(FileManagerFileTasksTest,ChooseAndSetDefaultTask_FallbackHtmlTextApp)271 TEST(FileManagerFileTasksTest, ChooseAndSetDefaultTask_FallbackHtmlTextApp) {
272   TestingPrefServiceSimple pref_service;
273   RegisterDefaultTaskPreferences(&pref_service);
274 
275   // Define the browser handler of the Files app for "foo.html".
276   TaskDescriptor files_app_task(
277       kFileManagerAppId, TASK_TYPE_FILE_BROWSER_HANDLER, "view-in-browser");
278   // Define the text editor app for "foo.html".
279   TaskDescriptor text_app_task(kTextEditorAppId, TASK_TYPE_FILE_HANDLER,
280                                "Text");
281   std::vector<FullTaskDescriptor> tasks;
282   tasks.emplace_back(
283       files_app_task, "View in browser", Verb::VERB_OPEN_WITH,
284       GURL("http://example.com/some_icon.png"), false /* is_default */,
285       false /* is_generic_file_handler */, false /* is_file_extension_match */);
286   tasks.emplace_back(
287       text_app_task, "Text", Verb::VERB_OPEN_WITH,
288       GURL("chrome://extension-icon/mmfbcljfglbokpmkimbfghdkjmjhdgbg/16/1"),
289       false /* is_default */, false /* is_generic_file_handler */,
290       false /* is_file_extension_match */);
291   std::vector<extensions::EntryInfo> entries;
292   entries.emplace_back(base::FilePath::FromUTF8Unsafe("foo.html"), "text/html",
293                        false);
294 
295   // The internal file browser handler should be chosen as default,
296   // as it's a fallback file browser handler.
297   ChooseAndSetDefaultTask(pref_service, entries, &tasks);
298   EXPECT_TRUE(tasks[0].is_default());
299 }
300 
301 // Test that Audio Player is chosen as default even if nothing is set in the
302 // preferences.
TEST(FileManagerFileTasksTest,ChooseAndSetDefaultTask_FallbackAudioPlayer)303 TEST(FileManagerFileTasksTest, ChooseAndSetDefaultTask_FallbackAudioPlayer) {
304   TestingPrefServiceSimple pref_service;
305   RegisterDefaultTaskPreferences(&pref_service);
306 
307   // The Audio Player app was found for "sound.wav".
308   TaskDescriptor files_app_task(kAudioPlayerAppId, TASK_TYPE_FILE_HANDLER,
309                                 "Audio Player");
310   std::vector<FullTaskDescriptor> tasks;
311   tasks.emplace_back(
312       files_app_task, "Audio Player", Verb::VERB_OPEN_WITH,
313       GURL("chrome://extension-icon/cjbfomnbifhcdnihkgipgfcihmgjfhbf/32/1"),
314       false /* is_default */, false /* is_generic_file_handler */,
315       false /* is_file_extension_match */);
316   std::vector<extensions::EntryInfo> entries;
317   entries.emplace_back(base::FilePath::FromUTF8Unsafe("sound.wav"), "audio/wav",
318                        false);
319 
320   // The Audio Player app should be chosen as default, as it's a fallback file
321   // browser handler.
322   ChooseAndSetDefaultTask(pref_service, entries, &tasks);
323   EXPECT_TRUE(tasks[0].is_default());
324 }
325 
326 // Test that Office Editing is chosen as default even if nothing is set in the
327 // preferences.
TEST(FileManagerFileTasksTest,ChooseAndSetDefaultTask_FallbackOfficeEditing)328 TEST(FileManagerFileTasksTest, ChooseAndSetDefaultTask_FallbackOfficeEditing) {
329   TestingPrefServiceSimple pref_service;
330   RegisterDefaultTaskPreferences(&pref_service);
331 
332   // The Office Editing app was found for "slides.pptx".
333   TaskDescriptor files_app_task(
334       extension_misc::kQuickOfficeComponentExtensionId, TASK_TYPE_FILE_HANDLER,
335       "Office Editing for Docs, Sheets & Slides");
336   std::vector<FullTaskDescriptor> tasks;
337   tasks.emplace_back(
338       files_app_task, "Office Editing for Docs, Sheets & Slides",
339       Verb::VERB_OPEN_WITH,
340       GURL("chrome://extension-icon/bpmcpldpdmajfigpchkicefoigmkfalc/32/1"),
341       false /* is_default */, false /* is_generic_file_handler */,
342       false /* is_file_extension_match */);
343   std::vector<extensions::EntryInfo> entries;
344   entries.emplace_back(base::FilePath::FromUTF8Unsafe("slides.pptx"), "",
345                        false);
346 
347   // The Office Editing app should be chosen as default, as it's a fallback
348   // file browser handler.
349   ChooseAndSetDefaultTask(pref_service, entries, &tasks);
350   EXPECT_TRUE(tasks[0].is_default());
351 }
352 
353 // Test IsFileHandlerEnabled which returns whether a file handler should be
354 // used.
TEST(FileManagerFileTasksTest,IsFileHandlerEnabled)355 TEST(FileManagerFileTasksTest, IsFileHandlerEnabled) {
356   content::BrowserTaskEnvironment task_environment;
357   TestingProfile test_profile;
358   crostini::FakeCrostiniFeatures crostini_features;
359 
360   apps::FileHandlerInfo test_handler;
361   test_handler.id = "test";
362 
363   // Test import-crostini-image.
364   apps::FileHandlerInfo crostini_import_handler;
365   crostini_import_handler.id = "import-crostini-image";
366   crostini_features.set_export_import_ui_allowed(true);
367   EXPECT_TRUE(IsFileHandlerEnabled(&test_profile, crostini_import_handler));
368   EXPECT_TRUE(IsFileHandlerEnabled(&test_profile, test_handler));
369 
370   crostini_features.set_export_import_ui_allowed(false);
371   EXPECT_FALSE(IsFileHandlerEnabled(&test_profile, crostini_import_handler));
372   EXPECT_TRUE(IsFileHandlerEnabled(&test_profile, test_handler));
373 
374   // Test install-linux-package.
375   apps::FileHandlerInfo install_linux_handler;
376   install_linux_handler.id = "install-linux-package";
377   crostini_features.set_root_access_allowed(true);
378   EXPECT_TRUE(IsFileHandlerEnabled(&test_profile, install_linux_handler));
379   EXPECT_TRUE(IsFileHandlerEnabled(&test_profile, test_handler));
380 
381   crostini_features.set_root_access_allowed(false);
382   EXPECT_FALSE(IsFileHandlerEnabled(&test_profile, install_linux_handler));
383   EXPECT_TRUE(IsFileHandlerEnabled(&test_profile, test_handler));
384 }
385 
386 // Test IsGoodMatchFileHandler which returns whether a file handle info matches
387 // with files as good match or not.
TEST(FileManagerFileTasksTest,IsGoodMatchFileHandler)388 TEST(FileManagerFileTasksTest, IsGoodMatchFileHandler) {
389   using FileHandlerInfo = apps::FileHandlerInfo;
390 
391   std::vector<extensions::EntryInfo> entries_1;
392   entries_1.emplace_back(base::FilePath(FILE_PATH_LITERAL("foo.jpg")),
393                          "image/jpeg", false);
394   entries_1.emplace_back(base::FilePath(FILE_PATH_LITERAL("bar.txt")),
395                          "text/plain", false);
396 
397   std::vector<extensions::EntryInfo> entries_2;
398   entries_2.emplace_back(base::FilePath(FILE_PATH_LITERAL("foo.ics")),
399                          "text/calendar", false);
400 
401   // extensions: ["*"]
402   FileHandlerInfo file_handler_info_1;
403   file_handler_info_1.extensions.insert("*");
404   EXPECT_FALSE(IsGoodMatchFileHandler(file_handler_info_1, entries_1));
405 
406   // extensions: ["*", "jpg"]
407   FileHandlerInfo file_handler_info_2;
408   file_handler_info_2.extensions.insert("*");
409   file_handler_info_2.extensions.insert("jpg");
410   EXPECT_FALSE(IsGoodMatchFileHandler(file_handler_info_2, entries_1));
411 
412   // extensions: ["jpg"]
413   FileHandlerInfo file_handler_info_3;
414   file_handler_info_3.extensions.insert("jpg");
415   EXPECT_TRUE(IsGoodMatchFileHandler(file_handler_info_3, entries_1));
416 
417   // types: ["*"]
418   FileHandlerInfo file_handler_info_4;
419   file_handler_info_4.types.insert("*");
420   EXPECT_FALSE(IsGoodMatchFileHandler(file_handler_info_4, entries_1));
421 
422   // types: ["*/*"]
423   FileHandlerInfo file_handler_info_5;
424   file_handler_info_5.types.insert("*/*");
425   EXPECT_FALSE(IsGoodMatchFileHandler(file_handler_info_5, entries_1));
426 
427   // types: ["image/*"]
428   FileHandlerInfo file_handler_info_6;
429   file_handler_info_6.types.insert("image/*");
430   // Partial wild card is not generic.
431   EXPECT_TRUE(IsGoodMatchFileHandler(file_handler_info_6, entries_1));
432 
433   // types: ["*", "image/*"]
434   FileHandlerInfo file_handler_info_7;
435   file_handler_info_7.types.insert("*");
436   file_handler_info_7.types.insert("image/*");
437   EXPECT_FALSE(IsGoodMatchFileHandler(file_handler_info_7, entries_1));
438 
439   // extensions: ["*"], types: ["image/*"]
440   FileHandlerInfo file_handler_info_8;
441   file_handler_info_8.extensions.insert("*");
442   file_handler_info_8.types.insert("image/*");
443   EXPECT_FALSE(IsGoodMatchFileHandler(file_handler_info_8, entries_1));
444 
445   // types: ["text/*"] and target files contain unsupported text mime type, e.g.
446   // text/calendar.
447   FileHandlerInfo file_handler_info_9;
448   file_handler_info_9.types.insert("text/*");
449   EXPECT_FALSE(IsGoodMatchFileHandler(file_handler_info_9, entries_2));
450 
451   // types: ["text/*"] and target files don't contain unsupported text mime
452   // type.
453   FileHandlerInfo file_handler_info_10;
454   file_handler_info_10.types.insert("text/*");
455   EXPECT_TRUE(IsGoodMatchFileHandler(file_handler_info_10, entries_1));
456 
457   // path_directory_set not empty.
458   FileHandlerInfo file_handler_info_11;
459   std::vector<extensions::EntryInfo> entries_3;
460   entries_3.emplace_back(base::FilePath(FILE_PATH_LITERAL("dir1")), "", true);
461   EXPECT_FALSE(IsGoodMatchFileHandler(file_handler_info_11, entries_3));
462 }
463 
464 // Test IsGoodMatchAppsFileHandler, which returns whether an apps::FileHandler
465 // is capable of handling all of a set of files.
TEST(FileManagerFileTasksTest,IsGoodMatchAppsFileHandler)466 TEST(FileManagerFileTasksTest, IsGoodMatchAppsFileHandler) {
467   std::vector<extensions::EntryInfo> entries_1;
468   entries_1.emplace_back(base::FilePath(FILE_PATH_LITERAL("foo.jpg")),
469                          "image/jpeg", false);
470   entries_1.emplace_back(base::FilePath(FILE_PATH_LITERAL("bar.txt")),
471                          "text/plain", false);
472 
473   std::vector<extensions::EntryInfo> entries_2;
474   entries_2.emplace_back(base::FilePath(FILE_PATH_LITERAL("foo.ics")),
475                          "text/calendar", false);
476 
477   // file_extensions: ["*"]
478   {
479     apps::FileHandler file_handler;
480     apps::FileHandler::AcceptEntry accept_entry;
481     accept_entry.file_extensions.insert("*");
482     file_handler.accept.push_back(accept_entry);
483     EXPECT_FALSE(IsGoodMatchAppsFileHandler(file_handler, entries_1));
484   }
485 
486   // file_extensions: ["*", ".jpg"]
487   {
488     apps::FileHandler file_handler;
489     apps::FileHandler::AcceptEntry accept_entry;
490     accept_entry.file_extensions.insert("*");
491     accept_entry.file_extensions.insert(".jpg");
492     file_handler.accept.push_back(accept_entry);
493     EXPECT_FALSE(IsGoodMatchAppsFileHandler(file_handler, entries_1));
494   }
495 
496   // file_extensions: [".jpg"]
497   {
498     apps::FileHandler file_handler;
499     apps::FileHandler::AcceptEntry accept_entry;
500     accept_entry.file_extensions.insert(".jpg");
501     file_handler.accept.push_back(accept_entry);
502     EXPECT_TRUE(IsGoodMatchAppsFileHandler(file_handler, entries_1));
503   }
504 
505   // mime_type: "*"
506   {
507     apps::FileHandler file_handler;
508     apps::FileHandler::AcceptEntry accept_entry;
509     accept_entry.mime_type = "*";
510     file_handler.accept.push_back(accept_entry);
511     EXPECT_FALSE(IsGoodMatchAppsFileHandler(file_handler, entries_1));
512   }
513 
514   // mime_type: "*/*"
515   {
516     apps::FileHandler file_handler;
517     apps::FileHandler::AcceptEntry accept_entry;
518     accept_entry.mime_type = "*/*";
519     file_handler.accept.push_back(accept_entry);
520     EXPECT_FALSE(IsGoodMatchAppsFileHandler(file_handler, entries_1));
521   }
522 
523   // mime_type: "image/*"
524   {
525     apps::FileHandler file_handler;
526     apps::FileHandler::AcceptEntry accept_entry;
527     accept_entry.mime_type = "image/*";
528     file_handler.accept.push_back(accept_entry);
529     // Partial wild card is not generic.
530     EXPECT_TRUE(IsGoodMatchAppsFileHandler(file_handler, entries_1));
531   }
532 
533   // mime_type: "*" and "image/*"
534   {
535     apps::FileHandler file_handler;
536     apps::FileHandler::AcceptEntry accept_entry_1;
537     accept_entry_1.mime_type = "*";
538     file_handler.accept.push_back(accept_entry_1);
539     apps::FileHandler::AcceptEntry accept_entry_2;
540     accept_entry_2.mime_type = "image/*";
541     file_handler.accept.push_back(accept_entry_2);
542     EXPECT_FALSE(IsGoodMatchAppsFileHandler(file_handler, entries_1));
543   }
544 
545   // file_extensions: ["*"], mime_type: ["image/*"]
546   {
547     apps::FileHandler file_handler;
548     apps::FileHandler::AcceptEntry accept_entry;
549     accept_entry.mime_type = "image/*";
550     accept_entry.file_extensions.insert("*");
551     file_handler.accept.push_back(accept_entry);
552     EXPECT_FALSE(IsGoodMatchAppsFileHandler(file_handler, entries_1));
553   }
554 
555   // mime_type: "text/*" and target files contain unsupported text MIME type
556   // (e.g. text/calendar).
557   {
558     apps::FileHandler file_handler;
559     apps::FileHandler::AcceptEntry accept_entry;
560     accept_entry.mime_type = "text/*";
561     file_handler.accept.push_back(accept_entry);
562     EXPECT_FALSE(IsGoodMatchAppsFileHandler(file_handler, entries_2));
563   }
564 
565   // mime_type: "text/*" and target files don't contain unsupported text MIME
566   // type.
567   {
568     apps::FileHandler file_handler;
569     apps::FileHandler::AcceptEntry accept_entry;
570     accept_entry.mime_type = "text/*";
571     file_handler.accept.push_back(accept_entry);
572     EXPECT_TRUE(IsGoodMatchAppsFileHandler(file_handler, entries_1));
573   }
574 
575   // path_directory_set not empty.
576   {
577     apps::FileHandler file_handler;
578     std::vector<extensions::EntryInfo> entries_3;
579     entries_3.emplace_back(base::FilePath(FILE_PATH_LITERAL("dir1")), "", true);
580     EXPECT_FALSE(IsGoodMatchAppsFileHandler(file_handler, entries_3));
581   }
582 }
583 
584 // Test using the test extension system, which needs lots of setup.
585 class FileManagerFileTasksComplexTest : public testing::Test {
586  protected:
FileManagerFileTasksComplexTest()587   FileManagerFileTasksComplexTest()
588       : command_line_(base::CommandLine::NO_PROGRAM),
589         extension_service_(nullptr) {
590     extensions::TestExtensionSystem* test_extension_system =
591         static_cast<extensions::TestExtensionSystem*>(
592             extensions::ExtensionSystem::Get(&test_profile_));
593     extension_service_ = test_extension_system->CreateExtensionService(
594         &command_line_,
595         base::FilePath()  /* install_directory */,
596         false  /* autoupdate_enabled*/);
597   }
598 
599   // Helper class for calling FindAllTypesOfTask synchronously.
600   class FindAllTypesOfTasksSynchronousWrapper {
601    public:
Call(Profile * profile,const std::vector<extensions::EntryInfo> & entries,const std::vector<GURL> & file_urls,std::vector<FullTaskDescriptor> * result)602     void Call(Profile* profile,
603               const std::vector<extensions::EntryInfo>& entries,
604               const std::vector<GURL>& file_urls,
605               std::vector<FullTaskDescriptor>* result) {
606       FindAllTypesOfTasks(
607           profile, entries, file_urls,
608           base::BindOnce(&FindAllTypesOfTasksSynchronousWrapper::OnReply,
609                          base::Unretained(this), result));
610       run_loop_.Run();
611     }
612 
613    private:
OnReply(std::vector<FullTaskDescriptor> * out,std::unique_ptr<std::vector<FullTaskDescriptor>> result)614     void OnReply(std::vector<FullTaskDescriptor>* out,
615                  std::unique_ptr<std::vector<FullTaskDescriptor>> result) {
616       *out = *result;
617       run_loop_.Quit();
618     }
619 
620     base::RunLoop run_loop_;
621   };
622 
623   content::BrowserTaskEnvironment task_environment_;
624   chromeos::ScopedCrosSettingsTestHelper cros_settings_test_helper_;
625   chromeos::ScopedTestUserManager test_user_manager_;
626   TestingProfile test_profile_;
627   base::CommandLine command_line_;
628   extensions::ExtensionService* extension_service_;  // Owned by test_profile_;
629 };
630 
TEST_F(FileManagerFileTasksComplexTest,FindFileHandlerTasks)631 TEST_F(FileManagerFileTasksComplexTest, FindFileHandlerTasks) {
632   // Random IDs generated by
633   // % ruby -le 'print (0...32).to_a.map{(?a + rand(16)).chr}.join'
634   const char kFooId[] = "hhgbjpmdppecanaaogonaigmmifgpaph";
635   const char kBarId[] = "odlhccgofgkadkkhcmhgnhgahonahoca";
636 
637   // Foo.app can handle "text/plain" and "text/html".
638   extensions::ExtensionBuilder foo_app;
639   foo_app.SetManifest(
640       extensions::DictionaryBuilder()
641           .Set("name", "Foo")
642           .Set("version", "1.0.0")
643           .Set("manifest_version", 2)
644           .Set("app", extensions::DictionaryBuilder()
645                           .Set("background",
646                                extensions::DictionaryBuilder()
647                                    .Set("scripts", extensions::ListBuilder()
648                                                        .Append("background.js")
649                                                        .Build())
650                                    .Build())
651                           .Build())
652           .Set("file_handlers",
653                extensions::DictionaryBuilder()
654                    .Set("text", extensions::DictionaryBuilder()
655                                     .Set("title", "Text")
656                                     .Set("types", extensions::ListBuilder()
657                                                       .Append("text/plain")
658                                                       .Append("text/html")
659                                                       .Build())
660                                     .Build())
661                    .Build())
662           .Build());
663   foo_app.SetID(kFooId);
664   extension_service_->AddExtension(foo_app.Build().get());
665 
666   // Bar.app can only handle "text/plain".
667   extensions::ExtensionBuilder bar_app;
668   bar_app.SetManifest(
669       extensions::DictionaryBuilder()
670           .Set("name", "Bar")
671           .Set("version", "1.0.0")
672           .Set("manifest_version", 2)
673           .Set("app", extensions::DictionaryBuilder()
674                           .Set("background",
675                                extensions::DictionaryBuilder()
676                                    .Set("scripts", extensions::ListBuilder()
677                                                        .Append("background.js")
678                                                        .Build())
679                                    .Build())
680                           .Build())
681           .Set("file_handlers",
682                extensions::DictionaryBuilder()
683                    .Set("text", extensions::DictionaryBuilder()
684                                     .Set("title", "Text")
685                                     .Set("types", extensions::ListBuilder()
686                                                       .Append("text/plain")
687                                                       .Build())
688                                     .Build())
689                    .Build())
690           .Build());
691   bar_app.SetID(kBarId);
692   extension_service_->AddExtension(bar_app.Build().get());
693 
694   // Find apps for a "text/plain" file. Foo.app and Bar.app should be found.
695   std::vector<extensions::EntryInfo> entries;
696   entries.emplace_back(
697       util::GetMyFilesFolderForProfile(&test_profile_).AppendASCII("foo.txt"),
698       "text/plain", false);
699 
700   std::vector<FullTaskDescriptor> tasks;
701   FindFileHandlerTasks(&test_profile_, entries, &tasks);
702   ASSERT_EQ(2U, tasks.size());
703   // Sort the app IDs, as the order is not guaranteed.
704   std::vector<std::string> app_ids;
705   app_ids.push_back(tasks[0].task_descriptor().app_id);
706   app_ids.push_back(tasks[1].task_descriptor().app_id);
707   std::sort(app_ids.begin(), app_ids.end());
708   // Confirm that both Foo.app and Bar.app are found.
709   EXPECT_EQ(kFooId, app_ids[0]);
710   EXPECT_EQ(kBarId, app_ids[1]);
711 
712   // Find apps for "text/plain" and "text/html" files. Only Foo.app should be
713   // found.
714   entries.clear();
715   entries.emplace_back(
716       util::GetMyFilesFolderForProfile(&test_profile_).AppendASCII("foo.txt"),
717       "text/plain", false);
718   entries.emplace_back(
719       util::GetMyFilesFolderForProfile(&test_profile_).AppendASCII("foo.html"),
720       "text/html", false);
721   tasks.clear();
722   FindFileHandlerTasks(&test_profile_, entries, &tasks);
723   ASSERT_EQ(1U, tasks.size());
724   // Confirm that only Foo.app is found.
725   EXPECT_EQ(kFooId, tasks[0].task_descriptor().app_id);
726 
727   // Add an "image/png" file. No tasks should be found.
728   entries.emplace_back(base::FilePath::FromUTF8Unsafe("foo.png"), "image/png",
729                        false);
730   tasks.clear();
731   FindFileHandlerTasks(&test_profile_, entries, &tasks);
732   // Confirm no tasks are found.
733   ASSERT_TRUE(tasks.empty());
734 }
735 
TEST_F(FileManagerFileTasksComplexTest,BookmarkAppsAreNotListedInFileHandlerTasks)736 TEST_F(FileManagerFileTasksComplexTest,
737        BookmarkAppsAreNotListedInFileHandlerTasks) {
738   const char kGraphrId[] = "ppcpljkgngnngojbghcdiojhbneibgdg";
739   const char kGraphrFileAction[] = "https://graphr.tld/open-files/?name=raw";
740   extensions::ExtensionBuilder graphr;
741   graphr.SetManifest(
742       extensions::DictionaryBuilder()
743           .Set("name", "Graphr")
744           .Set("version", "1.0.0")
745           .Set("manifest_version", 2)
746           .Set("app",
747                extensions::DictionaryBuilder()
748                    .Set("launch", extensions::DictionaryBuilder()
749                                       .Set("web_url", "https://graphr.tld")
750                                       .Build())
751                    .Build())
752           .Set(
753               "file_handlers",
754               extensions::DictionaryBuilder()
755                   .Set(kGraphrFileAction,
756                        extensions::DictionaryBuilder()
757                            .Set("title", "Raw")
758                            .Set("types", extensions::ListBuilder()
759                                              .Append("text/csv")
760                                              .Build())
761                            .Set("extensions",
762                                 extensions::ListBuilder().Append("csv").Build())
763                            .Build())
764                   .Build())
765           .Build());
766   graphr.SetID(kGraphrId);
767   graphr.AddFlags(extensions::Extension::InitFromValueFlags::FROM_BOOKMARK);
768 
769   extension_service_->AddExtension(graphr.Build().get());
770   extensions::ExtensionRegistry* registry =
771       extensions::ExtensionRegistry::Get(&test_profile_);
772   const extensions::Extension* extension = registry->GetExtensionById(
773       kGraphrId, extensions::ExtensionRegistry::ENABLED);
774 
775   ASSERT_EQ(extension->GetType(), extensions::Manifest::Type::TYPE_HOSTED_APP);
776   ASSERT_TRUE(extension->from_bookmark());
777 
778   std::vector<FullTaskDescriptor> tasks;
779   std::vector<extensions::EntryInfo> entries;
780   entries.emplace_back(
781       util::GetMyFilesFolderForProfile(&test_profile_).AppendASCII("foo.csv"),
782       "text/csv", false);
783 
784   base::test::ScopedFeatureList scoped_feature_list;
785   scoped_feature_list.InitWithFeatures({blink::features::kFileHandlingAPI}, {});
786   FindFileHandlerTasks(&test_profile_, entries, &tasks);
787   EXPECT_EQ(0u, tasks.size());
788 }
789 
790 // The basic logic is similar to a test case for FindFileHandlerTasks above.
TEST_F(FileManagerFileTasksComplexTest,FindFileBrowserHandlerTasks)791 TEST_F(FileManagerFileTasksComplexTest, FindFileBrowserHandlerTasks) {
792   // Copied from FindFileHandlerTasks test above.
793   const char kFooId[] = "hhgbjpmdppecanaaogonaigmmifgpaph";
794   const char kBarId[] = "odlhccgofgkadkkhcmhgnhgahonahoca";
795 
796   // Foo.app can handle ".txt" and ".html".
797   // This one is an extension, and has "file_browser_handlers"
798   extensions::ExtensionBuilder foo_app;
799   foo_app.SetManifest(
800       extensions::DictionaryBuilder()
801           .Set("name", "Foo")
802           .Set("version", "1.0.0")
803           .Set("manifest_version", 2)
804           .Set("permissions",
805                extensions::ListBuilder().Append("fileBrowserHandler").Build())
806           .Set("file_browser_handlers",
807                extensions::ListBuilder()
808                    .Append(
809                        extensions::DictionaryBuilder()
810                            .Set("id", "open")
811                            .Set("default_title", "open")
812                            .Set("file_filters", extensions::ListBuilder()
813                                                     .Append("filesystem:*.txt")
814                                                     .Append("filesystem:*.html")
815                                                     .Build())
816                            .Build())
817                    .Build())
818           .Build());
819   foo_app.SetID(kFooId);
820   extension_service_->AddExtension(foo_app.Build().get());
821 
822   // Bar.app can only handle ".txt".
823   extensions::ExtensionBuilder bar_app;
824   bar_app.SetManifest(
825       extensions::DictionaryBuilder()
826           .Set("name", "Bar")
827           .Set("version", "1.0.0")
828           .Set("manifest_version", 2)
829           .Set("permissions",
830                extensions::ListBuilder().Append("fileBrowserHandler").Build())
831           .Set("file_browser_handlers",
832                extensions::ListBuilder()
833                    .Append(
834                        extensions::DictionaryBuilder()
835                            .Set("id", "open")
836                            .Set("default_title", "open")
837                            .Set("file_filters", extensions::ListBuilder()
838                                                     .Append("filesystem:*.txt")
839                                                     .Build())
840                            .Build())
841                    .Build())
842           .Build());
843   bar_app.SetID(kBarId);
844   extension_service_->AddExtension(bar_app.Build().get());
845 
846   // Find apps for a ".txt" file. Foo.app and Bar.app should be found.
847   std::vector<GURL> file_urls;
848   file_urls.emplace_back("filesystem:chrome-extension://id/dir/foo.txt");
849 
850   std::vector<FullTaskDescriptor> tasks;
851   FindFileBrowserHandlerTasks(&test_profile_, file_urls, &tasks);
852   ASSERT_EQ(2U, tasks.size());
853   // Sort the app IDs, as the order is not guaranteed.
854   std::vector<std::string> app_ids;
855   app_ids.push_back(tasks[0].task_descriptor().app_id);
856   app_ids.push_back(tasks[1].task_descriptor().app_id);
857   std::sort(app_ids.begin(), app_ids.end());
858   // Confirm that both Foo.app and Bar.app are found.
859   EXPECT_EQ(kFooId, app_ids[0]);
860   EXPECT_EQ(kBarId, app_ids[1]);
861 
862   // Find apps for ".txt" and ".html" files. Only Foo.app should be found.
863   file_urls.clear();
864   file_urls.emplace_back("filesystem:chrome-extension://id/dir/foo.txt");
865   file_urls.emplace_back("filesystem:chrome-extension://id/dir/foo.html");
866   tasks.clear();
867   FindFileBrowserHandlerTasks(&test_profile_, file_urls, &tasks);
868   ASSERT_EQ(1U, tasks.size());
869   // Confirm that only Foo.app is found.
870   EXPECT_EQ(kFooId, tasks[0].task_descriptor().app_id);
871 
872   // Add an ".png" file. No tasks should be found.
873   file_urls.emplace_back("filesystem:chrome-extension://id/dir/foo.png");
874   tasks.clear();
875   FindFileBrowserHandlerTasks(&test_profile_, file_urls, &tasks);
876   // Confirm no tasks are found.
877   ASSERT_TRUE(tasks.empty());
878 }
879 
880 // Test that all kinds of apps (file handler and file browser handler) are
881 // returned.
TEST_F(FileManagerFileTasksComplexTest,FindAllTypesOfTasks)882 TEST_F(FileManagerFileTasksComplexTest, FindAllTypesOfTasks) {
883   // kFooId and kBarId copied from FindFileHandlerTasks test above.
884   const char kFooId[] = "hhgbjpmdppecanaaogonaigmmifgpaph";
885   const char kBarId[] = "odlhccgofgkadkkhcmhgnhgahonahoca";
886 
887   // Foo.app can handle "text/plain".
888   // This is a packaged app (file handler).
889   extensions::ExtensionBuilder foo_app;
890   foo_app.SetManifest(
891       extensions::DictionaryBuilder()
892           .Set("name", "Foo")
893           .Set("version", "1.0.0")
894           .Set("manifest_version", 2)
895           .Set("app", extensions::DictionaryBuilder()
896                           .Set("background",
897                                extensions::DictionaryBuilder()
898                                    .Set("scripts", extensions::ListBuilder()
899                                                        .Append("background.js")
900                                                        .Build())
901                                    .Build())
902                           .Build())
903           .Set("file_handlers",
904                extensions::DictionaryBuilder()
905                    .Set("text", extensions::DictionaryBuilder()
906                                     .Set("title", "Text")
907                                     .Set("types", extensions::ListBuilder()
908                                                       .Append("text/plain")
909                                                       .Build())
910                                     .Build())
911                    .Build())
912           .Build());
913   foo_app.SetID(kFooId);
914   extension_service_->AddExtension(foo_app.Build().get());
915 
916   // Bar.app can only handle ".txt".
917   // This is an extension (file browser handler).
918   extensions::ExtensionBuilder bar_app;
919   bar_app.SetManifest(
920       extensions::DictionaryBuilder()
921           .Set("name", "Bar")
922           .Set("version", "1.0.0")
923           .Set("manifest_version", 2)
924           .Set("permissions",
925                extensions::ListBuilder().Append("fileBrowserHandler").Build())
926           .Set("file_browser_handlers",
927                extensions::ListBuilder()
928                    .Append(
929                        extensions::DictionaryBuilder()
930                            .Set("id", "open")
931                            .Set("default_title", "open")
932                            .Set("file_filters", extensions::ListBuilder()
933                                                     .Append("filesystem:*.txt")
934                                                     .Build())
935                            .Build())
936                    .Build())
937           .Build());
938   bar_app.SetID(kBarId);
939   extension_service_->AddExtension(bar_app.Build().get());
940 
941   // Find apps for "foo.txt". All apps should be found.
942   std::vector<extensions::EntryInfo> entries;
943   std::vector<GURL> file_urls;
944   entries.emplace_back(
945       util::GetMyFilesFolderForProfile(&test_profile_).AppendASCII("foo.txt"),
946       "text/plain", false);
947   file_urls.emplace_back("filesystem:chrome-extension://id/dir/foo.txt");
948 
949   std::vector<FullTaskDescriptor> tasks;
950   FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, entries,
951                                                file_urls, &tasks);
952   ASSERT_EQ(2U, tasks.size());
953 
954   // Sort the app IDs, as the order is not guaranteed.
955   std::vector<std::string> app_ids;
956   app_ids.push_back(tasks[0].task_descriptor().app_id);
957   app_ids.push_back(tasks[1].task_descriptor().app_id);
958   std::sort(app_ids.begin(), app_ids.end());
959   // Confirm that all apps are found.
960   EXPECT_EQ(kFooId, app_ids[0]);
961   EXPECT_EQ(kBarId, app_ids[1]);
962 }
963 
TEST_F(FileManagerFileTasksComplexTest,FindAllTypesOfTasks_GoogleDocument)964 TEST_F(FileManagerFileTasksComplexTest, FindAllTypesOfTasks_GoogleDocument) {
965   // kFooId and kBarId copied from FindFileHandlerTasks test above.
966   const char kBarId[] = "odlhccgofgkadkkhcmhgnhgahonahoca";
967 
968   // Bar.app can handle ".gdoc" files.
969   // This is an extension (file browser handler).
970   extensions::ExtensionBuilder bar_app;
971   bar_app.SetManifest(
972       extensions::DictionaryBuilder()
973           .Set("name", "Bar")
974           .Set("version", "1.0.0")
975           .Set("manifest_version", 2)
976           .Set("permissions",
977                extensions::ListBuilder().Append("fileBrowserHandler").Build())
978           .Set("file_browser_handlers",
979                extensions::ListBuilder()
980                    .Append(
981                        extensions::DictionaryBuilder()
982                            .Set("id", "open")
983                            .Set("default_title", "open")
984                            .Set("file_filters", extensions::ListBuilder()
985                                                     .Append("filesystem:*.gdoc")
986                                                     .Build())
987                            .Build())
988                    .Build())
989           .Build());
990   bar_app.SetID(kBarId);
991   extension_service_->AddExtension(bar_app.Build().get());
992 
993   // The Files app can handle ".gdoc" files.
994   // The ID "kFileManagerAppId" used here is precisely the one that identifies
995   // the Chrome OS Files app application.
996   extensions::ExtensionBuilder files_app;
997   files_app.SetManifest(
998       extensions::DictionaryBuilder()
999           .Set("name", "Files")
1000           .Set("version", "1.0.0")
1001           .Set("manifest_version", 2)
1002           .Set("permissions",
1003                extensions::ListBuilder().Append("fileBrowserHandler").Build())
1004           .Set("file_browser_handlers",
1005                extensions::ListBuilder()
1006                    .Append(
1007                        extensions::DictionaryBuilder()
1008                            .Set("id", "open")
1009                            .Set("default_title", "open")
1010                            .Set("file_filters", extensions::ListBuilder()
1011                                                     .Append("filesystem:*.gdoc")
1012                                                     .Build())
1013                            .Build())
1014                    .Build())
1015           .Build());
1016   files_app.SetID(kFileManagerAppId);
1017   extension_service_->AddExtension(files_app.Build().get());
1018 
1019   // Find apps for a ".gdoc file". Only the built-in handler of the Files apps
1020   // should be found.
1021   std::vector<extensions::EntryInfo> entries;
1022   std::vector<GURL> file_urls;
1023   entries.emplace_back(
1024       util::GetMyFilesFolderForProfile(&test_profile_).AppendASCII("foo.gdoc"),
1025       "application/vnd.google-apps.document", false);
1026   file_urls.emplace_back("filesystem:chrome-extension://id/dir/foo.gdoc");
1027 
1028   std::vector<FullTaskDescriptor> tasks;
1029   FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, entries,
1030                                                file_urls, &tasks);
1031   ASSERT_EQ(1U, tasks.size());
1032   EXPECT_EQ(kFileManagerAppId, tasks[0].task_descriptor().app_id);
1033 }
1034 
TEST_F(FileManagerFileTasksComplexTest,FindFileHandlerTask_Generic)1035 TEST_F(FileManagerFileTasksComplexTest, FindFileHandlerTask_Generic) {
1036   // Since we want to keep the order of the result as foo,bar,baz,qux,
1037   // keep the ids in alphabetical order.
1038   const char kFooId[] = "hhgbjpmdppecanaaogonaigmmifgpaph";
1039   const char kBarId[] = "odlhccgofgkadkkhcmhgnhgahonahoca";
1040   const char kBazId[] = "plifkpkakemokpflgbnnigcoldgcbdmc";
1041   const char kQuxId[] = "pmifkpkakgkadkkhcmhgnigmmifgpaph";
1042 
1043   // Foo app provides file handler for text/plain and all file types.
1044   extensions::ExtensionBuilder foo_app;
1045   foo_app.SetManifest(
1046       extensions::DictionaryBuilder()
1047           .Set("name", "Foo")
1048           .Set("version", "1.0.0")
1049           .Set("manifest_version", 2)
1050           .Set("app", extensions::DictionaryBuilder()
1051                           .Set("background",
1052                                extensions::DictionaryBuilder()
1053                                    .Set("scripts", extensions::ListBuilder()
1054                                                        .Append("background.js")
1055                                                        .Build())
1056                                    .Build())
1057                           .Build())
1058           .Set(
1059               "file_handlers",
1060               extensions::DictionaryBuilder()
1061                   .Set("any",
1062                        extensions::DictionaryBuilder()
1063                            .Set("types",
1064                                 extensions::ListBuilder().Append("*/*").Build())
1065                            .Build())
1066                   .Set("text", extensions::DictionaryBuilder()
1067                                    .Set("types", extensions::ListBuilder()
1068                                                      .Append("text/plain")
1069                                                      .Build())
1070                                    .Build())
1071                   .Build())
1072           .Build());
1073   foo_app.SetID(kFooId);
1074   extension_service_->AddExtension(foo_app.Build().get());
1075 
1076   // Bar app provides file handler for .txt and not provide generic file
1077   // handler, but handles directories.
1078   extensions::ExtensionBuilder bar_app;
1079   bar_app.SetManifest(
1080       extensions::DictionaryBuilder()
1081           .Set("name", "Bar")
1082           .Set("version", "1.0.0")
1083           .Set("manifest_version", 2)
1084           .Set("app", extensions::DictionaryBuilder()
1085                           .Set("background",
1086                                extensions::DictionaryBuilder()
1087                                    .Set("scripts", extensions::ListBuilder()
1088                                                        .Append("background.js")
1089                                                        .Build())
1090                                    .Build())
1091                           .Build())
1092           .Set(
1093               "file_handlers",
1094               extensions::DictionaryBuilder()
1095                   .Set("text",
1096                        extensions::DictionaryBuilder()
1097                            .Set("include_directories", true)
1098                            .Set("extensions",
1099                                 extensions::ListBuilder().Append("txt").Build())
1100                            .Build())
1101                   .Build())
1102           .Build());
1103   bar_app.SetID(kBarId);
1104   extension_service_->AddExtension(bar_app.Build().get());
1105 
1106   // Baz app provides file handler for all extensions and images.
1107   extensions::ExtensionBuilder baz_app;
1108   baz_app.SetManifest(
1109       extensions::DictionaryBuilder()
1110           .Set("name", "Baz")
1111           .Set("version", "1.0.0")
1112           .Set("manifest_version", 2)
1113           .Set("app", extensions::DictionaryBuilder()
1114                           .Set("background",
1115                                extensions::DictionaryBuilder()
1116                                    .Set("scripts", extensions::ListBuilder()
1117                                                        .Append("background.js")
1118                                                        .Build())
1119                                    .Build())
1120                           .Build())
1121           .Set("file_handlers",
1122                extensions::DictionaryBuilder()
1123                    .Set("any", extensions::DictionaryBuilder()
1124                                    .Set("extensions", extensions::ListBuilder()
1125                                                           .Append("*")
1126                                                           .Append("bar")
1127                                                           .Build())
1128                                    .Build())
1129                    .Set("image", extensions::DictionaryBuilder()
1130                                      .Set("types", extensions::ListBuilder()
1131                                                        .Append("image/*")
1132                                                        .Build())
1133                                      .Build())
1134                    .Build())
1135           .Build());
1136   baz_app.SetID(kBazId);
1137   extension_service_->AddExtension(baz_app.Build().get());
1138 
1139   // Qux app provides file handler for all types.
1140   extensions::ExtensionBuilder qux_app;
1141   qux_app.SetManifest(
1142       extensions::DictionaryBuilder()
1143           .Set("name", "Qux")
1144           .Set("version", "1.0.0")
1145           .Set("manifest_version", 2)
1146           .Set("app", extensions::DictionaryBuilder()
1147                           .Set("background",
1148                                extensions::DictionaryBuilder()
1149                                    .Set("scripts", extensions::ListBuilder()
1150                                                        .Append("background.js")
1151                                                        .Build())
1152                                    .Build())
1153                           .Build())
1154           .Set("file_handlers",
1155                extensions::DictionaryBuilder()
1156                    .Set("any",
1157                         extensions::DictionaryBuilder()
1158                             .Set("types",
1159                                  extensions::ListBuilder().Append("*").Build())
1160                             .Build())
1161                    .Build())
1162           .Build());
1163   qux_app.SetID(kQuxId);
1164   extension_service_->AddExtension(qux_app.Build().get());
1165 
1166   // Test case with .txt file
1167   std::vector<extensions::EntryInfo> txt_entries;
1168   txt_entries.emplace_back(
1169       util::GetMyFilesFolderForProfile(&test_profile_).AppendASCII("foo.txt"),
1170       "text/plain", false);
1171   std::vector<FullTaskDescriptor> txt_result;
1172   FindFileHandlerTasks(&test_profile_, txt_entries, &txt_result);
1173   EXPECT_EQ(4U, txt_result.size());
1174   // Foo app provides a handler for text/plain.
1175   EXPECT_EQ("Foo", txt_result[0].task_title());
1176   EXPECT_FALSE(txt_result[0].is_generic_file_handler());
1177   // Bar app provides a handler for .txt.
1178   EXPECT_EQ("Bar", txt_result[1].task_title());
1179   EXPECT_FALSE(txt_result[1].is_generic_file_handler());
1180   // Baz app provides a handler for all extensions.
1181   EXPECT_EQ("Baz", txt_result[2].task_title());
1182   EXPECT_TRUE(txt_result[2].is_generic_file_handler());
1183   // Qux app provides a handler for all types.
1184   EXPECT_EQ("Qux", txt_result[3].task_title());
1185   EXPECT_TRUE(txt_result[3].is_generic_file_handler());
1186 
1187   // Test case with .jpg file
1188   std::vector<extensions::EntryInfo> jpg_entries;
1189   jpg_entries.emplace_back(
1190       util::GetMyFilesFolderForProfile(&test_profile_).AppendASCII("foo.jpg"),
1191       "image/jpeg", false);
1192   std::vector<FullTaskDescriptor> jpg_result;
1193   FindFileHandlerTasks(&test_profile_, jpg_entries, &jpg_result);
1194   EXPECT_EQ(3U, jpg_result.size());
1195   // Foo app provides a handler for all types.
1196   EXPECT_EQ("Foo", jpg_result[0].task_title());
1197   EXPECT_TRUE(jpg_result[0].is_generic_file_handler());
1198   // Baz app provides a handler for image/*. A partial wildcarded handler is
1199   // treated as non-generic handler.
1200   EXPECT_EQ("Baz", jpg_result[1].task_title());
1201   EXPECT_FALSE(jpg_result[1].is_generic_file_handler());
1202   // Qux app provides a handler for all types.
1203   EXPECT_EQ("Qux", jpg_result[2].task_title());
1204   EXPECT_TRUE(jpg_result[2].is_generic_file_handler());
1205 
1206   // Test case with directories.
1207   std::vector<extensions::EntryInfo> dir_entries;
1208   dir_entries.emplace_back(
1209       util::GetMyFilesFolderForProfile(&test_profile_).AppendASCII("dir"), "",
1210       true);
1211   std::vector<FullTaskDescriptor> dir_result;
1212   FindFileHandlerTasks(&test_profile_, dir_entries, &dir_result);
1213   ASSERT_EQ(1U, dir_result.size());
1214   // Confirm that only Bar.app is found and that it is a generic file handler.
1215   EXPECT_EQ(kBarId, dir_result[0].task_descriptor().app_id);
1216   EXPECT_TRUE(dir_result[0].is_generic_file_handler());
1217 }
1218 
1219 // The basic logic is similar to a test case for FindFileHandlerTasks above.
TEST_F(FileManagerFileTasksComplexTest,FindFileHandlerTask_Verbs)1220 TEST_F(FileManagerFileTasksComplexTest, FindFileHandlerTask_Verbs) {
1221   // kFooId copied from FindFileHandlerTasks test above.
1222   const char kFooId[] = "hhgbjpmdppecanaaogonaigmmifgpaph";
1223 
1224   // Foo.app can handle "text/plain" and "text/html".
1225   extensions::ExtensionBuilder foo_app;
1226   foo_app.SetManifest(
1227       extensions::DictionaryBuilder()
1228           .Set("name", "Foo")
1229           .Set("version", "1.0.0")
1230           .Set("manifest_version", 2)
1231           .Set("app", extensions::DictionaryBuilder()
1232                           .Set("background",
1233                                extensions::DictionaryBuilder()
1234                                    .Set("scripts", extensions::ListBuilder()
1235                                                        .Append("background.js")
1236                                                        .Build())
1237                                    .Build())
1238                           .Build())
1239           .Set(
1240               "file_handlers",
1241               extensions::DictionaryBuilder()
1242                   .Set("any",
1243                        extensions::DictionaryBuilder()
1244                            .Set("types",
1245                                 extensions::ListBuilder().Append("*").Build())
1246                            .Set("verb", "add_to")
1247                            .Build())
1248                   .Set("any_with_directories",
1249                        extensions::DictionaryBuilder()
1250                            .Set("include_directories", true)
1251                            .Set("types",
1252                                 extensions::ListBuilder().Append("*").Build())
1253                            .Set("verb", "pack_with")
1254                            .Build())
1255                   .Set("all_text", extensions::DictionaryBuilder()
1256                                        .Set("title", "Text")
1257                                        .Set("types", extensions::ListBuilder()
1258                                                          .Append("text/plain")
1259                                                          .Append("text/html")
1260                                                          .Build())
1261                                        .Set("verb", "add_to")
1262                                        .Build())
1263                   .Set("plain_text", extensions::DictionaryBuilder()
1264                                          .Set("title", "Plain")
1265                                          .Set("types", extensions::ListBuilder()
1266                                                            .Append("text/plain")
1267                                                            .Build())
1268                                          .Set("verb", "open_with")
1269                                          .Build())
1270                   .Set("html_text_duplicate_verb",
1271                        extensions::DictionaryBuilder()
1272                            .Set("title", "Html")
1273                            .Set("types", extensions::ListBuilder()
1274                                              .Append("text/html")
1275                                              .Build())
1276                            .Set("verb", "add_to")
1277                            .Build())
1278                   .Set("share_plain_text",
1279                        extensions::DictionaryBuilder()
1280                            .Set("title", "Share Plain")
1281                            .Set("types", extensions::ListBuilder()
1282                                              .Append("text/plain")
1283                                              .Build())
1284                            .Set("verb", "share_with")
1285                            .Build())
1286                   .Build())
1287           .Build());
1288   foo_app.SetID(kFooId);
1289   extension_service_->AddExtension(foo_app.Build().get());
1290 
1291   // Find app with corresponding verbs for a "text/plain" file.
1292   // Foo.app with ADD_TO, OPEN_WITH, PACK_WITH and SHARE_WITH should be found,
1293   // but only one ADD_TO that is not a generic handler will be taken into
1294   // account, even though there are 2 ADD_TO matches for "text/plain".
1295   std::vector<extensions::EntryInfo> entries;
1296   entries.emplace_back(
1297       util::GetMyFilesFolderForProfile(&test_profile_).AppendASCII("foo.txt"),
1298       "text/plain", false);
1299 
1300   std::vector<FullTaskDescriptor> tasks;
1301   FindFileHandlerTasks(&test_profile_, entries, &tasks);
1302 
1303   ASSERT_EQ(4U, tasks.size());
1304   EXPECT_EQ(kFooId, tasks[0].task_descriptor().app_id);
1305   EXPECT_EQ("Foo", tasks[0].task_title());
1306   EXPECT_EQ(Verb::VERB_ADD_TO, tasks[0].task_verb());
1307   EXPECT_EQ(kFooId, tasks[1].task_descriptor().app_id);
1308   EXPECT_EQ("Foo", tasks[1].task_title());
1309   EXPECT_EQ(Verb::VERB_OPEN_WITH, tasks[1].task_verb());
1310   EXPECT_EQ(kFooId, tasks[2].task_descriptor().app_id);
1311   EXPECT_EQ("Foo", tasks[2].task_title());
1312   EXPECT_EQ(Verb::VERB_PACK_WITH, tasks[2].task_verb());
1313   EXPECT_EQ(kFooId, tasks[3].task_descriptor().app_id);
1314   EXPECT_EQ("Foo", tasks[3].task_title());
1315   EXPECT_EQ(Verb::VERB_SHARE_WITH, tasks[3].task_verb());
1316 
1317   // Find app with corresponding verbs for a "text/html" file.
1318   // Foo.app with ADD_TO and PACK_WITH should be found, but only the first
1319   // ADD_TO that is a good match will be taken into account, even though there
1320   // are 3 ADD_TO matches for "text/html".
1321   entries.clear();
1322   entries.emplace_back(
1323       util::GetMyFilesFolderForProfile(&test_profile_).AppendASCII("foo.html"),
1324       "text/html", false);
1325   tasks.clear();
1326   FindFileHandlerTasks(&test_profile_, entries, &tasks);
1327 
1328   ASSERT_EQ(2U, tasks.size());
1329   EXPECT_EQ(kFooId, tasks[0].task_descriptor().app_id);
1330   EXPECT_EQ("Foo", tasks[0].task_title());
1331   EXPECT_EQ(Verb::VERB_ADD_TO, tasks[0].task_verb());
1332   EXPECT_EQ(kFooId, tasks[1].task_descriptor().app_id);
1333   EXPECT_EQ("Foo", tasks[1].task_title());
1334   EXPECT_EQ(Verb::VERB_PACK_WITH, tasks[1].task_verb());
1335 
1336   // Find app with corresponding verbs for directories.
1337   // Foo.app with only PACK_WITH should be found.
1338   entries.clear();
1339   entries.emplace_back(
1340       util::GetMyFilesFolderForProfile(&test_profile_).AppendASCII("dir"), "",
1341       true);
1342   tasks.clear();
1343   FindFileHandlerTasks(&test_profile_, entries, &tasks);
1344 
1345   ASSERT_EQ(1U, tasks.size());
1346   EXPECT_EQ(kFooId, tasks[0].task_descriptor().app_id);
1347   EXPECT_EQ("Foo", tasks[0].task_title());
1348   EXPECT_EQ(Verb::VERB_PACK_WITH, tasks[0].task_verb());
1349 }
1350 
1351 // Test using the test extension system, which needs lots of setup.
1352 class FileManagerFileTasksCrostiniTest
1353     : public FileManagerFileTasksComplexTest {
1354  protected:
FileManagerFileTasksCrostiniTest()1355   FileManagerFileTasksCrostiniTest()
1356       : crostini_test_helper_(&test_profile_),
1357         crostini_folder_(util::GetCrostiniMountDirectory(&test_profile_)) {
1358     chromeos::DBusThreadManager::GetSetterForTesting()->SetConciergeClient(
1359         std::make_unique<chromeos::FakeConciergeClient>());
1360 
1361     vm_tools::apps::App text_app =
1362         crostini::CrostiniTestHelper::BasicApp("text_app");
1363     *text_app.add_mime_types() = "text/plain";
1364     crostini_test_helper_.AddApp(text_app);
1365 
1366     vm_tools::apps::App image_app =
1367         crostini::CrostiniTestHelper::BasicApp("image_app");
1368     *image_app.add_mime_types() = "image/gif";
1369     *image_app.add_mime_types() = "image/jpeg";
1370     *image_app.add_mime_types() = "image/jpg";
1371     *image_app.add_mime_types() = "image/png";
1372     crostini_test_helper_.AddApp(image_app);
1373 
1374     vm_tools::apps::App gif_app =
1375         crostini::CrostiniTestHelper::BasicApp("gif_app");
1376     *gif_app.add_mime_types() = "image/gif";
1377     crostini_test_helper_.AddApp(gif_app);
1378 
1379     vm_tools::apps::App alt_mime_app =
1380         crostini::CrostiniTestHelper::BasicApp("alt_mime_app");
1381     *alt_mime_app.add_mime_types() = "foo/x-bar";
1382     crostini_test_helper_.AddApp(alt_mime_app);
1383 
1384     text_app_id_ = crostini::CrostiniTestHelper::GenerateAppId("text_app");
1385     image_app_id_ = crostini::CrostiniTestHelper::GenerateAppId("image_app");
1386     gif_app_id_ = crostini::CrostiniTestHelper::GenerateAppId("gif_app");
1387     alt_mime_app_id_ =
1388         crostini::CrostiniTestHelper::GenerateAppId("alt_mime_app");
1389 
1390     // Setup the custom MIME type mapping.
1391     vm_tools::apps::MimeTypes mime_types_list;
1392     mime_types_list.set_vm_name(crostini::kCrostiniDefaultVmName);
1393     mime_types_list.set_container_name(crostini::kCrostiniDefaultContainerName);
1394     (*mime_types_list.mutable_mime_type_mappings())["foo"] = "foo/x-bar";
1395 
1396     crostini::CrostiniMimeTypesServiceFactory::GetForProfile(&test_profile_)
1397         ->UpdateMimeTypes(mime_types_list);
1398   }
1399 
SetUp()1400   void SetUp() override {
1401     storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
1402         util::GetDownloadsMountPointName(&test_profile_),
1403         storage::kFileSystemTypeNativeLocal, storage::FileSystemMountOption(),
1404         util::GetMyFilesFolderForProfile(&test_profile_));
1405   }
1406 
TearDown()1407   void TearDown() override {
1408     storage::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
1409         util::GetDownloadsMountPointName(&test_profile_));
1410   }
1411 
PathToURL(const std::string & path)1412   GURL PathToURL(const std::string& path) {
1413     std::string virtual_path = net::EscapeUrlEncodedData(
1414         util::GetDownloadsMountPointName(&test_profile_) + "/" + path,
1415         /*use_plus=*/false);
1416     return GURL("filesystem:chrome-extension://id/external/" + virtual_path);
1417   }
1418 
1419   crostini::CrostiniTestHelper crostini_test_helper_;
1420   base::FilePath crostini_folder_;
1421   std::string text_app_id_;
1422   std::string image_app_id_;
1423   std::string gif_app_id_;
1424   std::string alt_mime_app_id_;
1425 };
1426 
TEST_F(FileManagerFileTasksCrostiniTest,BasicFiles)1427 TEST_F(FileManagerFileTasksCrostiniTest, BasicFiles) {
1428   std::vector<extensions::EntryInfo> entries{
1429       {crostini_folder_.Append("foo.txt"), "text/plain", false}};
1430   std::vector<GURL> file_urls{PathToURL("dir/foo.txt")};
1431 
1432   std::vector<FullTaskDescriptor> tasks;
1433   FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, entries,
1434                                                file_urls, &tasks);
1435   ASSERT_EQ(1U, tasks.size());
1436   EXPECT_EQ(text_app_id_, tasks[0].task_descriptor().app_id);
1437 
1438   // Multiple text files
1439   entries.emplace_back(crostini_folder_.Append("bar.txt"), "text/plain", false);
1440   file_urls.emplace_back(PathToURL("dir/bar.txt"));
1441   FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, entries,
1442                                                file_urls, &tasks);
1443   ASSERT_EQ(1U, tasks.size());
1444   EXPECT_EQ(text_app_id_, tasks[0].task_descriptor().app_id);
1445 }
1446 
TEST_F(FileManagerFileTasksCrostiniTest,Directories)1447 TEST_F(FileManagerFileTasksCrostiniTest, Directories) {
1448   std::vector<extensions::EntryInfo> entries{
1449       {crostini_folder_.Append("dir"), "", true}};
1450   std::vector<GURL> file_urls{PathToURL("dir/dir")};
1451   std::vector<FullTaskDescriptor> tasks;
1452   FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, entries,
1453                                                file_urls, &tasks);
1454   EXPECT_EQ(0U, tasks.size());
1455 
1456   entries.emplace_back(crostini_folder_.Append("foo.txt"), "text/plain", false);
1457   file_urls.emplace_back(PathToURL("dir/foo.txt"));
1458   FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, entries,
1459                                                file_urls, &tasks);
1460   EXPECT_EQ(0U, tasks.size());
1461 }
1462 
TEST_F(FileManagerFileTasksCrostiniTest,MultipleMatches)1463 TEST_F(FileManagerFileTasksCrostiniTest, MultipleMatches) {
1464   std::vector<extensions::EntryInfo> entries{
1465       {crostini_folder_.Append("foo.gif"), "image/gif", false},
1466       {crostini_folder_.Append("bar.gif"), "image/gif", false}};
1467   std::vector<GURL> file_urls{PathToURL("dir/foo.gif"),
1468                               PathToURL("dir/bar.gif")};
1469 
1470   std::vector<FullTaskDescriptor> tasks;
1471   FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, entries,
1472                                                file_urls, &tasks);
1473   // The returned values happen to be ordered alphabetically by app_id, so we
1474   // rely on this to keep the test simple.
1475   EXPECT_LT(gif_app_id_, image_app_id_);
1476   ASSERT_EQ(2U, tasks.size());
1477   EXPECT_EQ(gif_app_id_, tasks[0].task_descriptor().app_id);
1478   EXPECT_EQ(image_app_id_, tasks[1].task_descriptor().app_id);
1479 }
1480 
TEST_F(FileManagerFileTasksCrostiniTest,MultipleTypes)1481 TEST_F(FileManagerFileTasksCrostiniTest, MultipleTypes) {
1482   std::vector<extensions::EntryInfo> entries{
1483       {crostini_folder_.Append("foo.gif"), "image/gif", false},
1484       {crostini_folder_.Append("bar.png"), "image/png", false}};
1485   std::vector<GURL> file_urls{PathToURL("dir/foo.gif"),
1486                               PathToURL("dir/bar.png")};
1487 
1488   std::vector<FullTaskDescriptor> tasks;
1489   FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, entries,
1490                                                file_urls, &tasks);
1491   ASSERT_EQ(1U, tasks.size());
1492   EXPECT_EQ(image_app_id_, tasks[0].task_descriptor().app_id);
1493 
1494   entries.emplace_back(crostini_folder_.Append("qux.mp4"), "video/mp4", false);
1495   file_urls.emplace_back(PathToURL("dir/qux.mp4"));
1496   FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, entries,
1497                                                file_urls, &tasks);
1498   EXPECT_EQ(0U, tasks.size());
1499 }
1500 
TEST_F(FileManagerFileTasksCrostiniTest,AlternateMimeTypes)1501 TEST_F(FileManagerFileTasksCrostiniTest, AlternateMimeTypes) {
1502   std::vector<extensions::EntryInfo> entries{
1503       {crostini_folder_.Append("bar1.foo"), "text/plain", false},
1504       {crostini_folder_.Append("bar2.foo"), "application/octet-stream", false}};
1505   std::vector<GURL> file_urls{PathToURL("dir/bar1.foo"),
1506                               PathToURL("dir/bar2.foo")};
1507 
1508   std::vector<FullTaskDescriptor> tasks;
1509   FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, entries,
1510                                                file_urls, &tasks);
1511   ASSERT_EQ(1U, tasks.size());
1512   EXPECT_EQ(alt_mime_app_id_, tasks[0].task_descriptor().app_id);
1513 }
1514 
1515 }  // namespace file_tasks
1516 }  // namespace file_manager.
1517