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