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

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: Work around obscure crash due to bug in glib <= 2.32. 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 // 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
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
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
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
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
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