Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(318)

Side by Side Diff: chrome/browser/shell_integration_linux.cc

Issue 12208085: On Linux, automatically create app shortcuts on install or update. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Added a new ShortcutLocations boolean, hidden, and used that when updating shortcuts. Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698