Chromium Code Reviews| 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/path_service.h" | 14 #include "base/path_service.h" |
| 15 #include "base/strings/string16.h" | |
| 15 #include "base/strings/string_piece.h" | 16 #include "base/strings/string_piece.h" |
| 16 #include "base/strings/stringprintf.h" | 17 #include "base/strings/stringprintf.h" |
| 17 #include "base/strings/utf_string_conversions.h" | 18 #include "base/strings/utf_string_conversions.h" |
| 18 #include "base/win/shortcut.h" | 19 #include "base/win/shortcut.h" |
| 19 #include "base/win/windows_version.h" | 20 #include "base/win/windows_version.h" |
| 21 #include "chrome/browser/profiles/profile.h" | |
| 20 #include "chrome/browser/web_applications/update_shortcut_worker_win.h" | 22 #include "chrome/browser/web_applications/update_shortcut_worker_win.h" |
| 21 #include "chrome/common/chrome_switches.h" | 23 #include "chrome/common/chrome_switches.h" |
| 24 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h" | |
| 22 #include "chrome/installer/util/browser_distribution.h" | 25 #include "chrome/installer/util/browser_distribution.h" |
| 23 #include "chrome/installer/util/shell_util.h" | 26 #include "chrome/installer/util/shell_util.h" |
| 24 #include "chrome/installer/util/util_constants.h" | 27 #include "chrome/installer/util/util_constants.h" |
| 25 #include "content/public/browser/browser_thread.h" | 28 #include "content/public/browser/browser_thread.h" |
| 29 #include "extensions/common/extension.h" | |
| 30 #include "net/base/mime_util.h" | |
| 26 #include "ui/base/win/shell.h" | 31 #include "ui/base/win/shell.h" |
| 27 #include "ui/gfx/icon_util.h" | 32 #include "ui/gfx/icon_util.h" |
| 28 #include "ui/gfx/image/image.h" | 33 #include "ui/gfx/image/image.h" |
| 29 #include "ui/gfx/image/image_family.h" | 34 #include "ui/gfx/image/image_family.h" |
| 30 | 35 |
| 31 namespace { | 36 namespace { |
| 32 | 37 |
| 33 const base::FilePath::CharType kIconChecksumFileExt[] = | 38 const base::FilePath::CharType kIconChecksumFileExt[] = |
| 34 FILE_PATH_LITERAL(".ico.md5"); | 39 FILE_PATH_LITERAL(".ico.md5"); |
| 35 | 40 |
| 41 const base::FilePath::CharType kWinAppShimExe[] = | |
| 42 FILE_PATH_LITERAL("win_app_shim.exe"); | |
| 43 | |
| 36 // Calculates checksum of an icon family using MD5. | 44 // Calculates checksum of an icon family using MD5. |
| 37 // The checksum is derived from all of the icons in the family. | 45 // The checksum is derived from all of the icons in the family. |
| 38 void GetImageCheckSum(const gfx::ImageFamily& image, base::MD5Digest* digest) { | 46 void GetImageCheckSum(const gfx::ImageFamily& image, base::MD5Digest* digest) { |
| 39 DCHECK(digest); | 47 DCHECK(digest); |
| 40 base::MD5Context md5_context; | 48 base::MD5Context md5_context; |
| 41 base::MD5Init(&md5_context); | 49 base::MD5Init(&md5_context); |
| 42 | 50 |
| 43 for (gfx::ImageFamily::const_iterator it = image.begin(); it != image.end(); | 51 for (gfx::ImageFamily::const_iterator it = image.begin(); it != image.end(); |
| 44 ++it) { | 52 ++it) { |
| 45 SkBitmap bitmap = it->AsBitmap(); | 53 SkBitmap bitmap = it->AsBitmap(); |
| (...skipping 309 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 355 HWND hwnd, | 363 HWND hwnd, |
| 356 const web_app::ShortcutInfo& shortcut_info) { | 364 const web_app::ShortcutInfo& shortcut_info) { |
| 357 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 365 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 358 | 366 |
| 359 // Set window's icon to the one we're about to create/update in the web app | 367 // Set window's icon to the one we're about to create/update in the web app |
| 360 // path. The icon cache will refresh on icon creation. | 368 // path. The icon cache will refresh on icon creation. |
| 361 base::FilePath web_app_path = | 369 base::FilePath web_app_path = |
| 362 web_app::GetWebAppDataDirectory(shortcut_info.profile_path, | 370 web_app::GetWebAppDataDirectory(shortcut_info.profile_path, |
| 363 shortcut_info.extension_id, | 371 shortcut_info.extension_id, |
| 364 shortcut_info.url); | 372 shortcut_info.url); |
| 365 base::FilePath icon_file = | 373 base::FilePath icon_file = GetIconFilePath(web_app_path, shortcut_info.title); |
| 366 web_app_path.Append(web_app::internals::GetSanitizedFileName( | |
| 367 shortcut_info.title)) | |
| 368 .ReplaceExtension(FILE_PATH_LITERAL(".ico")); | |
| 369 | |
| 370 content::BrowserThread::PostBlockingPoolTask( | 374 content::BrowserThread::PostBlockingPoolTask( |
| 371 FROM_HERE, | 375 FROM_HERE, |
| 372 base::Bind(&CreateIconAndSetRelaunchDetails, | 376 base::Bind(&CreateIconAndSetRelaunchDetails, |
| 373 web_app_path, | 377 web_app_path, |
| 374 icon_file, | 378 icon_file, |
| 375 shortcut_info, | 379 shortcut_info, |
| 376 hwnd)); | 380 hwnd)); |
| 377 } | 381 } |
| 378 | 382 |
| 383 // Creates an "app shim exe" by linking or copying the generic app shim exe. | |
| 384 // This is the binary that will be run when the user opens a file with this | |
| 385 // application. The name and icon of the binary will be used on the Open With | |
| 386 // menu. For this reason, we cannot simply launch chrome.exe. We give the app | |
| 387 // shim exe the same name as the application (with no ".exe" extension), so that | |
| 388 // the correct title will appear on the Open With menu. (Note: we also need a | |
| 389 // separate binary per app because Windows only allows a single association with | |
| 390 // each executable.) | |
| 391 // |file_name| is the full path of the shim binary to be created. | |
|
jackhou1
2014/10/27 02:13:28
Should this say "|path| is the full ..."?
Matt Giuca
2014/11/04 06:00:09
Done.
| |
| 392 bool CreateAppShimBinary(const base::FilePath& path) { | |
| 393 // TODO(mgiuca): Hard-link instead of copying, if on the same file system. | |
| 394 base::FilePath chrome_binary_directory; | |
| 395 if (!PathService::Get(base::DIR_EXE, &chrome_binary_directory)) { | |
| 396 NOTREACHED(); | |
| 397 return false; | |
| 398 } | |
| 399 base::FilePath generic_shim_path = | |
| 400 chrome_binary_directory.Append(kWinAppShimExe); | |
| 401 LOG(INFO) << "Copying shim exe from " << generic_shim_path.value() << " to " | |
| 402 << path.value(); | |
| 403 if (!base::CopyFile(generic_shim_path, path)) { | |
| 404 LOG(ERROR) << "Could not copy app shim exe to " << path.value(); | |
| 405 return false; | |
| 406 } | |
| 407 | |
| 408 return true; | |
| 409 } | |
| 410 | |
| 411 // Gets the full command line for calling the shim binary. This will include a | |
| 412 // placeholder "%1" argument, which Windows will substitute with the filename | |
| 413 // chosen by the user. | |
| 414 base::CommandLine GetAppShimCommandLine(const base::FilePath& app_shim_path, | |
| 415 const std::string& extension_id, | |
| 416 const base::FilePath& profile_path) { | |
| 417 // Get the command-line to pass to the shim (e.g., "chrome.exe --app-id=..."). | |
| 418 base::FilePath chrome_exe; | |
| 419 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { | |
| 420 NOTREACHED(); | |
| 421 return base::CommandLine(base::CommandLine::NO_PROGRAM); | |
| 422 } | |
| 423 CommandLine chrome_cmd_line = ShellIntegration::CommandLineArgsForLauncher( | |
| 424 GURL(), extension_id, profile_path); | |
| 425 chrome_cmd_line.SetProgram(chrome_exe); | |
| 426 chrome_cmd_line.AppendArg("%1"); | |
| 427 | |
| 428 // Get the command-line for calling the shim (e.g., | |
| 429 // "app_shim -- chrome.exe --app-id=..."). | |
| 430 CommandLine shim_cmd_line(app_shim_path); | |
| 431 // Ensure all subsequent switches are treated as args to the shim. | |
| 432 shim_cmd_line.AppendArg("--"); | |
| 433 for (const auto& arg : chrome_cmd_line.argv()) | |
| 434 shim_cmd_line.AppendArgNative(arg); | |
| 435 | |
| 436 return shim_cmd_line; | |
| 437 } | |
| 438 | |
| 439 // Gets the set of file extensions associated with a particular file handler. | |
| 440 // Uses both the MIME types and extensions. | |
| 441 void GetHandlerFileExtensions(const extensions::FileHandlerInfo& handler, | |
| 442 std::set<base::string16>* exts) { | |
| 443 for (const auto& mime : handler.types) { | |
| 444 std::vector<base::string16> mime_type_extensions; | |
| 445 net::GetExtensionsForMimeType(mime, &mime_type_extensions); | |
| 446 exts->insert(mime_type_extensions.begin(), mime_type_extensions.end()); | |
| 447 } | |
| 448 for (const auto& ext : handler.extensions) | |
| 449 exts->insert(base::UTF8ToUTF16(ext)); | |
| 450 } | |
| 451 | |
| 452 // Creates operating system file type associations for a given app. | |
| 453 // This is the platform specific implementation of the CreateFileAssociations | |
| 454 // function, and is executed on the FILE thread. | |
| 455 // Returns true on success, false on failure. | |
| 456 bool CreateFileAssociationsForApp( | |
| 457 const std::string& extension_id, | |
| 458 const base::string16& title, | |
| 459 const base::FilePath& profile_path, | |
| 460 const extensions::FileHandlersInfo& file_handlers_info) { | |
| 461 LOG(INFO) << "CreateFileAssociationsForApp: " << title; | |
| 462 | |
| 463 base::FilePath web_app_path = | |
| 464 web_app::GetWebAppDataDirectory(profile_path, extension_id, GURL()); | |
| 465 base::FilePath file_name = web_app::internals::GetSanitizedFileName(title); | |
| 466 | |
| 467 // The progid is "chrome-APPID-HANDLERID". This is the internal name Windows | |
| 468 // will use for file associations with this application. | |
| 469 base::string16 progid_base = L"chrome-"; | |
| 470 progid_base += base::UTF8ToUTF16(extension_id); | |
| 471 | |
| 472 // Create the app shim binary (see CreateAppShimBinary for rationale). Get the | |
| 473 // command line for the shim. | |
| 474 base::FilePath app_shim_path = web_app_path.Append(file_name); | |
| 475 if (!CreateAppShimBinary(app_shim_path)) | |
| 476 return false; | |
|
jackhou1
2014/10/27 02:13:28
Blank line after return.
Matt Giuca
2014/11/04 06:00:09
Done.
| |
| 477 CommandLine shim_cmd_line( | |
| 478 GetAppShimCommandLine(app_shim_path, extension_id, profile_path)); | |
| 479 | |
| 480 // TODO(mgiuca): Get the file type name from the manifest, or generate a | |
| 481 // default one. (If this is blank, Windows will generate one of the form | |
| 482 // '<EXT> file'.) | |
| 483 base::string16 file_type_name = L""; | |
| 484 | |
| 485 // TODO(mgiuca): Generate a new icon for this application's file associations | |
| 486 // that looks like a page with the application icon inside. | |
| 487 base::FilePath icon_file = GetIconFilePath(web_app_path, title); | |
| 488 | |
| 489 // Create a separate file association (ProgId) for each handler. This allows | |
| 490 // each handler to have its own filetype name and icon, and also a different | |
| 491 // command line (so the app can see which handler was invoked). | |
| 492 for (const auto& handler : file_handlers_info) { | |
| 493 base::string16 progid = progid_base + L"-" + base::UTF8ToUTF16(handler.id); | |
| 494 | |
| 495 std::set<base::string16> exts; | |
| 496 GetHandlerFileExtensions(handler, &exts); | |
| 497 | |
| 498 if (!ShellUtil::AddFileAssociations( | |
|
jackhou1
2014/10/27 02:13:28
Just something to think about: Would any issues co
Matt Giuca
2014/11/04 06:00:09
Well if the progid doesn't change, then it will ju
| |
| 499 progid, shim_cmd_line, file_type_name, icon_file, exts)) { | |
| 500 return false; | |
|
jackhou1
2014/10/27 02:13:28
Should we delete the shim if this fails?
Matt Giuca
2014/11/04 06:00:09
Done.
| |
| 501 } | |
| 502 } | |
| 503 | |
| 504 // Success. Tell Windows Explorer to update its cache. | |
| 505 SHChangeNotify( | |
| 506 SHCNE_ASSOCCHANGED, SHCNF_IDLIST | SHCNF_FLUSHNOWAIT, nullptr, nullptr); | |
| 507 return true; | |
| 508 } | |
| 509 | |
| 379 } // namespace | 510 } // namespace |
| 380 | 511 |
| 381 namespace web_app { | 512 namespace web_app { |
| 382 | 513 |
| 383 base::FilePath CreateShortcutInWebAppDir(const base::FilePath& web_app_dir, | 514 base::FilePath CreateShortcutInWebAppDir(const base::FilePath& web_app_dir, |
| 384 const ShortcutInfo& shortcut_info) { | 515 const ShortcutInfo& shortcut_info) { |
| 385 std::vector<base::FilePath> paths; | 516 std::vector<base::FilePath> paths; |
| 386 paths.push_back(web_app_dir); | 517 paths.push_back(web_app_dir); |
| 387 std::vector<base::FilePath> out_filenames; | 518 std::vector<base::FilePath> out_filenames; |
| 388 base::FilePath web_app_dir_shortcut = | 519 base::FilePath web_app_dir_shortcut = |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 476 if (pin_to_taskbar) { | 607 if (pin_to_taskbar) { |
| 477 base::FilePath file_name = GetSanitizedFileName(shortcut_info.title); | 608 base::FilePath file_name = GetSanitizedFileName(shortcut_info.title); |
| 478 // Use the web app path shortcut for pinning to avoid having unique numbers | 609 // Use the web app path shortcut for pinning to avoid having unique numbers |
| 479 // in the application name. | 610 // in the application name. |
| 480 base::FilePath shortcut_to_pin = web_app_path.Append(file_name). | 611 base::FilePath shortcut_to_pin = web_app_path.Append(file_name). |
| 481 AddExtension(installer::kLnkExt); | 612 AddExtension(installer::kLnkExt); |
| 482 if (!base::win::TaskbarPinShortcutLink(shortcut_to_pin.value().c_str())) | 613 if (!base::win::TaskbarPinShortcutLink(shortcut_to_pin.value().c_str())) |
| 483 return false; | 614 return false; |
| 484 } | 615 } |
| 485 | 616 |
| 617 if (switches::kEnableAppsFileAssociations) { | |
| 618 CreateFileAssociationsForApp(shortcut_info.extension_id, | |
| 619 shortcut_info.title, | |
| 620 shortcut_info.profile_path, | |
| 621 file_handlers_info); | |
| 622 } | |
| 623 | |
| 486 return true; | 624 return true; |
| 487 } | 625 } |
| 488 | 626 |
| 489 void UpdatePlatformShortcuts( | 627 void UpdatePlatformShortcuts( |
| 490 const base::FilePath& web_app_path, | 628 const base::FilePath& web_app_path, |
| 491 const base::string16& old_app_title, | 629 const base::string16& old_app_title, |
| 492 const ShortcutInfo& shortcut_info, | 630 const ShortcutInfo& shortcut_info, |
| 493 const extensions::FileHandlersInfo& file_handlers_info) { | 631 const extensions::FileHandlersInfo& file_handlers_info) { |
| 494 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); | 632 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); |
| 495 | 633 |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 619 | 757 |
| 620 } // namespace internals | 758 } // namespace internals |
| 621 | 759 |
| 622 void UpdateShortcutForTabContents(content::WebContents* web_contents) { | 760 void UpdateShortcutForTabContents(content::WebContents* web_contents) { |
| 623 // UpdateShortcutWorker will delete itself when it's done. | 761 // UpdateShortcutWorker will delete itself when it's done. |
| 624 UpdateShortcutWorker* worker = new UpdateShortcutWorker(web_contents); | 762 UpdateShortcutWorker* worker = new UpdateShortcutWorker(web_contents); |
| 625 worker->Run(); | 763 worker->Run(); |
| 626 } | 764 } |
| 627 | 765 |
| 628 } // namespace web_app | 766 } // namespace web_app |
| OLD | NEW |