1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h"
9 #include "base/json/json_reader.h"
10 #include "base/json/json_value_converter.h"
11 #include "base/json/json_writer.h"
12 #include "base/path_service.h"
13 #include "base/strings/string_piece.h"
14 #include "base/time/time.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/chromeos/drive/file_system_util.h"
17 #include "chrome/browser/chromeos/file_manager/mount_test_util.h"
18 #include "chrome/browser/chromeos/file_manager/path_util.h"
19 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
20 #include "chrome/browser/extensions/component_loader.h"
21 #include "chrome/browser/notifications/notification.h"
22 #include "chrome/browser/notifications/notification_ui_manager.h"
23 #include "chrome/common/chrome_switches.h"
24 #include "chromeos/chromeos_switches.h"
25 #include "components/drive/file_system_interface.h"
26 #include "components/drive/service/fake_drive_service.h"
27 #include "content/public/browser/notification_service.h"
28 #include "content/public/test/test_utils.h"
29 #include "extensions/browser/api/test/test_api.h"
30 #include "extensions/browser/notification_types.h"
31 #include "google_apis/drive/drive_api_parser.h"
32 #include "google_apis/drive/test_util.h"
33 #include "net/test/embedded_test_server/embedded_test_server.h"
34 #include "storage/browser/fileapi/external_mount_points.h"
36 namespace file_manager
{
55 // Obtains file manager test data directory.
56 base::FilePath
GetTestFilePath(const std::string
& relative_path
) {
58 if (!PathService::Get(base::DIR_SOURCE_ROOT
, &path
))
59 return base::FilePath();
60 path
= path
.AppendASCII("chrome")
63 .AppendASCII("chromeos")
64 .AppendASCII("file_manager")
65 .Append(base::FilePath::FromUTF8Unsafe(relative_path
));
69 // Maps the given string to EntryType. Returns true on success.
70 bool MapStringToEntryType(const base::StringPiece
& value
, EntryType
* output
) {
73 else if (value
== "directory")
80 // Maps the given string to SharedOption. Returns true on success.
81 bool MapStringToSharedOption(const base::StringPiece
& value
,
82 SharedOption
* output
) {
83 if (value
== "shared")
85 else if (value
== "none")
92 // Maps the given string to TargetVolume. Returns true on success.
93 bool MapStringToTargetVolume(const base::StringPiece
& value
,
94 TargetVolume
* output
) {
96 *output
= DRIVE_VOLUME
;
97 else if (value
== "local")
98 *output
= LOCAL_VOLUME
;
99 else if (value
== "usb")
100 *output
= USB_VOLUME
;
106 // Maps the given string to base::Time. Returns true on success.
107 bool MapStringToTime(const base::StringPiece
& value
, base::Time
* time
) {
108 return base::Time::FromString(value
.as_string().c_str(), time
);
111 // Test data of file or directory.
112 struct TestEntryInfo
{
113 TestEntryInfo() : type(FILE), shared_option(NONE
) {}
115 TestEntryInfo(EntryType type
,
116 const std::string
& source_file_name
,
117 const std::string
& target_path
,
118 const std::string
& mime_type
,
119 SharedOption shared_option
,
120 const base::Time
& last_modified_time
)
122 source_file_name(source_file_name
),
123 target_path(target_path
),
124 mime_type(mime_type
),
125 shared_option(shared_option
),
126 last_modified_time(last_modified_time
) {}
129 std::string source_file_name
; // Source file name to be used as a prototype.
130 std::string target_path
; // Target file or directory path.
131 std::string mime_type
;
132 SharedOption shared_option
;
133 base::Time last_modified_time
;
135 // Registers the member information to the given converter.
136 static void RegisterJSONConverter(
137 base::JSONValueConverter
<TestEntryInfo
>* converter
);
141 void TestEntryInfo::RegisterJSONConverter(
142 base::JSONValueConverter
<TestEntryInfo
>* converter
) {
143 converter
->RegisterCustomField("type", &TestEntryInfo::type
,
144 &MapStringToEntryType
);
145 converter
->RegisterStringField("sourceFileName",
146 &TestEntryInfo::source_file_name
);
147 converter
->RegisterStringField("targetPath", &TestEntryInfo::target_path
);
148 converter
->RegisterStringField("mimeType", &TestEntryInfo::mime_type
);
149 converter
->RegisterCustomField("sharedOption", &TestEntryInfo::shared_option
,
150 &MapStringToSharedOption
);
151 converter
->RegisterCustomField(
152 "lastModifiedTime", &TestEntryInfo::last_modified_time
, &MapStringToTime
);
155 // Message from JavaScript to add entries.
156 struct AddEntriesMessage
{
157 // Target volume to be added the |entries|.
160 // Entries to be added.
161 ScopedVector
<TestEntryInfo
> entries
;
163 // Registers the member information to the given converter.
164 static void RegisterJSONConverter(
165 base::JSONValueConverter
<AddEntriesMessage
>* converter
);
169 void AddEntriesMessage::RegisterJSONConverter(
170 base::JSONValueConverter
<AddEntriesMessage
>* converter
) {
171 converter
->RegisterCustomField("volume", &AddEntriesMessage::volume
,
172 &MapStringToTargetVolume
);
173 converter
->RegisterRepeatedMessage
<TestEntryInfo
>(
174 "entries", &AddEntriesMessage::entries
);
180 explicit TestVolume(const std::string
& name
) : name_(name
) {}
181 virtual ~TestVolume() {}
183 bool CreateRootDirectory(const Profile
* profile
) {
184 const base::FilePath path
= profile
->GetPath().Append(name_
);
185 return root_
.path() == path
|| root_
.Set(path
);
188 const std::string
& name() { return name_
; }
189 const base::FilePath
root_path() { return root_
.path(); }
193 base::ScopedTempDir root_
;
196 // Listener to obtain the test relative messages synchronously.
197 class FileManagerTestListener
: public content::NotificationObserver
{
202 scoped_refptr
<extensions::TestSendMessageFunction
> function
;
205 FileManagerTestListener() {
206 registrar_
.Add(this, extensions::NOTIFICATION_EXTENSION_TEST_PASSED
,
207 content::NotificationService::AllSources());
208 registrar_
.Add(this, extensions::NOTIFICATION_EXTENSION_TEST_FAILED
,
209 content::NotificationService::AllSources());
210 registrar_
.Add(this, extensions::NOTIFICATION_EXTENSION_TEST_MESSAGE
,
211 content::NotificationService::AllSources());
214 Message
GetNextMessage() {
215 if (messages_
.empty())
216 content::RunMessageLoop();
217 const Message entry
= messages_
.front();
218 messages_
.pop_front();
222 void Observe(int type
,
223 const content::NotificationSource
& source
,
224 const content::NotificationDetails
& details
) override
{
227 entry
.message
= type
!= extensions::NOTIFICATION_EXTENSION_TEST_PASSED
228 ? *content::Details
<std::string
>(details
).ptr()
231 type
== extensions::NOTIFICATION_EXTENSION_TEST_MESSAGE
232 ? content::Source
<extensions::TestSendMessageFunction
>(source
).ptr()
234 messages_
.push_back(entry
);
235 base::MessageLoopForUI::current()->Quit();
239 std::deque
<Message
> messages_
;
240 content::NotificationRegistrar registrar_
;
243 } // anonymous namespace
245 // The local volume class for test.
246 // This class provides the operations for a test volume that simulates local
248 class LocalTestVolume
: public TestVolume
{
250 explicit LocalTestVolume(const std::string
& name
) : TestVolume(name
) {}
251 ~LocalTestVolume() override
{}
253 // Adds this volume to the file system as a local volume. Returns true on
255 virtual bool Mount(Profile
* profile
) = 0;
257 void CreateEntry(const TestEntryInfo
& entry
) {
258 const base::FilePath target_path
=
259 root_path().AppendASCII(entry
.target_path
);
261 entries_
.insert(std::make_pair(target_path
, entry
));
262 switch (entry
.type
) {
264 const base::FilePath source_path
=
265 GetTestFilePath(entry
.source_file_name
);
266 ASSERT_TRUE(base::CopyFile(source_path
, target_path
))
267 << "Copy from " << source_path
.value() << " to "
268 << target_path
.value() << " failed.";
272 ASSERT_TRUE(base::CreateDirectory(target_path
))
273 << "Failed to create a directory: " << target_path
.value();
276 ASSERT_TRUE(UpdateModifiedTime(entry
));
280 // Updates ModifiedTime of the entry and its parents by referring
281 // TestEntryInfo. Returns true on success.
282 bool UpdateModifiedTime(const TestEntryInfo
& entry
) {
283 const base::FilePath path
= root_path().AppendASCII(entry
.target_path
);
284 if (!base::TouchFile(path
, entry
.last_modified_time
,
285 entry
.last_modified_time
))
288 // Update the modified time of parent directories because it may be also
289 // affected by the update of child items.
290 if (path
.DirName() != root_path()) {
291 const std::map
<base::FilePath
, const TestEntryInfo
>::iterator it
=
292 entries_
.find(path
.DirName());
293 if (it
== entries_
.end())
295 return UpdateModifiedTime(it
->second
);
300 std::map
<base::FilePath
, const TestEntryInfo
> entries_
;
303 class DownloadsTestVolume
: public LocalTestVolume
{
305 DownloadsTestVolume() : LocalTestVolume("Downloads") {}
306 ~DownloadsTestVolume() override
{}
308 bool Mount(Profile
* profile
) override
{
309 return CreateRootDirectory(profile
) &&
310 VolumeManager::Get(profile
)
311 ->RegisterDownloadsDirectoryForTesting(root_path());
315 // Test volume for mimicing a specified type of volumes by a local folder.
316 class FakeTestVolume
: public LocalTestVolume
{
318 FakeTestVolume(const std::string
& name
,
319 VolumeType volume_type
,
320 chromeos::DeviceType device_type
)
321 : LocalTestVolume(name
),
322 volume_type_(volume_type
),
323 device_type_(device_type
) {}
324 ~FakeTestVolume() override
{}
326 // Simple test entries used for testing, e.g., read-only volumes.
327 bool PrepareTestEntries(Profile
* profile
) {
328 if (!CreateRootDirectory(profile
))
330 // Must be in sync with BASIC_FAKE_ENTRY_SET in the JS test code.
331 CreateEntry(TestEntryInfo(FILE, "text.txt", "hello.txt", "text/plain", NONE
,
333 CreateEntry(TestEntryInfo(DIRECTORY
, std::string(), "A", std::string(),
334 NONE
, base::Time::Now()));
338 bool Mount(Profile
* profile
) override
{
339 if (!CreateRootDirectory(profile
))
341 storage::ExternalMountPoints
* const mount_points
=
342 storage::ExternalMountPoints::GetSystemInstance();
344 // First revoke the existing mount point (if any).
345 mount_points
->RevokeFileSystem(name());
346 const bool result
= mount_points
->RegisterFileSystem(
347 name(), storage::kFileSystemTypeNativeLocal
,
348 storage::FileSystemMountOption(), root_path());
352 VolumeManager::Get(profile
)->AddVolumeForTesting(
353 root_path(), volume_type_
, device_type_
, false /* read_only */);
358 const VolumeType volume_type_
;
359 const chromeos::DeviceType device_type_
;
362 // The drive volume class for test.
363 // This class provides the operations for a test volume that simulates Google
365 class DriveTestVolume
: public TestVolume
{
367 DriveTestVolume() : TestVolume("drive"), integration_service_(NULL
) {}
368 ~DriveTestVolume() override
{}
370 void CreateEntry(const TestEntryInfo
& entry
) {
371 const base::FilePath path
=
372 base::FilePath::FromUTF8Unsafe(entry
.target_path
);
373 const std::string target_name
= path
.BaseName().AsUTF8Unsafe();
375 // Obtain the parent entry.
376 drive::FileError error
= drive::FILE_ERROR_OK
;
377 scoped_ptr
<drive::ResourceEntry
> parent_entry(new drive::ResourceEntry
);
378 integration_service_
->file_system()->GetResourceEntry(
379 drive::util::GetDriveMyDriveRootPath().Append(path
).DirName(),
380 google_apis::test_util::CreateCopyResultCallback(&error
,
382 content::RunAllBlockingPoolTasksUntilIdle();
383 ASSERT_EQ(drive::FILE_ERROR_OK
, error
);
384 ASSERT_TRUE(parent_entry
);
386 switch (entry
.type
) {
388 CreateFile(entry
.source_file_name
, parent_entry
->resource_id(),
389 target_name
, entry
.mime_type
, entry
.shared_option
== SHARED
,
390 entry
.last_modified_time
);
393 CreateDirectory(parent_entry
->resource_id(), target_name
,
394 entry
.last_modified_time
);
399 // Creates an empty directory with the given |name| and |modification_time|.
400 void CreateDirectory(const std::string
& parent_id
,
401 const std::string
& target_name
,
402 const base::Time
& modification_time
) {
403 google_apis::DriveApiErrorCode error
= google_apis::DRIVE_OTHER_ERROR
;
404 scoped_ptr
<google_apis::FileResource
> entry
;
405 fake_drive_service_
->AddNewDirectory(
406 parent_id
, target_name
, drive::AddNewDirectoryOptions(),
407 google_apis::test_util::CreateCopyResultCallback(&error
, &entry
));
408 base::MessageLoop::current()->RunUntilIdle();
409 ASSERT_EQ(google_apis::HTTP_CREATED
, error
);
412 fake_drive_service_
->SetLastModifiedTime(
413 entry
->file_id(), modification_time
,
414 google_apis::test_util::CreateCopyResultCallback(&error
, &entry
));
415 base::MessageLoop::current()->RunUntilIdle();
416 ASSERT_TRUE(error
== google_apis::HTTP_SUCCESS
);
421 // Creates a test file with the given spec.
422 // Serves |test_file_name| file. Pass an empty string for an empty file.
423 void CreateFile(const std::string
& source_file_name
,
424 const std::string
& parent_id
,
425 const std::string
& target_name
,
426 const std::string
& mime_type
,
428 const base::Time
& modification_time
) {
429 google_apis::DriveApiErrorCode error
= google_apis::DRIVE_OTHER_ERROR
;
431 std::string content_data
;
432 if (!source_file_name
.empty()) {
433 base::FilePath source_file_path
= GetTestFilePath(source_file_name
);
434 ASSERT_TRUE(base::ReadFileToString(source_file_path
, &content_data
));
437 scoped_ptr
<google_apis::FileResource
> entry
;
438 fake_drive_service_
->AddNewFile(
439 mime_type
, content_data
, parent_id
, target_name
, shared_with_me
,
440 google_apis::test_util::CreateCopyResultCallback(&error
, &entry
));
441 base::MessageLoop::current()->RunUntilIdle();
442 ASSERT_EQ(google_apis::HTTP_CREATED
, error
);
445 fake_drive_service_
->SetLastModifiedTime(
446 entry
->file_id(), modification_time
,
447 google_apis::test_util::CreateCopyResultCallback(&error
, &entry
));
448 base::MessageLoop::current()->RunUntilIdle();
449 ASSERT_EQ(google_apis::HTTP_SUCCESS
, error
);
455 // Notifies FileSystem that the contents in FakeDriveService are
456 // changed, hence the new contents should be fetched.
457 void CheckForUpdates() {
458 if (integration_service_
&& integration_service_
->file_system()) {
459 integration_service_
->file_system()->CheckForUpdates();
463 // Sets the url base for the test server to be used to generate share urls
464 // on the files and directories.
465 void ConfigureShareUrlBase(const GURL
& share_url_base
) {
466 fake_drive_service_
->set_share_url_base(share_url_base
);
469 drive::DriveIntegrationService
* CreateDriveIntegrationService(
472 fake_drive_service_
= new drive::FakeDriveService
;
473 fake_drive_service_
->LoadAppListForDriveApi("drive/applist.json");
475 if (!CreateRootDirectory(profile
))
477 integration_service_
= new drive::DriveIntegrationService(
478 profile
, NULL
, fake_drive_service_
, std::string(), root_path(), NULL
);
479 return integration_service_
;
484 drive::FakeDriveService
* fake_drive_service_
;
485 drive::DriveIntegrationService
* integration_service_
;
488 FileManagerBrowserTestBase::FileManagerBrowserTestBase() {
491 FileManagerBrowserTestBase::~FileManagerBrowserTestBase() {
494 void FileManagerBrowserTestBase::SetUpInProcessBrowserTestFixture() {
495 ExtensionApiTest::SetUpInProcessBrowserTestFixture();
496 extensions::ComponentLoader::EnableBackgroundExtensionsForTesting();
498 local_volume_
.reset(new DownloadsTestVolume
);
499 if (GetGuestModeParam() != IN_GUEST_MODE
) {
500 create_drive_integration_service_
=
501 base::Bind(&FileManagerBrowserTestBase::CreateDriveIntegrationService
,
502 base::Unretained(this));
503 service_factory_for_test_
.reset(
504 new drive::DriveIntegrationServiceFactory::ScopedFactoryForTest(
505 &create_drive_integration_service_
));
509 void FileManagerBrowserTestBase::SetUp() {
510 net::NetworkChangeNotifier::SetTestNotificationsOnly(true);
511 ExtensionApiTest::SetUp();
514 void FileManagerBrowserTestBase::SetUpOnMainThread() {
515 ExtensionApiTest::SetUpOnMainThread();
516 ASSERT_TRUE(local_volume_
->Mount(profile()));
518 if (GetGuestModeParam() != IN_GUEST_MODE
) {
519 // Install the web server to serve the mocked share dialog.
520 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
521 const GURL
share_url_base(embedded_test_server()->GetURL(
522 "/chromeos/file_manager/share_dialog_mock/index.html"));
523 drive_volume_
= drive_volumes_
[profile()->GetOriginalProfile()];
524 drive_volume_
->ConfigureShareUrlBase(share_url_base
);
525 test_util::WaitUntilDriveMountPointIsAdded(profile());
529 void FileManagerBrowserTestBase::SetUpCommandLine(
530 base::CommandLine
* command_line
) {
531 if (GetGuestModeParam() == IN_GUEST_MODE
) {
532 command_line
->AppendSwitch(chromeos::switches::kGuestSession
);
533 command_line
->AppendSwitchNative(chromeos::switches::kLoginUser
, "");
534 command_line
->AppendSwitch(switches::kIncognito
);
536 if (GetGuestModeParam() == IN_INCOGNITO
) {
537 command_line
->AppendSwitch(switches::kIncognito
);
539 ExtensionApiTest::SetUpCommandLine(command_line
);
542 void FileManagerBrowserTestBase::InstallExtension(const base::FilePath
& path
,
543 const char* manifest_name
) {
544 base::FilePath root_path
;
545 ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT
, &root_path
));
547 // Launch the extension.
548 const base::FilePath absolute_path
= root_path
.Append(path
);
549 const extensions::Extension
* const extension
=
550 LoadExtensionAsComponentWithManifest(absolute_path
, manifest_name
);
551 ASSERT_TRUE(extension
);
554 void FileManagerBrowserTestBase::StartTest() {
556 base::FilePath(FILE_PATH_LITERAL("ui/file_manager/integration_tests")),
557 GetTestManifestName());
558 RunTestMessageLoop();
561 void FileManagerBrowserTestBase::RunTestMessageLoop() {
562 // Handle the messages from JavaScript.
563 // The while loop is break when the test is passed or failed.
564 FileManagerTestListener listener
;
566 FileManagerTestListener::Message entry
= listener
.GetNextMessage();
567 if (entry
.type
== extensions::NOTIFICATION_EXTENSION_TEST_PASSED
) {
570 } else if (entry
.type
== extensions::NOTIFICATION_EXTENSION_TEST_FAILED
) {
572 ADD_FAILURE() << entry
.message
;
576 // Parse the message value as JSON.
577 const scoped_ptr
<const base::Value
> value(
578 base::JSONReader::DeprecatedRead(entry
.message
));
580 // If the message is not the expected format, just ignore it.
581 const base::DictionaryValue
* message_dictionary
= NULL
;
583 if (!value
|| !value
->GetAsDictionary(&message_dictionary
) ||
584 !message_dictionary
->GetString("name", &name
))
588 OnMessage(name
, *message_dictionary
, &output
);
589 if (HasFatalFailure())
592 entry
.function
->Reply(output
);
596 void FileManagerBrowserTestBase::OnMessage(const std::string
& name
,
597 const base::DictionaryValue
& value
,
598 std::string
* output
) {
599 if (name
== "getTestName") {
600 // Pass the test case name.
601 *output
= GetTestCaseNameParam();
605 if (name
== "getRootPaths") {
606 // Pass the root paths.
607 base::DictionaryValue res
;
608 res
.SetString("downloads",
609 "/" + util::GetDownloadsMountPointName(profile()));
610 res
.SetString("drive", "/" +
611 drive::util::GetDriveMountPointPath(profile())
615 base::JSONWriter::Write(res
, output
);
619 if (name
== "isInGuestMode") {
620 // Obtain whether the test is in guest mode or not.
621 *output
= GetGuestModeParam() != NOT_IN_GUEST_MODE
? "true" : "false";
625 if (name
== "getCwsWidgetContainerMockUrl") {
626 // Obtain whether the test is in guest mode or not.
627 const GURL url
= embedded_test_server()->GetURL(
628 "/chromeos/file_manager/cws_container_mock/index.html");
629 std::string origin
= url
.GetOrigin().spec();
631 // Removes trailing a slash.
632 if (*origin
.rbegin() == '/')
633 origin
.resize(origin
.length() - 1);
635 base::DictionaryValue res
;
636 res
.SetString("url", url
.spec());
637 res
.SetString("origin", origin
);
638 base::JSONWriter::Write(res
, output
);
642 if (name
== "addEntries") {
643 // Add entries to the specified volume.
644 base::JSONValueConverter
<AddEntriesMessage
> add_entries_message_converter
;
645 AddEntriesMessage message
;
646 ASSERT_TRUE(add_entries_message_converter
.Convert(value
, &message
));
648 for (size_t i
= 0; i
< message
.entries
.size(); ++i
) {
649 switch (message
.volume
) {
651 local_volume_
->CreateEntry(*message
.entries
[i
]);
654 if (drive_volume_
.get())
655 drive_volume_
->CreateEntry(*message
.entries
[i
]);
659 usb_volume_
->CreateEntry(*message
.entries
[i
]);
670 if (name
== "mountFakeUsb") {
671 usb_volume_
.reset(new FakeTestVolume("fake-usb",
672 VOLUME_TYPE_REMOVABLE_DISK_PARTITION
,
673 chromeos::DEVICE_TYPE_USB
));
674 usb_volume_
->Mount(profile());
678 if (name
== "mountFakeMtp") {
679 mtp_volume_
.reset(new FakeTestVolume("fake-mtp", VOLUME_TYPE_MTP
,
680 chromeos::DEVICE_TYPE_UNKNOWN
));
681 ASSERT_TRUE(mtp_volume_
->PrepareTestEntries(profile()));
683 mtp_volume_
->Mount(profile());
687 if (name
== "useCellularNetwork") {
688 net::NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests(
689 net::NetworkChangeNotifier::CONNECTION_3G
);
693 if (name
== "clickNotificationButton") {
694 std::string extension_id
;
695 std::string notification_id
;
697 ASSERT_TRUE(value
.GetString("extensionId", &extension_id
));
698 ASSERT_TRUE(value
.GetString("notificationId", ¬ification_id
));
699 ASSERT_TRUE(value
.GetInteger("index", &index
));
701 const std::string delegate_id
= extension_id
+ "-" + notification_id
;
702 const Notification
* notification
=
703 g_browser_process
->notification_ui_manager()->FindById(delegate_id
,
705 ASSERT_TRUE(notification
);
707 notification
->delegate()->ButtonClick(index
);
711 if (name
== "installProviderExtension") {
712 std::string manifest
;
713 ASSERT_TRUE(value
.GetString("manifest", &manifest
));
714 InstallExtension(base::FilePath(FILE_PATH_LITERAL(
715 "ui/file_manager/integration_tests/testing_provider")),
720 FAIL() << "Unknown test message: " << name
;
723 drive::DriveIntegrationService
*
724 FileManagerBrowserTestBase::CreateDriveIntegrationService(Profile
* profile
) {
725 drive_volumes_
[profile
->GetOriginalProfile()].reset(new DriveTestVolume());
726 return drive_volumes_
[profile
->GetOriginalProfile()]
727 ->CreateDriveIntegrationService(profile
);
730 } // namespace file_manager