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/shell_integration_linux.h" | 5 #include "chrome/browser/shell_integration_linux.h" |
| 6 | 6 |
| 7 #include <fcntl.h> | 7 #include <fcntl.h> |
| 8 #include <glib.h> | 8 #include <glib.h> |
| 9 #include <stdlib.h> | 9 #include <stdlib.h> |
| 10 #include <sys/stat.h> | 10 #include <sys/stat.h> |
| (...skipping 376 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 387 // xdg-settings failed: we can't determine or set the default browser. | 387 // xdg-settings failed: we can't determine or set the default browser. |
| 388 return ShellIntegration::UNKNOWN_DEFAULT; | 388 return ShellIntegration::UNKNOWN_DEFAULT; |
| 389 } | 389 } |
| 390 | 390 |
| 391 // Allow any reply that starts with "yes". | 391 // Allow any reply that starts with "yes". |
| 392 return (reply.find("yes") == 0) ? ShellIntegration::IS_DEFAULT : | 392 return (reply.find("yes") == 0) ? ShellIntegration::IS_DEFAULT : |
| 393 ShellIntegration::NOT_DEFAULT; | 393 ShellIntegration::NOT_DEFAULT; |
| 394 #endif | 394 #endif |
| 395 } | 395 } |
| 396 | 396 |
| 397 // Get the value of NoDisplay from the [Desktop Entry] section of a .desktop | |
| 398 // file, given in |shortcut_contents|. If the key is not found, returns false. | |
| 399 bool GetNoDisplayFromDesktopFile(const std::string& shortcut_contents) { | |
| 400 GKeyFile* key_file = g_key_file_new(); | |
| 401 GError* err = NULL; | |
| 402 if (!g_key_file_load_from_data(key_file, shortcut_contents.c_str(), | |
| 403 shortcut_contents.size(), G_KEY_FILE_NONE, | |
| 404 &err)) { | |
| 405 LOG(WARNING) << "Unable to read desktop file template: " << err->message; | |
| 406 g_error_free(err); | |
| 407 return false; | |
| 408 } | |
| 409 | |
| 410 bool nodisplay = false; | |
| 411 char* nodisplay_c_string = g_key_file_get_string(key_file, kDesktopEntry, | |
| 412 "NoDisplay", &err); | |
| 413 if (nodisplay_c_string) { | |
| 414 if (!g_strcmp0(nodisplay_c_string, "true")) | |
| 415 nodisplay = true; | |
| 416 g_free(nodisplay_c_string); | |
| 417 } | |
| 418 | |
| 419 g_key_file_free(key_file); | |
| 420 return nodisplay; | |
| 421 } | |
| 422 | |
| 397 } // namespace | 423 } // namespace |
| 398 | 424 |
| 399 // static | 425 // static |
| 400 ShellIntegration::DefaultWebClientSetPermission | 426 ShellIntegration::DefaultWebClientSetPermission |
| 401 ShellIntegration::CanSetAsDefaultBrowser() { | 427 ShellIntegration::CanSetAsDefaultBrowser() { |
| 402 return SET_DEFAULT_UNATTENDED; | 428 return SET_DEFAULT_UNATTENDED; |
| 403 } | 429 } |
| 404 | 430 |
| 405 // static | 431 // static |
| 406 bool ShellIntegration::SetAsDefaultBrowser() { | 432 bool ShellIntegration::SetAsDefaultBrowser() { |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 450 // Allow $CHROME_DESKTOP to override the built-in value, so that development | 476 // Allow $CHROME_DESKTOP to override the built-in value, so that development |
| 451 // versions can set themselves as the default without interfering with | 477 // versions can set themselves as the default without interfering with |
| 452 // non-official, packaged versions using the built-in value. | 478 // non-official, packaged versions using the built-in value. |
| 453 std::string name; | 479 std::string name; |
| 454 if (env->GetVar("CHROME_DESKTOP", &name) && !name.empty()) | 480 if (env->GetVar("CHROME_DESKTOP", &name) && !name.empty()) |
| 455 return name; | 481 return name; |
| 456 return "chromium-browser.desktop"; | 482 return "chromium-browser.desktop"; |
| 457 #endif | 483 #endif |
| 458 } | 484 } |
| 459 | 485 |
| 460 bool GetDesktopShortcutTemplate(base::Environment* env, | 486 ShellIntegration::ShortcutLocations GetExistingShortcutLocations( |
| 461 std::string* output) { | 487 base::Environment* env, |
| 488 const base::FilePath& profile_path, | |
| 489 const std::string& extension_id) { | |
| 490 base::FilePath desktop_path; | |
| 491 // If Get returns false, just leave desktop_path empty. | |
| 492 PathService::Get(base::DIR_USER_DESKTOP, &desktop_path); | |
| 493 return GetExistingShortcutLocations(env, profile_path, extension_id, | |
| 494 desktop_path); | |
| 495 } | |
| 496 | |
| 497 ShellIntegration::ShortcutLocations GetExistingShortcutLocations( | |
| 498 base::Environment* env, | |
| 499 const base::FilePath& profile_path, | |
| 500 const std::string& extension_id, | |
| 501 const base::FilePath& desktop_path) { | |
| 502 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 503 | |
| 504 base::FilePath shortcut_filename = GetExtensionShortcutFilename( | |
| 505 profile_path, extension_id); | |
| 506 DCHECK(!shortcut_filename.empty()); | |
| 507 ShellIntegration::ShortcutLocations locations; | |
| 508 | |
| 509 // Determine whether there is a shortcut on desktop. | |
| 510 if (!desktop_path.empty()) { | |
| 511 locations.on_desktop = | |
| 512 file_util::PathExists(desktop_path.Append(shortcut_filename)); | |
| 513 } | |
| 514 | |
| 515 // Determine whether there is a shortcut in applications menu that *does not* | |
| 516 // have NoDisplay=true. | |
|
benwells
2013/03/06 07:44:25
If there is a shortcut that does have NoDisplay=tr
Matt Giuca
2013/03/06 08:11:54
Right again. Done.
| |
| 517 std::string shortcut_contents; | |
| 518 if (GetExistingShortcutContents(env, shortcut_filename, | |
| 519 &shortcut_contents)) { | |
| 520 locations.in_applications_menu = | |
| 521 !GetNoDisplayFromDesktopFile(shortcut_contents); | |
| 522 } | |
| 523 | |
| 524 return locations; | |
| 525 } | |
| 526 | |
| 527 bool GetExistingShortcutContents(base::Environment* env, | |
| 528 const base::FilePath& desktop_filename, | |
| 529 std::string* output) { | |
| 462 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 530 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 463 | 531 |
| 464 std::vector<base::FilePath> search_paths; | 532 std::vector<base::FilePath> search_paths; |
| 465 | 533 |
| 466 // Search paths as specified in the XDG Base Directory Specification. | 534 // Search paths as specified in the XDG Base Directory Specification. |
| 467 // http://standards.freedesktop.org/basedir-spec/latest/ | 535 // http://standards.freedesktop.org/basedir-spec/latest/ |
| 468 std::string xdg_data_home; | 536 std::string xdg_data_home; |
| 469 std::string home; | 537 std::string home; |
| 470 if (env->GetVar("XDG_DATA_HOME", &xdg_data_home) && | 538 if (env->GetVar("XDG_DATA_HOME", &xdg_data_home) && |
| 471 !xdg_data_home.empty()) { | 539 !xdg_data_home.empty()) { |
| 472 search_paths.push_back(base::FilePath(xdg_data_home)); | 540 search_paths.push_back(base::FilePath(xdg_data_home)); |
| 473 } else if (env->GetVar("HOME", &home) && !home.empty()) { | 541 } else if (env->GetVar("HOME", &home) && !home.empty()) { |
| 474 search_paths.push_back(base::FilePath(home).Append(".local").Append( | 542 search_paths.push_back(base::FilePath(home).Append(".local").Append( |
| 475 "share")); | 543 "share")); |
| 476 } | 544 } |
| 477 | 545 |
| 478 std::string xdg_data_dirs; | 546 std::string xdg_data_dirs; |
| 479 if (env->GetVar("XDG_DATA_DIRS", &xdg_data_dirs) && | 547 if (env->GetVar("XDG_DATA_DIRS", &xdg_data_dirs) && |
| 480 !xdg_data_dirs.empty()) { | 548 !xdg_data_dirs.empty()) { |
| 481 base::StringTokenizer tokenizer(xdg_data_dirs, ":"); | 549 base::StringTokenizer tokenizer(xdg_data_dirs, ":"); |
| 482 while (tokenizer.GetNext()) { | 550 while (tokenizer.GetNext()) { |
| 483 base::FilePath data_dir(tokenizer.token()); | 551 base::FilePath data_dir(tokenizer.token()); |
| 484 search_paths.push_back(data_dir); | 552 search_paths.push_back(data_dir); |
| 485 } | 553 } |
| 486 } else { | 554 } else { |
| 487 search_paths.push_back(base::FilePath("/usr/local/share")); | 555 search_paths.push_back(base::FilePath("/usr/local/share")); |
| 488 search_paths.push_back(base::FilePath("/usr/share")); | 556 search_paths.push_back(base::FilePath("/usr/share")); |
| 489 } | 557 } |
| 490 | 558 |
| 491 std::string template_filename(GetDesktopName(env)); | |
| 492 for (std::vector<base::FilePath>::const_iterator i = search_paths.begin(); | 559 for (std::vector<base::FilePath>::const_iterator i = search_paths.begin(); |
| 493 i != search_paths.end(); ++i) { | 560 i != search_paths.end(); ++i) { |
| 494 base::FilePath path = i->Append("applications").Append(template_filename); | 561 base::FilePath path = i->Append("applications").Append(desktop_filename); |
| 495 VLOG(1) << "Looking for desktop file template in " << path.value(); | 562 VLOG(1) << "Looking for desktop file in " << path.value(); |
| 496 if (file_util::PathExists(path)) { | 563 if (file_util::PathExists(path)) { |
| 497 VLOG(1) << "Found desktop file template at " << path.value(); | 564 VLOG(1) << "Found desktop file at " << path.value(); |
| 498 return file_util::ReadFileToString(path, output); | 565 return file_util::ReadFileToString(path, output); |
| 499 } | 566 } |
| 500 } | 567 } |
| 501 | 568 |
| 502 LOG(ERROR) << "Could not find desktop file template."; | |
| 503 return false; | 569 return false; |
| 504 } | 570 } |
| 505 | 571 |
| 572 bool GetDesktopShortcutTemplate(base::Environment* env, | |
| 573 std::string* output) { | |
| 574 base::FilePath template_filename(GetDesktopName(env)); | |
| 575 if (GetExistingShortcutContents(env, template_filename, output)) { | |
| 576 return true; | |
| 577 } else { | |
| 578 LOG(ERROR) << "Could not find desktop file " << template_filename.value() | |
| 579 << "."; | |
| 580 return false; | |
| 581 } | |
| 582 } | |
| 583 | |
| 506 base::FilePath GetWebShortcutFilename(const GURL& url) { | 584 base::FilePath GetWebShortcutFilename(const GURL& url) { |
| 507 // Use a prefix, because xdg-desktop-menu requires it. | 585 // Use a prefix, because xdg-desktop-menu requires it. |
| 508 std::string filename = | 586 std::string filename = |
| 509 std::string(chrome::kBrowserProcessExecutableName) + "-" + url.spec(); | 587 std::string(chrome::kBrowserProcessExecutableName) + "-" + url.spec(); |
| 510 file_util::ReplaceIllegalCharactersInPath(&filename, '_'); | 588 file_util::ReplaceIllegalCharactersInPath(&filename, '_'); |
| 511 | 589 |
| 512 base::FilePath desktop_path; | 590 base::FilePath desktop_path; |
| 513 if (!PathService::Get(base::DIR_USER_DESKTOP, &desktop_path)) | 591 if (!PathService::Get(base::DIR_USER_DESKTOP, &desktop_path)) |
| 514 return base::FilePath(); | 592 return base::FilePath(); |
| 515 | 593 |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 542 } | 620 } |
| 543 | 621 |
| 544 std::string GetDesktopFileContents( | 622 std::string GetDesktopFileContents( |
| 545 const std::string& template_contents, | 623 const std::string& template_contents, |
| 546 const std::string& app_name, | 624 const std::string& app_name, |
| 547 const GURL& url, | 625 const GURL& url, |
| 548 const std::string& extension_id, | 626 const std::string& extension_id, |
| 549 const base::FilePath& extension_path, | 627 const base::FilePath& extension_path, |
| 550 const string16& title, | 628 const string16& title, |
| 551 const std::string& icon_name, | 629 const std::string& icon_name, |
| 552 const base::FilePath& profile_path) { | 630 const base::FilePath& profile_path, |
| 631 bool no_display) { | |
| 553 // Although not required by the spec, Nautilus on Ubuntu Karmic creates its | 632 // Although not required by the spec, Nautilus on Ubuntu Karmic creates its |
| 554 // launchers with an xdg-open shebang. Follow that convention. | 633 // launchers with an xdg-open shebang. Follow that convention. |
| 555 std::string output_buffer = std::string(kXdgOpenShebang) + "\n"; | 634 std::string output_buffer = std::string(kXdgOpenShebang) + "\n"; |
| 556 if (template_contents.empty()) | 635 if (template_contents.empty()) |
| 557 return output_buffer; | 636 return output_buffer; |
| 558 | 637 |
| 559 // See http://standards.freedesktop.org/desktop-entry-spec/latest/ | 638 // See http://standards.freedesktop.org/desktop-entry-spec/latest/ |
| 560 // http://developer.gnome.org/glib/unstable/glib-Key-value-file-parser.html | 639 // http://developer.gnome.org/glib/unstable/glib-Key-value-file-parser.html |
| 561 GKeyFile* key_file = g_key_file_new(); | 640 GKeyFile* key_file = g_key_file_new(); |
| 562 GError* err = NULL; | 641 GError* err = NULL; |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 643 } | 722 } |
| 644 } | 723 } |
| 645 | 724 |
| 646 g_key_file_set_string(key_file, kDesktopEntry, "Exec", final_path.c_str()); | 725 g_key_file_set_string(key_file, kDesktopEntry, "Exec", final_path.c_str()); |
| 647 } | 726 } |
| 648 | 727 |
| 649 // Set the "Icon" key. | 728 // Set the "Icon" key. |
| 650 if (!icon_name.empty()) | 729 if (!icon_name.empty()) |
| 651 g_key_file_set_string(key_file, kDesktopEntry, "Icon", icon_name.c_str()); | 730 g_key_file_set_string(key_file, kDesktopEntry, "Icon", icon_name.c_str()); |
| 652 | 731 |
| 732 // Set the "NoDisplay" key. | |
| 733 if (no_display) | |
| 734 g_key_file_set_string(key_file, kDesktopEntry, "NoDisplay", "true"); | |
| 735 | |
| 653 #if defined(TOOLKIT_GTK) | 736 #if defined(TOOLKIT_GTK) |
| 654 std::string wmclass = web_app::GetWMClassFromAppName(app_name); | 737 std::string wmclass = web_app::GetWMClassFromAppName(app_name); |
| 655 g_key_file_set_string(key_file, kDesktopEntry, "StartupWMClass", | 738 g_key_file_set_string(key_file, kDesktopEntry, "StartupWMClass", |
| 656 wmclass.c_str()); | 739 wmclass.c_str()); |
| 657 #endif | 740 #endif |
| 658 | 741 |
| 659 length = 0; | 742 length = 0; |
| 660 gchar* data_dump = g_key_file_to_data(key_file, &length, NULL); | 743 gchar* data_dump = g_key_file_to_data(key_file, &length, NULL); |
| 661 if (data_dump) { | 744 if (data_dump) { |
| 662 // If strlen(data_dump[0]) == 0, this check will fail. | 745 // If strlen(data_dump[0]) == 0, this check will fail. |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 681 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 764 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 682 | 765 |
| 683 base::FilePath shortcut_filename; | 766 base::FilePath shortcut_filename; |
| 684 if (!shortcut_info.extension_id.empty()) { | 767 if (!shortcut_info.extension_id.empty()) { |
| 685 shortcut_filename = GetExtensionShortcutFilename( | 768 shortcut_filename = GetExtensionShortcutFilename( |
| 686 shortcut_info.profile_path, shortcut_info.extension_id); | 769 shortcut_info.profile_path, shortcut_info.extension_id); |
| 687 // For extensions we do not want duplicate shortcuts. So, delete any that | 770 // For extensions we do not want duplicate shortcuts. So, delete any that |
| 688 // already exist and replace them. | 771 // already exist and replace them. |
| 689 if (creation_locations.on_desktop) | 772 if (creation_locations.on_desktop) |
| 690 DeleteShortcutOnDesktop(shortcut_filename); | 773 DeleteShortcutOnDesktop(shortcut_filename); |
| 691 if (creation_locations.in_applications_menu) | 774 if (creation_locations.in_applications_menu || creation_locations.hidden) |
| 692 DeleteShortcutInApplicationsMenu(shortcut_filename); | 775 DeleteShortcutInApplicationsMenu(shortcut_filename); |
| 693 } else { | 776 } else { |
| 694 shortcut_filename = GetWebShortcutFilename(shortcut_info.url); | 777 shortcut_filename = GetWebShortcutFilename(shortcut_info.url); |
| 695 } | 778 } |
| 696 if (shortcut_filename.empty()) | 779 if (shortcut_filename.empty()) |
| 697 return false; | 780 return false; |
| 698 | 781 |
| 699 std::string icon_name = CreateShortcutIcon(shortcut_info, shortcut_filename); | 782 std::string icon_name = CreateShortcutIcon(shortcut_info, shortcut_filename); |
| 700 | 783 |
| 701 std::string app_name = | 784 std::string app_name = |
| 702 web_app::GenerateApplicationNameFromInfo(shortcut_info); | 785 web_app::GenerateApplicationNameFromInfo(shortcut_info); |
| 703 std::string contents = ShellIntegrationLinux::GetDesktopFileContents( | |
| 704 shortcut_template, | |
| 705 app_name, | |
| 706 shortcut_info.url, | |
| 707 shortcut_info.extension_id, | |
| 708 shortcut_info.extension_path, | |
| 709 shortcut_info.title, | |
| 710 icon_name, | |
| 711 shortcut_info.profile_path); | |
| 712 | 786 |
| 713 bool success = true; | 787 bool success = true; |
| 714 | 788 |
| 715 if (creation_locations.on_desktop) | 789 if (creation_locations.on_desktop) { |
| 790 std::string contents = ShellIntegrationLinux::GetDesktopFileContents( | |
| 791 shortcut_template, | |
| 792 app_name, | |
| 793 shortcut_info.url, | |
| 794 shortcut_info.extension_id, | |
| 795 shortcut_info.extension_path, | |
| 796 shortcut_info.title, | |
| 797 icon_name, | |
| 798 shortcut_info.profile_path, | |
| 799 false); | |
| 716 success = CreateShortcutOnDesktop(shortcut_filename, contents); | 800 success = CreateShortcutOnDesktop(shortcut_filename, contents); |
| 801 } | |
| 717 | 802 |
| 718 if (creation_locations.in_applications_menu) | 803 // The 'in_applications_menu' and 'hidden' locations are actually the same |
| 804 // place ('applications'). | |
| 805 if (creation_locations.in_applications_menu || creation_locations.hidden) { | |
| 806 // Set NoDisplay=true if hidden but not in_applications_menu. This will hide | |
| 807 // the application from user-facing menus. | |
| 808 std::string contents = ShellIntegrationLinux::GetDesktopFileContents( | |
| 809 shortcut_template, | |
| 810 app_name, | |
| 811 shortcut_info.url, | |
| 812 shortcut_info.extension_id, | |
| 813 shortcut_info.extension_path, | |
| 814 shortcut_info.title, | |
| 815 icon_name, | |
| 816 shortcut_info.profile_path, | |
| 817 !creation_locations.in_applications_menu); | |
| 719 success = CreateShortcutInApplicationsMenu(shortcut_filename, contents) && | 818 success = CreateShortcutInApplicationsMenu(shortcut_filename, contents) && |
| 720 success; | 819 success; |
| 820 } | |
| 721 | 821 |
| 722 return success; | 822 return success; |
| 723 } | 823 } |
| 724 | 824 |
| 725 void DeleteDesktopShortcuts(const base::FilePath& profile_path, | 825 void DeleteDesktopShortcuts(const base::FilePath& profile_path, |
| 726 const std::string& extension_id) { | 826 const std::string& extension_id) { |
| 727 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 827 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 728 | 828 |
| 729 base::FilePath shortcut_filename = GetExtensionShortcutFilename( | 829 base::FilePath shortcut_filename = GetExtensionShortcutFilename( |
| 730 profile_path, extension_id); | 830 profile_path, extension_id); |
| 731 DCHECK(!shortcut_filename.empty()); | 831 DCHECK(!shortcut_filename.empty()); |
| 732 | 832 |
| 733 DeleteShortcutOnDesktop(shortcut_filename); | 833 DeleteShortcutOnDesktop(shortcut_filename); |
| 734 DeleteShortcutInApplicationsMenu(shortcut_filename); | 834 DeleteShortcutInApplicationsMenu(shortcut_filename); |
| 735 } | 835 } |
| 736 | 836 |
| 737 } // namespace ShellIntegrationLinux | 837 } // namespace ShellIntegrationLinux |
| OLD | NEW |