| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/web_applications/web_app_win.h" | 5 #include "chrome/browser/web_applications/web_app_win.h" |
| 6 | 6 |
| 7 #include <shlobj.h> | 7 #include <shlobj.h> |
| 8 | 8 |
| 9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
| 10 #include "base/files/file_enumerator.h" | 10 #include "base/files/file_enumerator.h" |
| 11 #include "base/files/file_util.h" | 11 #include "base/files/file_util.h" |
| 12 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "base/md5.h" | 13 #include "base/md5.h" |
| 14 #include "base/memory/scoped_ptr.h" |
| 14 #include "base/path_service.h" | 15 #include "base/path_service.h" |
| 15 #include "base/strings/string16.h" | 16 #include "base/strings/string16.h" |
| 16 #include "base/strings/string_piece.h" | 17 #include "base/strings/string_piece.h" |
| 17 #include "base/strings/stringprintf.h" | 18 #include "base/strings/stringprintf.h" |
| 18 #include "base/strings/utf_string_conversions.h" | 19 #include "base/strings/utf_string_conversions.h" |
| 19 #include "base/win/shortcut.h" | 20 #include "base/win/shortcut.h" |
| 20 #include "base/win/windows_version.h" | 21 #include "base/win/windows_version.h" |
| 21 #include "chrome/browser/profiles/profile.h" | 22 #include "chrome/browser/profiles/profile.h" |
| 22 #include "chrome/browser/web_applications/update_shortcut_worker_win.h" | 23 #include "chrome/browser/web_applications/update_shortcut_worker_win.h" |
| 23 #include "chrome/common/chrome_switches.h" | 24 #include "chrome/common/chrome_switches.h" |
| (...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 327 for (std::vector<base::FilePath>::const_iterator j = shortcut_files.begin(); | 328 for (std::vector<base::FilePath>::const_iterator j = shortcut_files.begin(); |
| 328 j != shortcut_files.end(); ++j) { | 329 j != shortcut_files.end(); ++j) { |
| 329 // Any shortcut could have been pinned, either by chrome or the user, so | 330 // Any shortcut could have been pinned, either by chrome or the user, so |
| 330 // they are all unpinned. | 331 // they are all unpinned. |
| 331 base::win::TaskbarUnpinShortcutLink(j->value().c_str()); | 332 base::win::TaskbarUnpinShortcutLink(j->value().c_str()); |
| 332 base::DeleteFile(*j, false); | 333 base::DeleteFile(*j, false); |
| 333 } | 334 } |
| 334 } | 335 } |
| 335 } | 336 } |
| 336 | 337 |
| 337 void CreateIconAndSetRelaunchDetails(const base::FilePath& web_app_path, | 338 void CreateIconAndSetRelaunchDetails( |
| 338 const base::FilePath& icon_file, | 339 const base::FilePath& web_app_path, |
| 339 const web_app::ShortcutInfo& shortcut_info, | 340 const base::FilePath& icon_file, |
| 340 HWND hwnd) { | 341 scoped_ptr<web_app::ShortcutInfo> shortcut_info, |
| 342 HWND hwnd) { |
| 341 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | 343 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); |
| 342 | 344 |
| 343 base::CommandLine command_line = | 345 base::CommandLine command_line = ShellIntegration::CommandLineArgsForLauncher( |
| 344 ShellIntegration::CommandLineArgsForLauncher(shortcut_info.url, | 346 shortcut_info->url, shortcut_info->extension_id, |
| 345 shortcut_info.extension_id, | 347 shortcut_info->profile_path); |
| 346 shortcut_info.profile_path); | |
| 347 | 348 |
| 348 base::FilePath chrome_exe; | 349 base::FilePath chrome_exe; |
| 349 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { | 350 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { |
| 350 NOTREACHED(); | 351 NOTREACHED(); |
| 351 return; | 352 return; |
| 352 } | 353 } |
| 353 command_line.SetProgram(chrome_exe); | 354 command_line.SetProgram(chrome_exe); |
| 354 ui::win::SetRelaunchDetailsForWindow( | 355 ui::win::SetRelaunchDetailsForWindow(command_line.GetCommandLineString(), |
| 355 command_line.GetCommandLineString(), shortcut_info.title, hwnd); | 356 shortcut_info->title, hwnd); |
| 356 | 357 |
| 357 if (!base::PathExists(web_app_path) && !base::CreateDirectory(web_app_path)) | 358 if (!base::PathExists(web_app_path) && !base::CreateDirectory(web_app_path)) |
| 358 return; | 359 return; |
| 359 | 360 |
| 360 ui::win::SetAppIconForWindow(icon_file.value(), hwnd); | 361 ui::win::SetAppIconForWindow(icon_file.value(), hwnd); |
| 361 web_app::internals::CheckAndSaveIcon(icon_file, shortcut_info.favicon, true); | 362 web_app::internals::CheckAndSaveIcon(icon_file, shortcut_info->favicon, true); |
| 362 } | 363 } |
| 363 | 364 |
| 364 void OnShortcutInfoLoadedForSetRelaunchDetails( | 365 void OnShortcutInfoLoadedForSetRelaunchDetails( |
| 365 HWND hwnd, | 366 HWND hwnd, |
| 366 const web_app::ShortcutInfo& shortcut_info) { | 367 scoped_ptr<web_app::ShortcutInfo> shortcut_info) { |
| 367 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 368 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 368 | 369 |
| 369 // Set window's icon to the one we're about to create/update in the web app | 370 // Set window's icon to the one we're about to create/update in the web app |
| 370 // path. The icon cache will refresh on icon creation. | 371 // path. The icon cache will refresh on icon creation. |
| 371 base::FilePath web_app_path = | 372 base::FilePath web_app_path = web_app::GetWebAppDataDirectory( |
| 372 web_app::GetWebAppDataDirectory(shortcut_info.profile_path, | 373 shortcut_info->profile_path, shortcut_info->extension_id, |
| 373 shortcut_info.extension_id, | 374 shortcut_info->url); |
| 374 shortcut_info.url); | |
| 375 base::FilePath icon_file = | 375 base::FilePath icon_file = |
| 376 web_app::internals::GetIconFilePath(web_app_path, shortcut_info.title); | 376 web_app::internals::GetIconFilePath(web_app_path, shortcut_info->title); |
| 377 content::BrowserThread::PostBlockingPoolTask( | 377 content::BrowserThread::PostBlockingPoolTask( |
| 378 FROM_HERE, | 378 FROM_HERE, base::Bind(&CreateIconAndSetRelaunchDetails, web_app_path, |
| 379 base::Bind(&CreateIconAndSetRelaunchDetails, | 379 icon_file, base::Passed(&shortcut_info), hwnd)); |
| 380 web_app_path, | |
| 381 icon_file, | |
| 382 shortcut_info, | |
| 383 hwnd)); | |
| 384 } | 380 } |
| 385 | 381 |
| 386 // Creates an "app shim exe" by linking or copying the generic app shim exe. | 382 // Creates an "app shim exe" by linking or copying the generic app shim exe. |
| 387 // This is the binary that will be run when the user opens a file with this | 383 // This is the binary that will be run when the user opens a file with this |
| 388 // application. The name and icon of the binary will be used on the Open With | 384 // application. The name and icon of the binary will be used on the Open With |
| 389 // menu. For this reason, we cannot simply launch chrome.exe. We give the app | 385 // menu. For this reason, we cannot simply launch chrome.exe. We give the app |
| 390 // shim exe the same name as the application (with no ".exe" extension), so that | 386 // shim exe the same name as the application (with no ".exe" extension), so that |
| 391 // the correct title will appear on the Open With menu. (Note: we also need a | 387 // the correct title will appear on the Open With menu. (Note: we also need a |
| 392 // separate binary per app because Windows only allows a single association with | 388 // separate binary per app because Windows only allows a single association with |
| 393 // each executable.) | 389 // each executable.) |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 518 nullptr, nullptr); | 514 nullptr, nullptr); |
| 519 } | 515 } |
| 520 | 516 |
| 521 return num_successes == file_handlers_info.size(); | 517 return num_successes == file_handlers_info.size(); |
| 522 } | 518 } |
| 523 | 519 |
| 524 } // namespace | 520 } // namespace |
| 525 | 521 |
| 526 namespace web_app { | 522 namespace web_app { |
| 527 | 523 |
| 528 base::FilePath CreateShortcutInWebAppDir(const base::FilePath& web_app_dir, | 524 base::FilePath CreateShortcutInWebAppDir( |
| 529 const ShortcutInfo& shortcut_info) { | 525 const base::FilePath& web_app_dir, |
| 526 scoped_ptr<ShortcutInfo> shortcut_info) { |
| 530 std::vector<base::FilePath> paths; | 527 std::vector<base::FilePath> paths; |
| 531 paths.push_back(web_app_dir); | 528 paths.push_back(web_app_dir); |
| 532 std::vector<base::FilePath> out_filenames; | 529 std::vector<base::FilePath> out_filenames; |
| 533 base::FilePath web_app_dir_shortcut = | 530 base::FilePath web_app_dir_shortcut = |
| 534 web_app_dir.Append(internals::GetSanitizedFileName(shortcut_info.title)) | 531 web_app_dir.Append(internals::GetSanitizedFileName(shortcut_info->title)) |
| 535 .AddExtension(installer::kLnkExt); | 532 .AddExtension(installer::kLnkExt); |
| 536 if (!PathExists(web_app_dir_shortcut)) { | 533 if (!PathExists(web_app_dir_shortcut)) { |
| 537 CreateShortcutsInPaths(web_app_dir, | 534 CreateShortcutsInPaths(web_app_dir, *shortcut_info, paths, |
| 538 shortcut_info, | 535 SHORTCUT_CREATION_BY_USER, &out_filenames); |
| 539 paths, | |
| 540 SHORTCUT_CREATION_BY_USER, | |
| 541 &out_filenames); | |
| 542 DCHECK_EQ(out_filenames.size(), 1u); | 536 DCHECK_EQ(out_filenames.size(), 1u); |
| 543 DCHECK_EQ(out_filenames[0].value(), web_app_dir_shortcut.value()); | 537 DCHECK_EQ(out_filenames[0].value(), web_app_dir_shortcut.value()); |
| 544 } else { | 538 } else { |
| 545 internals::CheckAndSaveIcon( | 539 internals::CheckAndSaveIcon( |
| 546 internals::GetIconFilePath(web_app_dir, shortcut_info.title), | 540 internals::GetIconFilePath(web_app_dir, shortcut_info->title), |
| 547 shortcut_info.favicon, true); | 541 shortcut_info->favicon, true); |
| 548 } | 542 } |
| 549 return web_app_dir_shortcut; | 543 return web_app_dir_shortcut; |
| 550 } | 544 } |
| 551 | 545 |
| 552 void UpdateRelaunchDetailsForApp(Profile* profile, | 546 void UpdateRelaunchDetailsForApp(Profile* profile, |
| 553 const extensions::Extension* extension, | 547 const extensions::Extension* extension, |
| 554 HWND hwnd) { | 548 HWND hwnd) { |
| 555 web_app::GetShortcutInfoForApp( | 549 web_app::GetShortcutInfoForApp( |
| 556 extension, | 550 extension, |
| 557 profile, | 551 profile, |
| (...skipping 21 matching lines...) Expand all Loading... |
| 579 // see explorer rebuilding the icon cache. It would be great that we find | 573 // see explorer rebuilding the icon cache. It would be great that we find |
| 580 // a better way to achieve this. | 574 // a better way to achieve this. |
| 581 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST | SHCNF_FLUSHNOWAIT, NULL, | 575 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST | SHCNF_FLUSHNOWAIT, NULL, |
| 582 NULL); | 576 NULL); |
| 583 } | 577 } |
| 584 return true; | 578 return true; |
| 585 } | 579 } |
| 586 | 580 |
| 587 bool CreatePlatformShortcuts( | 581 bool CreatePlatformShortcuts( |
| 588 const base::FilePath& web_app_path, | 582 const base::FilePath& web_app_path, |
| 589 const ShortcutInfo& shortcut_info, | 583 scoped_ptr<ShortcutInfo> shortcut_info, |
| 590 const extensions::FileHandlersInfo& file_handlers_info, | 584 const extensions::FileHandlersInfo& file_handlers_info, |
| 591 const ShortcutLocations& creation_locations, | 585 const ShortcutLocations& creation_locations, |
| 592 ShortcutCreationReason creation_reason) { | 586 ShortcutCreationReason creation_reason) { |
| 593 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); | 587 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); |
| 594 | 588 |
| 595 // Nothing to do on Windows for hidden apps. | 589 // Nothing to do on Windows for hidden apps. |
| 596 if (creation_locations.applications_menu_location == APP_MENU_LOCATION_HIDDEN) | 590 if (creation_locations.applications_menu_location == APP_MENU_LOCATION_HIDDEN) |
| 597 return true; | 591 return true; |
| 598 | 592 |
| 599 // Shortcut paths under which to create shortcuts. | 593 // Shortcut paths under which to create shortcuts. |
| 600 std::vector<base::FilePath> shortcut_paths = | 594 std::vector<base::FilePath> shortcut_paths = |
| 601 GetShortcutPaths(creation_locations); | 595 GetShortcutPaths(creation_locations); |
| 602 | 596 |
| 603 bool pin_to_taskbar = creation_locations.in_quick_launch_bar && | 597 bool pin_to_taskbar = creation_locations.in_quick_launch_bar && |
| 604 (base::win::GetVersion() >= base::win::VERSION_WIN7); | 598 (base::win::GetVersion() >= base::win::VERSION_WIN7); |
| 605 | 599 |
| 606 // Create/update the shortcut in the web app path for the "Pin To Taskbar" | 600 // Create/update the shortcut in the web app path for the "Pin To Taskbar" |
| 607 // option in Win7. We use the web app path shortcut because we will overwrite | 601 // option in Win7. We use the web app path shortcut because we will overwrite |
| 608 // it rather than appending unique numbers if the shortcut already exists. | 602 // it rather than appending unique numbers if the shortcut already exists. |
| 609 // This prevents pinned apps from having unique numbers in their names. | 603 // This prevents pinned apps from having unique numbers in their names. |
| 610 if (pin_to_taskbar) | 604 if (pin_to_taskbar) |
| 611 shortcut_paths.push_back(web_app_path); | 605 shortcut_paths.push_back(web_app_path); |
| 612 | 606 |
| 613 if (shortcut_paths.empty()) | 607 if (shortcut_paths.empty()) |
| 614 return false; | 608 return false; |
| 615 | 609 |
| 616 if (!CreateShortcutsInPaths(web_app_path, shortcut_info, shortcut_paths, | 610 if (!CreateShortcutsInPaths(web_app_path, *shortcut_info, shortcut_paths, |
| 617 creation_reason, NULL)) | 611 creation_reason, NULL)) |
| 618 return false; | 612 return false; |
| 619 | 613 |
| 620 if (pin_to_taskbar) { | 614 if (pin_to_taskbar) { |
| 621 base::FilePath file_name = GetSanitizedFileName(shortcut_info.title); | 615 base::FilePath file_name = GetSanitizedFileName(shortcut_info->title); |
| 622 // Use the web app path shortcut for pinning to avoid having unique numbers | 616 // Use the web app path shortcut for pinning to avoid having unique numbers |
| 623 // in the application name. | 617 // in the application name. |
| 624 base::FilePath shortcut_to_pin = web_app_path.Append(file_name). | 618 base::FilePath shortcut_to_pin = web_app_path.Append(file_name). |
| 625 AddExtension(installer::kLnkExt); | 619 AddExtension(installer::kLnkExt); |
| 626 if (!base::win::TaskbarPinShortcutLink(shortcut_to_pin.value().c_str())) | 620 if (!base::win::TaskbarPinShortcutLink(shortcut_to_pin.value().c_str())) |
| 627 return false; | 621 return false; |
| 628 } | 622 } |
| 629 | 623 |
| 630 if (base::CommandLine::ForCurrentProcess()->HasSwitch( | 624 if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| 631 switches::kEnableAppsFileAssociations)) { | 625 switches::kEnableAppsFileAssociations)) { |
| 632 CreateFileAssociationsForApp( | 626 CreateFileAssociationsForApp( |
| 633 shortcut_info.extension_id, shortcut_info.title, | 627 shortcut_info->extension_id, shortcut_info->title, |
| 634 shortcut_info.profile_path, file_handlers_info); | 628 shortcut_info->profile_path, file_handlers_info); |
| 635 } | 629 } |
| 636 | 630 |
| 637 return true; | 631 return true; |
| 638 } | 632 } |
| 639 | 633 |
| 640 void UpdatePlatformShortcuts( | 634 void UpdatePlatformShortcuts( |
| 641 const base::FilePath& web_app_path, | 635 const base::FilePath& web_app_path, |
| 642 const base::string16& old_app_title, | 636 const base::string16& old_app_title, |
| 643 const ShortcutInfo& shortcut_info, | 637 scoped_ptr<ShortcutInfo> shortcut_info, |
| 644 const extensions::FileHandlersInfo& file_handlers_info) { | 638 const extensions::FileHandlersInfo& file_handlers_info) { |
| 645 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); | 639 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); |
| 646 | 640 |
| 647 // Generates file name to use with persisted ico and shortcut file. | 641 // Generates file name to use with persisted ico and shortcut file. |
| 648 base::FilePath file_name = | 642 base::FilePath file_name = |
| 649 web_app::internals::GetSanitizedFileName(shortcut_info.title); | 643 web_app::internals::GetSanitizedFileName(shortcut_info->title); |
| 650 | 644 |
| 651 if (old_app_title != shortcut_info.title) { | 645 if (old_app_title != shortcut_info->title) { |
| 652 // The app's title has changed. Delete all existing app shortcuts and | 646 // The app's title has changed. Delete all existing app shortcuts and |
| 653 // recreate them in any locations they already existed (but do not add them | 647 // recreate them in any locations they already existed (but do not add them |
| 654 // to locations where they do not currently exist). | 648 // to locations where they do not currently exist). |
| 655 bool was_pinned_to_taskbar; | 649 bool was_pinned_to_taskbar; |
| 656 std::vector<base::FilePath> shortcut_paths; | 650 std::vector<base::FilePath> shortcut_paths; |
| 657 GetShortcutLocationsAndDeleteShortcuts( | 651 GetShortcutLocationsAndDeleteShortcuts( |
| 658 web_app_path, shortcut_info.profile_path, old_app_title, | 652 web_app_path, shortcut_info->profile_path, old_app_title, |
| 659 &was_pinned_to_taskbar, &shortcut_paths); | 653 &was_pinned_to_taskbar, &shortcut_paths); |
| 660 CreateShortcutsInPaths(web_app_path, shortcut_info, shortcut_paths, | 654 CreateShortcutsInPaths(web_app_path, *shortcut_info, shortcut_paths, |
| 661 SHORTCUT_CREATION_BY_USER, NULL); | 655 SHORTCUT_CREATION_BY_USER, NULL); |
| 662 // If the shortcut was pinned to the taskbar, | 656 // If the shortcut was pinned to the taskbar, |
| 663 // GetShortcutLocationsAndDeleteShortcuts will have deleted it. In that | 657 // GetShortcutLocationsAndDeleteShortcuts will have deleted it. In that |
| 664 // case, re-pin it. | 658 // case, re-pin it. |
| 665 if (was_pinned_to_taskbar) { | 659 if (was_pinned_to_taskbar) { |
| 666 base::FilePath file_name = GetSanitizedFileName(shortcut_info.title); | 660 base::FilePath file_name = GetSanitizedFileName(shortcut_info->title); |
| 667 // Use the web app path shortcut for pinning to avoid having unique | 661 // Use the web app path shortcut for pinning to avoid having unique |
| 668 // numbers in the application name. | 662 // numbers in the application name. |
| 669 base::FilePath shortcut_to_pin = web_app_path.Append(file_name). | 663 base::FilePath shortcut_to_pin = web_app_path.Append(file_name). |
| 670 AddExtension(installer::kLnkExt); | 664 AddExtension(installer::kLnkExt); |
| 671 base::win::TaskbarPinShortcutLink(shortcut_to_pin.value().c_str()); | 665 base::win::TaskbarPinShortcutLink(shortcut_to_pin.value().c_str()); |
| 672 } | 666 } |
| 673 } | 667 } |
| 674 | 668 |
| 675 // Update the icon if necessary. | 669 // Update the icon if necessary. |
| 676 base::FilePath icon_file = GetIconFilePath(web_app_path, shortcut_info.title); | 670 base::FilePath icon_file = |
| 677 CheckAndSaveIcon(icon_file, shortcut_info.favicon, true); | 671 GetIconFilePath(web_app_path, shortcut_info->title); |
| 672 CheckAndSaveIcon(icon_file, shortcut_info->favicon, true); |
| 678 } | 673 } |
| 679 | 674 |
| 680 void DeletePlatformShortcuts(const base::FilePath& web_app_path, | 675 void DeletePlatformShortcuts(const base::FilePath& web_app_path, |
| 681 const ShortcutInfo& shortcut_info) { | 676 scoped_ptr<ShortcutInfo> shortcut_info) { |
| 682 GetShortcutLocationsAndDeleteShortcuts( | 677 GetShortcutLocationsAndDeleteShortcuts(web_app_path, |
| 683 web_app_path, shortcut_info.profile_path, shortcut_info.title, NULL, | 678 shortcut_info->profile_path, |
| 684 NULL); | 679 shortcut_info->title, NULL, NULL); |
| 685 | 680 |
| 686 // If there are no more shortcuts in the Chrome Apps subdirectory, remove it. | 681 // If there are no more shortcuts in the Chrome Apps subdirectory, remove it. |
| 687 base::FilePath chrome_apps_dir; | 682 base::FilePath chrome_apps_dir; |
| 688 if (ShellUtil::GetShortcutPath( | 683 if (ShellUtil::GetShortcutPath( |
| 689 ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR, | 684 ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR, |
| 690 BrowserDistribution::GetDistribution(), | 685 BrowserDistribution::GetDistribution(), |
| 691 ShellUtil::CURRENT_USER, | 686 ShellUtil::CURRENT_USER, |
| 692 &chrome_apps_dir)) { | 687 &chrome_apps_dir)) { |
| 693 if (base::IsDirectoryEmpty(chrome_apps_dir)) | 688 if (base::IsDirectoryEmpty(chrome_apps_dir)) |
| 694 base::DeleteFile(chrome_apps_dir, false); | 689 base::DeleteFile(chrome_apps_dir, false); |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 770 | 765 |
| 771 } // namespace internals | 766 } // namespace internals |
| 772 | 767 |
| 773 void UpdateShortcutForTabContents(content::WebContents* web_contents) { | 768 void UpdateShortcutForTabContents(content::WebContents* web_contents) { |
| 774 // UpdateShortcutWorker will delete itself when it's done. | 769 // UpdateShortcutWorker will delete itself when it's done. |
| 775 UpdateShortcutWorker* worker = new UpdateShortcutWorker(web_contents); | 770 UpdateShortcutWorker* worker = new UpdateShortcutWorker(web_contents); |
| 776 worker->Run(); | 771 worker->Run(); |
| 777 } | 772 } |
| 778 | 773 |
| 779 } // namespace web_app | 774 } // namespace web_app |
| OLD | NEW |