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 |
| 466 // Search paths as specified in the XDG Base Directory Specification. | 493 // Search paths as specified in the XDG Base Directory Specification. |
| 467 // http://standards.freedesktop.org/basedir-spec/latest/ | 494 // http://standards.freedesktop.org/basedir-spec/latest/ |
| 468 std::string xdg_data_home; | 495 std::string xdg_data_home; |
| 469 std::string home; | 496 std::string home; |
| 470 if (env->GetVar("XDG_DATA_HOME", &xdg_data_home) && | 497 if (env->GetVar("XDG_DATA_HOME", &xdg_data_home) && |
| 471 !xdg_data_home.empty()) { | 498 !xdg_data_home.empty()) { |
| 472 search_paths.push_back(base::FilePath(xdg_data_home)); | 499 search_paths.push_back(base::FilePath(xdg_data_home)); |
| 473 } else if (env->GetVar("HOME", &home) && !home.empty()) { | 500 } else if (env->GetVar("HOME", &home) && !home.empty()) { |
| 474 search_paths.push_back(base::FilePath(home).Append(".local").Append( | 501 search_paths.push_back(base::FilePath(home).Append(".local").Append( |
| 475 "share")); | 502 "share")); |
| 476 } | 503 } |
| 477 | 504 |
| 478 std::string xdg_data_dirs; | 505 std::string xdg_data_dirs; |
| 479 if (env->GetVar("XDG_DATA_DIRS", &xdg_data_dirs) && | 506 if (env->GetVar("XDG_DATA_DIRS", &xdg_data_dirs) && |
| 480 !xdg_data_dirs.empty()) { | 507 !xdg_data_dirs.empty()) { |
| 481 base::StringTokenizer tokenizer(xdg_data_dirs, ":"); | 508 base::StringTokenizer tokenizer(xdg_data_dirs, ":"); |
| 482 while (tokenizer.GetNext()) { | 509 while (tokenizer.GetNext()) { |
| 483 base::FilePath data_dir(tokenizer.token()); | 510 base::FilePath data_dir(tokenizer.token()); |
| 484 search_paths.push_back(data_dir); | 511 search_paths.push_back(data_dir); |
| 485 } | 512 } |
| 486 } else { | 513 } else { |
| 487 search_paths.push_back(base::FilePath("/usr/share")); | 514 search_paths.push_back(base::FilePath("/usr/share")); |
| 488 search_paths.push_back(base::FilePath("/usr/local/share")); | 515 search_paths.push_back(base::FilePath("/usr/local/share")); |
| 489 } | 516 } |
| 490 | 517 |
| 491 std::string template_filename(GetDesktopName(env)); | |
| 492 for (std::vector<base::FilePath>::const_iterator i = search_paths.begin(); | 518 for (std::vector<base::FilePath>::const_iterator i = search_paths.begin(); |
| 493 i != search_paths.end(); ++i) { | 519 i != search_paths.end(); ++i) { |
| 494 base::FilePath path = i->Append("applications").Append(template_filename); | 520 base::FilePath path = i->Append("applications").Append(desktop_filename); |
| 495 VLOG(1) << "Looking for desktop file template in " << path.value(); | 521 VLOG(1) << "Looking for desktop file in " << path.value(); |
| 496 if (file_util::PathExists(path)) { | 522 if (file_util::PathExists(path)) { |
| 497 VLOG(1) << "Found desktop file template at " << path.value(); | 523 VLOG(1) << "Found desktop file at " << path.value(); |
| 498 return file_util::ReadFileToString(path, output); | 524 return file_util::ReadFileToString(path, output); |
| 499 } | 525 } |
| 500 } | 526 } |
| 501 | 527 |
| 502 LOG(ERROR) << "Could not find desktop file template."; | |
| 503 return false; | 528 return false; |
| 504 } | 529 } |
| 505 | 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 | |
| 506 base::FilePath GetWebShortcutFilename(const GURL& url) { | 543 base::FilePath GetWebShortcutFilename(const GURL& url) { |
| 507 // Use a prefix, because xdg-desktop-menu requires it. | 544 // Use a prefix, because xdg-desktop-menu requires it. |
| 508 std::string filename = | 545 std::string filename = |
| 509 std::string(chrome::kBrowserProcessExecutableName) + "-" + url.spec(); | 546 std::string(chrome::kBrowserProcessExecutableName) + "-" + url.spec(); |
| 510 file_util::ReplaceIllegalCharactersInPath(&filename, '_'); | 547 file_util::ReplaceIllegalCharactersInPath(&filename, '_'); |
| 511 | 548 |
| 512 base::FilePath desktop_path; | 549 base::FilePath desktop_path; |
| 513 if (!PathService::Get(base::DIR_USER_DESKTOP, &desktop_path)) | 550 if (!PathService::Get(base::DIR_USER_DESKTOP, &desktop_path)) |
| 514 return base::FilePath(); | 551 return base::FilePath(); |
| 515 | 552 |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 542 } | 579 } |
| 543 | 580 |
| 544 std::string GetDesktopFileContents( | 581 std::string GetDesktopFileContents( |
| 545 const std::string& template_contents, | 582 const std::string& template_contents, |
| 546 const std::string& app_name, | 583 const std::string& app_name, |
| 547 const GURL& url, | 584 const GURL& url, |
| 548 const std::string& extension_id, | 585 const std::string& extension_id, |
| 549 const base::FilePath& extension_path, | 586 const base::FilePath& extension_path, |
| 550 const string16& title, | 587 const string16& title, |
| 551 const std::string& icon_name, | 588 const std::string& icon_name, |
| 552 const base::FilePath& profile_path) { | 589 const base::FilePath& profile_path, |
| 590 bool no_display) { | |
| 553 // 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 |
| 554 // launchers with an xdg-open shebang. Follow that convention. | 592 // launchers with an xdg-open shebang. Follow that convention. |
| 555 std::string output_buffer = std::string(kXdgOpenShebang) + "\n"; | 593 std::string output_buffer = std::string(kXdgOpenShebang) + "\n"; |
| 556 if (template_contents.empty()) | 594 if (template_contents.empty()) |
| 557 return output_buffer; | 595 return output_buffer; |
| 558 | 596 |
| 559 // See http://standards.freedesktop.org/desktop-entry-spec/latest/ | 597 // See http://standards.freedesktop.org/desktop-entry-spec/latest/ |
| 560 // 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 |
| 561 GKeyFile* key_file = g_key_file_new(); | 599 GKeyFile* key_file = g_key_file_new(); |
| 562 GError* err = NULL; | 600 GError* err = NULL; |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 643 } | 681 } |
| 644 } | 682 } |
| 645 | 683 |
| 646 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()); |
| 647 } | 685 } |
| 648 | 686 |
| 649 // Set the "Icon" key. | 687 // Set the "Icon" key. |
| 650 if (!icon_name.empty()) | 688 if (!icon_name.empty()) |
| 651 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()); |
| 652 | 690 |
| 691 // Set the "NoDisplay" key. | |
| 692 if (no_display) | |
| 693 g_key_file_set_string(key_file, kDesktopEntry, "NoDisplay", "true"); | |
| 694 | |
| 653 #if defined(TOOLKIT_GTK) | 695 #if defined(TOOLKIT_GTK) |
| 654 std::string wmclass = web_app::GetWMClassFromAppName(app_name); | 696 std::string wmclass = web_app::GetWMClassFromAppName(app_name); |
| 655 g_key_file_set_string(key_file, kDesktopEntry, "StartupWMClass", | 697 g_key_file_set_string(key_file, kDesktopEntry, "StartupWMClass", |
| 656 wmclass.c_str()); | 698 wmclass.c_str()); |
| 657 #endif | 699 #endif |
| 658 | 700 |
| 659 length = 0; | 701 length = 0; |
| 660 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); |
| 661 if (data_dump) { | 703 if (data_dump) { |
| 662 // If strlen(data_dump[0]) == 0, this check will fail. | 704 // If strlen(data_dump[0]) == 0, this check will fail. |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 691 if (creation_locations.in_applications_menu) | 733 if (creation_locations.in_applications_menu) |
| 692 DeleteShortcutInApplicationsMenu(shortcut_filename); | 734 DeleteShortcutInApplicationsMenu(shortcut_filename); |
| 693 } else { | 735 } else { |
| 694 shortcut_filename = GetWebShortcutFilename(shortcut_info.url); | 736 shortcut_filename = GetWebShortcutFilename(shortcut_info.url); |
| 695 } | 737 } |
| 696 if (shortcut_filename.empty()) | 738 if (shortcut_filename.empty()) |
| 697 return false; | 739 return false; |
| 698 | 740 |
| 699 std::string icon_name = CreateShortcutIcon(shortcut_info, shortcut_filename); | 741 std::string icon_name = CreateShortcutIcon(shortcut_info, shortcut_filename); |
| 700 | 742 |
| 743 // If we are to create the shortcut in neither the desktop nor applications | |
| 744 // menu, we still want to register the app, so we create a shortcut with | |
| 745 // NoDisplay=true. | |
| 746 bool no_display = (!creation_locations.on_desktop && | |
|
benwells
2013/03/05 04:32:38
Note for later: this feels a bit strange. It would
Matt Giuca
2013/03/05 05:25:44
Well it wouldn't necessarily need to be platform-s
| |
| 747 !creation_locations.in_applications_menu); | |
| 748 | |
| 701 std::string app_name = | 749 std::string app_name = |
| 702 web_app::GenerateApplicationNameFromInfo(shortcut_info); | 750 web_app::GenerateApplicationNameFromInfo(shortcut_info); |
| 703 std::string contents = ShellIntegrationLinux::GetDesktopFileContents( | 751 std::string contents = ShellIntegrationLinux::GetDesktopFileContents( |
| 704 shortcut_template, | 752 shortcut_template, |
| 705 app_name, | 753 app_name, |
| 706 shortcut_info.url, | 754 shortcut_info.url, |
| 707 shortcut_info.extension_id, | 755 shortcut_info.extension_id, |
| 708 shortcut_info.extension_path, | 756 shortcut_info.extension_path, |
| 709 shortcut_info.title, | 757 shortcut_info.title, |
| 710 icon_name, | 758 icon_name, |
| 711 shortcut_info.profile_path); | 759 shortcut_info.profile_path, |
| 760 no_display); | |
| 712 | 761 |
| 713 bool success = true; | 762 bool success = true; |
| 714 | 763 |
| 715 if (creation_locations.on_desktop) | 764 if (creation_locations.on_desktop) |
| 716 success = CreateShortcutOnDesktop(shortcut_filename, contents); | 765 success = CreateShortcutOnDesktop(shortcut_filename, contents); |
| 717 | 766 |
| 718 if (creation_locations.in_applications_menu) | 767 // If no_display is true, CreateShortcutInApplicationsMenu will install the |
| 768 // shortcut, but it will not appear in menus or be searchable by users. | |
| 769 if (creation_locations.in_applications_menu || no_display) | |
| 719 success = CreateShortcutInApplicationsMenu(shortcut_filename, contents) && | 770 success = CreateShortcutInApplicationsMenu(shortcut_filename, contents) && |
| 720 success; | 771 success; |
| 721 | 772 |
| 722 return success; | 773 return success; |
| 723 } | 774 } |
| 724 | 775 |
| 725 void DeleteDesktopShortcuts(const base::FilePath& profile_path, | 776 void DeleteDesktopShortcuts(const base::FilePath& profile_path, |
| 726 const std::string& extension_id) { | 777 const std::string& extension_id) { |
| 727 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 778 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 728 | 779 |
| 729 base::FilePath shortcut_filename = GetExtensionShortcutFilename( | 780 base::FilePath shortcut_filename = GetExtensionShortcutFilename( |
| 730 profile_path, extension_id); | 781 profile_path, extension_id); |
| 731 DCHECK(!shortcut_filename.empty()); | 782 DCHECK(!shortcut_filename.empty()); |
| 732 | 783 |
| 733 DeleteShortcutOnDesktop(shortcut_filename); | 784 DeleteShortcutOnDesktop(shortcut_filename); |
| 734 DeleteShortcutInApplicationsMenu(shortcut_filename); | 785 DeleteShortcutInApplicationsMenu(shortcut_filename); |
| 735 } | 786 } |
| 736 | 787 |
| 788 ShellIntegration::ShortcutLocations DesktopShortcutLocations( | |
| 789 base::Environment* env, const base::FilePath& profile_path, | |
| 790 const std::string& extension_id) { | |
|
benwells
2013/03/05 04:32:38
Nit: one parameter per line.
Matt Giuca
2013/03/06 06:29:51
Done.
| |
| 791 base::FilePath desktop_path; | |
| 792 // If Get returns false, just leave desktop_path empty. | |
| 793 PathService::Get(base::DIR_USER_DESKTOP, &desktop_path); | |
| 794 return DesktopShortcutLocations(env, profile_path, extension_id, | |
| 795 desktop_path); | |
| 796 } | |
| 797 | |
| 798 ShellIntegration::ShortcutLocations DesktopShortcutLocations( | |
| 799 base::Environment* env, const base::FilePath& profile_path, | |
| 800 const std::string& extension_id, const base::FilePath& desktop_path) { | |
|
benwells
2013/03/05 04:32:38
Nit: one parameter per line.
Matt Giuca
2013/03/06 06:29:51
Done.
| |
| 801 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 802 | |
| 803 base::FilePath shortcut_filename = GetExtensionShortcutFilename( | |
| 804 profile_path, extension_id); | |
| 805 DCHECK(!shortcut_filename.empty()); | |
| 806 ShellIntegration::ShortcutLocations locations; | |
| 807 | |
| 808 // Determine whether there is a shortcut on desktop. | |
| 809 if (!desktop_path.empty()) { | |
| 810 locations.on_desktop = | |
| 811 file_util::PathExists(desktop_path.Append(shortcut_filename)); | |
| 812 } | |
| 813 | |
| 814 // Determine whether there is a shortcut in applications menu that *does not* | |
| 815 // have NoDisplay=true. | |
| 816 std::string shortcut_contents; | |
| 817 if (GetExistingDesktopShortcutContents(env, shortcut_filename, | |
| 818 &shortcut_contents)) { | |
| 819 locations.in_applications_menu = | |
| 820 !GetNoDisplayFromDesktopFile(shortcut_contents); | |
| 821 } | |
| 822 | |
| 823 return locations; | |
| 824 } | |
| 825 | |
| 737 } // namespace ShellIntegrationLinux | 826 } // namespace ShellIntegrationLinux |
| OLD | NEW |