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

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: Rename and move some functions to more appropriate names/places. 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
« no previous file with comments | « chrome/browser/shell_integration_linux.h ('k') | chrome/browser/shell_integration_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 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
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
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
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
OLDNEW
« no previous file with comments | « chrome/browser/shell_integration_linux.h ('k') | chrome/browser/shell_integration_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698