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 bool GetExistingDesktopShortcutContents(base::Environment* env, |
| 461 std::string* output) { | 487 const base::FilePath& desktop_filename, |
| 488 std::string* output) { | |
| 462 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 489 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 463 | 490 |
| 464 std::vector<base::FilePath> search_paths; | 491 std::vector<base::FilePath> search_paths; |
| 465 | 492 |
| 493 // Search paths as specified in the XDG Base Directory Specification. | |
| 494 // http://standards.freedesktop.org/basedir-spec/latest/ | |
| 466 std::string xdg_data_home; | 495 std::string xdg_data_home; |
| 496 std::string home; | |
| 467 if (env->GetVar("XDG_DATA_HOME", &xdg_data_home) && | 497 if (env->GetVar("XDG_DATA_HOME", &xdg_data_home) && |
| 468 !xdg_data_home.empty()) { | 498 !xdg_data_home.empty()) { |
| 469 search_paths.push_back(base::FilePath(xdg_data_home)); | 499 search_paths.push_back(base::FilePath(xdg_data_home)); |
| 500 } else if (env->GetVar("HOME", &home) && !home.empty()) { | |
| 501 search_paths.push_back(base::FilePath(home).Append(".local").Append( | |
| 502 "share")); | |
| 470 } | 503 } |
| 471 | 504 |
| 472 std::string xdg_data_dirs; | 505 std::string xdg_data_dirs; |
| 473 if (env->GetVar("XDG_DATA_DIRS", &xdg_data_dirs) && | 506 if (env->GetVar("XDG_DATA_DIRS", &xdg_data_dirs) && |
| 474 !xdg_data_dirs.empty()) { | 507 !xdg_data_dirs.empty()) { |
| 475 base::StringTokenizer tokenizer(xdg_data_dirs, ":"); | 508 base::StringTokenizer tokenizer(xdg_data_dirs, ":"); |
| 476 while (tokenizer.GetNext()) { | 509 while (tokenizer.GetNext()) { |
| 477 base::FilePath data_dir(tokenizer.token()); | 510 base::FilePath data_dir(tokenizer.token()); |
| 478 search_paths.push_back(data_dir); | 511 search_paths.push_back(data_dir); |
| 479 search_paths.push_back(data_dir.Append("applications")); | |
| 480 } | 512 } |
| 513 } else { | |
| 514 search_paths.push_back(base::FilePath("/usr/local/share")); | |
| 515 search_paths.push_back(base::FilePath("/usr/share")); | |
| 481 } | 516 } |
| 482 | 517 |
| 483 // Add some fallback paths for systems which don't have XDG_DATA_DIRS or have | |
| 484 // it incomplete. | |
| 485 search_paths.push_back(base::FilePath("/usr/share/applications")); | |
| 486 search_paths.push_back(base::FilePath("/usr/local/share/applications")); | |
| 487 | |
| 488 std::string template_filename(GetDesktopName(env)); | |
| 489 for (std::vector<base::FilePath>::const_iterator i = search_paths.begin(); | 518 for (std::vector<base::FilePath>::const_iterator i = search_paths.begin(); |
| 490 i != search_paths.end(); ++i) { | 519 i != search_paths.end(); ++i) { |
| 491 base::FilePath path = i->Append(template_filename); | 520 base::FilePath path = i->Append("applications").Append(desktop_filename); |
| 492 VLOG(1) << "Looking for desktop file template in " << path.value(); | 521 VLOG(1) << "Looking for desktop file in " << path.value(); |
| 493 if (file_util::PathExists(path)) { | 522 if (file_util::PathExists(path)) { |
| 494 VLOG(1) << "Found desktop file template at " << path.value(); | 523 VLOG(1) << "Found desktop file at " << path.value(); |
| 495 return file_util::ReadFileToString(path, output); | 524 return file_util::ReadFileToString(path, output); |
| 496 } | 525 } |
| 497 } | 526 } |
| 498 | 527 |
| 499 LOG(ERROR) << "Could not find desktop file template."; | |
| 500 return false; | 528 return false; |
| 501 } | 529 } |
| 502 | 530 |
| 531 bool GetDesktopShortcutTemplate(base::Environment* env, | |
| 532 std::string* output) { | |
| 533 base::FilePath template_filename(GetDesktopName(env)); | |
| 534 if (GetExistingDesktopShortcutContents(env, template_filename, output)) { | |
| 535 return true; | |
| 536 } else { | |
| 537 LOG(ERROR) << "Could not find desktop file " << template_filename.value() | |
| 538 << "."; | |
| 539 return false; | |
| 540 } | |
| 541 } | |
| 542 | |
| 503 base::FilePath GetWebShortcutFilename(const GURL& url) { | 543 base::FilePath GetWebShortcutFilename(const GURL& url) { |
| 504 // Use a prefix, because xdg-desktop-menu requires it. | 544 // Use a prefix, because xdg-desktop-menu requires it. |
| 505 std::string filename = | 545 std::string filename = |
| 506 std::string(chrome::kBrowserProcessExecutableName) + "-" + url.spec(); | 546 std::string(chrome::kBrowserProcessExecutableName) + "-" + url.spec(); |
| 507 file_util::ReplaceIllegalCharactersInPath(&filename, '_'); | 547 file_util::ReplaceIllegalCharactersInPath(&filename, '_'); |
| 508 | 548 |
| 509 base::FilePath desktop_path; | 549 base::FilePath desktop_path; |
| 510 if (!PathService::Get(base::DIR_USER_DESKTOP, &desktop_path)) | 550 if (!PathService::Get(base::DIR_USER_DESKTOP, &desktop_path)) |
| 511 return base::FilePath(); | 551 return base::FilePath(); |
| 512 | 552 |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 539 } | 579 } |
| 540 | 580 |
| 541 std::string GetDesktopFileContents( | 581 std::string GetDesktopFileContents( |
| 542 const std::string& template_contents, | 582 const std::string& template_contents, |
| 543 const std::string& app_name, | 583 const std::string& app_name, |
| 544 const GURL& url, | 584 const GURL& url, |
| 545 const std::string& extension_id, | 585 const std::string& extension_id, |
| 546 const base::FilePath& extension_path, | 586 const base::FilePath& extension_path, |
| 547 const string16& title, | 587 const string16& title, |
| 548 const std::string& icon_name, | 588 const std::string& icon_name, |
| 549 const base::FilePath& profile_path) { | 589 const base::FilePath& profile_path, |
| 590 bool no_display) { | |
| 550 // Although not required by the spec, Nautilus on Ubuntu Karmic creates its | 591 // Although not required by the spec, Nautilus on Ubuntu Karmic creates its |
| 551 // launchers with an xdg-open shebang. Follow that convention. | 592 // launchers with an xdg-open shebang. Follow that convention. |
| 552 std::string output_buffer = std::string(kXdgOpenShebang) + "\n"; | 593 std::string output_buffer = std::string(kXdgOpenShebang) + "\n"; |
| 553 if (template_contents.empty()) | 594 if (template_contents.empty()) |
| 554 return output_buffer; | 595 return output_buffer; |
| 555 | 596 |
| 556 // See http://standards.freedesktop.org/desktop-entry-spec/latest/ | 597 // See http://standards.freedesktop.org/desktop-entry-spec/latest/ |
| 557 // http://developer.gnome.org/glib/unstable/glib-Key-value-file-parser.html | 598 // http://developer.gnome.org/glib/unstable/glib-Key-value-file-parser.html |
| 558 GKeyFile* key_file = g_key_file_new(); | 599 GKeyFile* key_file = g_key_file_new(); |
| 559 GError* err = NULL; | 600 GError* err = NULL; |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 640 } | 681 } |
| 641 } | 682 } |
| 642 | 683 |
| 643 g_key_file_set_string(key_file, kDesktopEntry, "Exec", final_path.c_str()); | 684 g_key_file_set_string(key_file, kDesktopEntry, "Exec", final_path.c_str()); |
| 644 } | 685 } |
| 645 | 686 |
| 646 // Set the "Icon" key. | 687 // Set the "Icon" key. |
| 647 if (!icon_name.empty()) | 688 if (!icon_name.empty()) |
| 648 g_key_file_set_string(key_file, kDesktopEntry, "Icon", icon_name.c_str()); | 689 g_key_file_set_string(key_file, kDesktopEntry, "Icon", icon_name.c_str()); |
| 649 | 690 |
| 691 // Set the "NoDisplay" key. | |
| 692 if (no_display) | |
| 693 g_key_file_set_string(key_file, kDesktopEntry, "NoDisplay", "true"); | |
| 694 | |
| 650 #if defined(TOOLKIT_GTK) | 695 #if defined(TOOLKIT_GTK) |
| 651 std::string wmclass = web_app::GetWMClassFromAppName(app_name); | 696 std::string wmclass = web_app::GetWMClassFromAppName(app_name); |
| 652 g_key_file_set_string(key_file, kDesktopEntry, "StartupWMClass", | 697 g_key_file_set_string(key_file, kDesktopEntry, "StartupWMClass", |
| 653 wmclass.c_str()); | 698 wmclass.c_str()); |
| 654 #endif | 699 #endif |
| 655 | 700 |
| 656 length = 0; | 701 length = 0; |
| 657 gchar* data_dump = g_key_file_to_data(key_file, &length, NULL); | 702 gchar* data_dump = g_key_file_to_data(key_file, &length, NULL); |
| 658 if (data_dump) { | 703 if (data_dump) { |
| 659 // If strlen(data_dump[0]) == 0, this check will fail. | 704 // If strlen(data_dump[0]) == 0, this check will fail. |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 678 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 723 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 679 | 724 |
| 680 base::FilePath shortcut_filename; | 725 base::FilePath shortcut_filename; |
| 681 if (!shortcut_info.extension_id.empty()) { | 726 if (!shortcut_info.extension_id.empty()) { |
| 682 shortcut_filename = GetExtensionShortcutFilename( | 727 shortcut_filename = GetExtensionShortcutFilename( |
| 683 shortcut_info.profile_path, shortcut_info.extension_id); | 728 shortcut_info.profile_path, shortcut_info.extension_id); |
| 684 // For extensions we do not want duplicate shortcuts. So, delete any that | 729 // For extensions we do not want duplicate shortcuts. So, delete any that |
| 685 // already exist and replace them. | 730 // already exist and replace them. |
| 686 if (creation_locations.on_desktop) | 731 if (creation_locations.on_desktop) |
| 687 DeleteShortcutOnDesktop(shortcut_filename); | 732 DeleteShortcutOnDesktop(shortcut_filename); |
| 688 if (creation_locations.in_applications_menu) | 733 if (creation_locations.in_applications_menu || creation_locations.hidden) |
| 689 DeleteShortcutInApplicationsMenu(shortcut_filename); | 734 DeleteShortcutInApplicationsMenu(shortcut_filename); |
| 690 } else { | 735 } else { |
| 691 shortcut_filename = GetWebShortcutFilename(shortcut_info.url); | 736 shortcut_filename = GetWebShortcutFilename(shortcut_info.url); |
| 692 } | 737 } |
| 693 if (shortcut_filename.empty()) | 738 if (shortcut_filename.empty()) |
| 694 return false; | 739 return false; |
| 695 | 740 |
| 696 std::string icon_name = CreateShortcutIcon(shortcut_info, shortcut_filename); | 741 std::string icon_name = CreateShortcutIcon(shortcut_info, shortcut_filename); |
| 697 | 742 |
| 698 std::string app_name = | 743 std::string app_name = |
| 699 web_app::GenerateApplicationNameFromInfo(shortcut_info); | 744 web_app::GenerateApplicationNameFromInfo(shortcut_info); |
| 700 std::string contents = ShellIntegrationLinux::GetDesktopFileContents( | |
| 701 shortcut_template, | |
| 702 app_name, | |
| 703 shortcut_info.url, | |
| 704 shortcut_info.extension_id, | |
| 705 shortcut_info.extension_path, | |
| 706 shortcut_info.title, | |
| 707 icon_name, | |
| 708 shortcut_info.profile_path); | |
| 709 | 745 |
| 710 bool success = true; | 746 bool success = true; |
| 711 | 747 |
| 712 if (creation_locations.on_desktop) | 748 if (creation_locations.on_desktop) { |
| 749 std::string contents = ShellIntegrationLinux::GetDesktopFileContents( | |
| 750 shortcut_template, | |
| 751 app_name, | |
| 752 shortcut_info.url, | |
| 753 shortcut_info.extension_id, | |
| 754 shortcut_info.extension_path, | |
| 755 shortcut_info.title, | |
| 756 icon_name, | |
| 757 shortcut_info.profile_path, | |
| 758 false); | |
| 713 success = CreateShortcutOnDesktop(shortcut_filename, contents); | 759 success = CreateShortcutOnDesktop(shortcut_filename, contents); |
| 760 } | |
| 714 | 761 |
| 715 if (creation_locations.in_applications_menu) | 762 // The 'in_applications_menu' and 'hidden' locations are actually the same |
| 763 // place ('applications'). | |
| 764 if (creation_locations.in_applications_menu || creation_locations.hidden) { | |
| 765 // Set NoDisplay=true if hidden but not in_applications_menu. This will hide | |
| 766 // the application from user-facing menus. | |
| 767 std::string contents = ShellIntegrationLinux::GetDesktopFileContents( | |
| 768 shortcut_template, | |
| 769 app_name, | |
| 770 shortcut_info.url, | |
| 771 shortcut_info.extension_id, | |
| 772 shortcut_info.extension_path, | |
| 773 shortcut_info.title, | |
| 774 icon_name, | |
| 775 shortcut_info.profile_path, | |
| 776 !creation_locations.in_applications_menu); | |
| 716 success = CreateShortcutInApplicationsMenu(shortcut_filename, contents) && | 777 success = CreateShortcutInApplicationsMenu(shortcut_filename, contents) && |
| 717 success; | 778 success; |
| 779 } | |
| 718 | 780 |
| 719 return success; | 781 return success; |
| 720 } | 782 } |
| 721 | 783 |
| 722 void DeleteDesktopShortcuts(const base::FilePath& profile_path, | 784 void DeleteDesktopShortcuts(const base::FilePath& profile_path, |
| 723 const std::string& extension_id) { | 785 const std::string& extension_id) { |
| 724 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 786 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 725 | 787 |
| 726 base::FilePath shortcut_filename = GetExtensionShortcutFilename( | 788 base::FilePath shortcut_filename = GetExtensionShortcutFilename( |
| 727 profile_path, extension_id); | 789 profile_path, extension_id); |
| 728 DCHECK(!shortcut_filename.empty()); | 790 DCHECK(!shortcut_filename.empty()); |
| 729 | 791 |
| 730 DeleteShortcutOnDesktop(shortcut_filename); | 792 DeleteShortcutOnDesktop(shortcut_filename); |
| 731 DeleteShortcutInApplicationsMenu(shortcut_filename); | 793 DeleteShortcutInApplicationsMenu(shortcut_filename); |
| 732 } | 794 } |
| 733 | 795 |
| 796 ShellIntegration::ShortcutLocations DesktopShortcutLocations( | |
| 797 base::Environment* env, | |
| 798 const base::FilePath& profile_path, | |
| 799 const std::string& extension_id) { | |
| 800 base::FilePath desktop_path; | |
| 801 // If Get returns false, just leave desktop_path empty. | |
| 802 PathService::Get(base::DIR_USER_DESKTOP, &desktop_path); | |
| 803 return DesktopShortcutLocations(env, profile_path, extension_id, | |
| 804 desktop_path); | |
| 805 } | |
| 806 | |
| 807 ShellIntegration::ShortcutLocations DesktopShortcutLocations( | |
| 808 base::Environment* env, | |
| 809 const base::FilePath& profile_path, | |
| 810 const std::string& extension_id, | |
| 811 const base::FilePath& desktop_path) { | |
| 812 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 813 | |
| 814 base::FilePath shortcut_filename = GetExtensionShortcutFilename( | |
| 815 profile_path, extension_id); | |
| 816 DCHECK(!shortcut_filename.empty()); | |
| 817 ShellIntegration::ShortcutLocations locations; | |
| 818 | |
| 819 // Determine whether there is a shortcut on desktop. | |
| 820 if (!desktop_path.empty()) { | |
| 821 locations.on_desktop = | |
| 822 file_util::PathExists(desktop_path.Append(shortcut_filename)); | |
| 823 } | |
| 824 | |
| 825 // Determine whether there is a shortcut in applications menu that *does not* | |
| 826 // have NoDisplay=true. | |
| 827 std::string shortcut_contents; | |
| 828 if (GetExistingDesktopShortcutContents(env, shortcut_filename, | |
| 829 &shortcut_contents)) { | |
| 830 locations.in_applications_menu = | |
| 831 !GetNoDisplayFromDesktopFile(shortcut_contents); | |
| 832 } | |
| 833 | |
| 834 // Always create a shortcut in applications (set NoDisplay=true if necessary). | |
| 835 locations.hidden = true; | |
|
benwells
2013/03/06 06:36:39
I don't understand this: I thought this function w
Matt Giuca
2013/03/06 07:23:11
Yeah now that you mention it, this does not belong
| |
| 836 | |
| 837 return locations; | |
| 838 } | |
| 839 | |
| 734 } // namespace ShellIntegrationLinux | 840 } // namespace ShellIntegrationLinux |
| OLD | NEW |