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

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: Diff against Issue 12386077. 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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698