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 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
147 // |shortcut_filename| we'll just undo his action. | 147 // |shortcut_filename| we'll just undo his action. |
148 unlinkat(desktop_fd, shortcut_filename.value().c_str(), 0); | 148 unlinkat(desktop_fd, shortcut_filename.value().c_str(), 0); |
149 } | 149 } |
150 | 150 |
151 if (HANDLE_EINTR(close(desktop_fd)) < 0) | 151 if (HANDLE_EINTR(close(desktop_fd)) < 0) |
152 PLOG(ERROR) << "close"; | 152 PLOG(ERROR) << "close"; |
153 | 153 |
154 return true; | 154 return true; |
155 } | 155 } |
156 | 156 |
157 void DeleteShortcutOnDesktop(const FilePath& shortcut_filename) { | |
158 FilePath desktop_path; | |
159 if (PathService::Get(chrome::DIR_USER_DESKTOP, &desktop_path)) | |
160 file_util::Delete(desktop_path.Append(shortcut_filename), false); | |
161 } | |
162 | |
157 bool CreateShortcutInApplicationsMenu(const FilePath& shortcut_filename, | 163 bool CreateShortcutInApplicationsMenu(const FilePath& shortcut_filename, |
158 const std::string& contents) { | 164 const std::string& contents) { |
159 ScopedTempDir temp_dir; | 165 ScopedTempDir temp_dir; |
160 if (!temp_dir.CreateUniqueTempDir()) | 166 if (!temp_dir.CreateUniqueTempDir()) |
161 return false; | 167 return false; |
162 | 168 |
163 FilePath temp_file_path = temp_dir.path().Append(shortcut_filename); | 169 FilePath temp_file_path = temp_dir.path().Append(shortcut_filename); |
164 | 170 |
165 int bytes_written = file_util::WriteFile(temp_file_path, contents.data(), | 171 int bytes_written = file_util::WriteFile(temp_file_path, contents.data(), |
166 contents.length()); | 172 contents.length()); |
167 | 173 |
168 if (bytes_written != static_cast<int>(contents.length())) | 174 if (bytes_written != static_cast<int>(contents.length())) |
169 return false; | 175 return false; |
170 | 176 |
171 std::vector<std::string> argv; | 177 std::vector<std::string> argv; |
172 argv.push_back("xdg-desktop-menu"); | 178 argv.push_back("xdg-desktop-menu"); |
173 argv.push_back("install"); | 179 argv.push_back("install"); |
174 | 180 |
175 // Always install in user mode, even if someone runs the browser as root | 181 // Always install in user mode, even if someone runs the browser as root |
176 // (people do that). | 182 // (people do that). |
177 argv.push_back("--mode"); | 183 argv.push_back("--mode"); |
178 argv.push_back("user"); | 184 argv.push_back("user"); |
179 | 185 |
180 argv.push_back(temp_file_path.value()); | 186 argv.push_back(temp_file_path.value()); |
181 int exit_code; | 187 int exit_code; |
182 LaunchXdgUtility(argv, &exit_code); | 188 LaunchXdgUtility(argv, &exit_code); |
183 return exit_code == 0; | 189 return exit_code == 0; |
184 } | 190 } |
185 | 191 |
192 void DeleteShortcutInApplicationsMenu(const FilePath& shortcut_filename) { | |
193 std::vector<std::string> argv; | |
194 argv.push_back("xdg-desktop-menu"); | |
195 argv.push_back("uninstall"); | |
196 | |
197 // Uninstall in user mode, to match the install. | |
198 argv.push_back("--mode"); | |
199 argv.push_back("user"); | |
200 | |
201 // The file does not need to exist anywhere - xdg-desktop-menu will uninstall | |
202 // items from the menu with a matching name. | |
203 argv.push_back(shortcut_filename.value()); | |
204 int exit_code; | |
205 LaunchXdgUtility(argv, &exit_code); | |
206 } | |
207 | |
186 // Quote a string such that it appears as one verbatim argument for the Exec | 208 // Quote a string such that it appears as one verbatim argument for the Exec |
187 // key in a desktop file. | 209 // key in a desktop file. |
188 std::string QuoteArgForDesktopFileExec(const std::string& arg) { | 210 std::string QuoteArgForDesktopFileExec(const std::string& arg) { |
189 // http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html | 211 // http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html |
190 | 212 |
191 // Quoting is only necessary if the argument has a reserved character. | 213 // Quoting is only necessary if the argument has a reserved character. |
192 if (arg.find_first_of(" \t\n\"'\\><~|&;$*?#()`") == std::string::npos) | 214 if (arg.find_first_of(" \t\n\"'\\><~|&;$*?#()`") == std::string::npos) |
193 return arg; // No quoting necessary. | 215 return arg; // No quoting necessary. |
194 | 216 |
195 std::string quoted = "\""; | 217 std::string quoted = "\""; |
(...skipping 252 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
448 if (file_util::PathExists(path)) { | 470 if (file_util::PathExists(path)) { |
449 VLOG(1) << "Found desktop file template at " << path.value(); | 471 VLOG(1) << "Found desktop file template at " << path.value(); |
450 return file_util::ReadFileToString(path, output); | 472 return file_util::ReadFileToString(path, output); |
451 } | 473 } |
452 } | 474 } |
453 | 475 |
454 LOG(ERROR) << "Could not find desktop file template."; | 476 LOG(ERROR) << "Could not find desktop file template."; |
455 return false; | 477 return false; |
456 } | 478 } |
457 | 479 |
458 FilePath GetDesktopShortcutFilename(const GURL& url) { | 480 FilePath GetWebShortcutFilename(const GURL& url) { |
459 // Use a prefix, because xdg-desktop-menu requires it. | 481 // Use a prefix, because xdg-desktop-menu requires it. |
460 std::string filename = | 482 std::string filename = |
461 std::string(chrome::kBrowserProcessExecutableName) + "-" + url.spec(); | 483 std::string(chrome::kBrowserProcessExecutableName) + "-" + url.spec(); |
462 file_util::ReplaceIllegalCharactersInPath(&filename, '_'); | 484 file_util::ReplaceIllegalCharactersInPath(&filename, '_'); |
463 | 485 |
464 FilePath desktop_path; | 486 FilePath desktop_path; |
465 if (!PathService::Get(chrome::DIR_USER_DESKTOP, &desktop_path)) | 487 if (!PathService::Get(chrome::DIR_USER_DESKTOP, &desktop_path)) |
466 return FilePath(); | 488 return FilePath(); |
467 | 489 |
468 FilePath filepath = desktop_path.Append(filename); | 490 FilePath filepath = desktop_path.Append(filename); |
469 FilePath alternative_filepath(filepath.value() + ".desktop"); | 491 FilePath alternative_filepath(filepath.value() + ".desktop"); |
470 for (size_t i = 1; i < 100; ++i) { | 492 for (size_t i = 1; i < 100; ++i) { |
471 if (file_util::PathExists(FilePath(alternative_filepath))) { | 493 if (file_util::PathExists(FilePath(alternative_filepath))) { |
472 alternative_filepath = FilePath( | 494 alternative_filepath = FilePath( |
473 filepath.value() + "_" + base::IntToString(i) + ".desktop"); | 495 filepath.value() + "_" + base::IntToString(i) + ".desktop"); |
474 } else { | 496 } else { |
475 return FilePath(alternative_filepath).BaseName(); | 497 return FilePath(alternative_filepath).BaseName(); |
476 } | 498 } |
477 } | 499 } |
478 | 500 |
479 return FilePath(); | 501 return FilePath(); |
480 } | 502 } |
481 | 503 |
504 FilePath GetExtensionShortcutFilename(const FilePath& profile_path, | |
505 const std::string& extension_id) { | |
506 DCHECK(!extension_id.empty()); | |
507 | |
508 // Use a prefix, because xdg-desktop-menu requires it. | |
509 std::string filename(chrome::kBrowserProcessExecutableName); | |
510 filename.append("-") | |
511 .append(extension_id) | |
512 .append("-") | |
513 .append(profile_path.BaseName().value()); | |
514 file_util::ReplaceIllegalCharactersInPath(&filename, '_'); | |
515 return FilePath(filename.append(".desktop")); | |
516 } | |
517 | |
482 std::string GetDesktopFileContents( | 518 std::string GetDesktopFileContents( |
483 const std::string& template_contents, | 519 const std::string& template_contents, |
484 const std::string& app_name, | 520 const std::string& app_name, |
485 const GURL& url, | 521 const GURL& url, |
486 const std::string& extension_id, | 522 const std::string& extension_id, |
487 const bool is_platform_app, | 523 const bool is_platform_app, |
488 const FilePath& extension_path, | 524 const FilePath& extension_path, |
489 const string16& title, | 525 const string16& title, |
490 const std::string& icon_name, | 526 const std::string& icon_name, |
491 const FilePath& profile_path) { | 527 const FilePath& profile_path) { |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
545 g_free(exec_c_string); | 581 g_free(exec_c_string); |
546 StringTokenizer exec_tokenizer(exec_string, " "); | 582 StringTokenizer exec_tokenizer(exec_string, " "); |
547 | 583 |
548 std::string final_path; | 584 std::string final_path; |
549 while (exec_tokenizer.GetNext() && exec_tokenizer.token() != "%U") { | 585 while (exec_tokenizer.GetNext() && exec_tokenizer.token() != "%U") { |
550 if (!final_path.empty()) | 586 if (!final_path.empty()) |
551 final_path += " "; | 587 final_path += " "; |
552 final_path += exec_tokenizer.token(); | 588 final_path += exec_tokenizer.token(); |
553 } | 589 } |
554 CommandLine cmd_line(CommandLine::NO_PROGRAM); | 590 CommandLine cmd_line(CommandLine::NO_PROGRAM); |
555 cmd_line = ShellIntegration::CommandLineArgsForLauncher( | 591 cmd_line = ShellIntegration::CommandLineArgsForLauncher( |
Mihai Parparita -not on Chrome
2012/07/10 22:51:27
Encoding this data in the filename seems ugly (use
benwells
2012/07/10 22:55:51
It's not visible to users. Currently the filename
| |
556 url, extension_id, is_platform_app, profile_path); | 592 url, extension_id, is_platform_app, profile_path); |
557 const CommandLine::SwitchMap& switch_map = cmd_line.GetSwitches(); | 593 const CommandLine::SwitchMap& switch_map = cmd_line.GetSwitches(); |
558 for (CommandLine::SwitchMap::const_iterator i = switch_map.begin(); | 594 for (CommandLine::SwitchMap::const_iterator i = switch_map.begin(); |
559 i != switch_map.end(); ++i) { | 595 i != switch_map.end(); ++i) { |
560 if (i->second.empty()) { | 596 if (i->second.empty()) { |
561 final_path += " --" + i->first; | 597 final_path += " --" + i->first; |
562 } else { | 598 } else { |
563 final_path += " " + QuoteArgForDesktopFileExec("--" + i->first + | 599 final_path += " " + QuoteArgForDesktopFileExec("--" + i->first + |
564 "=" + i->second); | 600 "=" + i->second); |
565 } | 601 } |
(...skipping 24 matching lines...) Expand all Loading... | |
590 | 626 |
591 g_key_file_free(key_file); | 627 g_key_file_free(key_file); |
592 return output_buffer; | 628 return output_buffer; |
593 } | 629 } |
594 | 630 |
595 bool CreateDesktopShortcut( | 631 bool CreateDesktopShortcut( |
596 const ShellIntegration::ShortcutInfo& shortcut_info, | 632 const ShellIntegration::ShortcutInfo& shortcut_info, |
597 const std::string& shortcut_template) { | 633 const std::string& shortcut_template) { |
598 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 634 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
599 | 635 |
600 FilePath shortcut_filename = | 636 FilePath shortcut_filename; |
601 ShellIntegrationLinux::GetDesktopShortcutFilename(shortcut_info.url); | 637 if (!shortcut_info.extension_id.empty()) { |
638 shortcut_filename = GetExtensionShortcutFilename( | |
639 shortcut_info.profile_path, shortcut_info.extension_id); | |
640 // For extensions we do not want duplicate shortcuts. So, delete any that | |
641 // already exist and replace them. | |
642 if (shortcut_info.create_on_desktop) | |
643 DeleteShortcutOnDesktop(shortcut_filename); | |
644 if (shortcut_info.create_in_applications_menu) | |
645 DeleteShortcutInApplicationsMenu(shortcut_filename); | |
646 } else { | |
647 shortcut_filename = GetWebShortcutFilename(shortcut_info.url); | |
648 } | |
602 if (shortcut_filename.empty()) | 649 if (shortcut_filename.empty()) |
603 return false; | 650 return false; |
604 | 651 |
605 std::string icon_name = CreateShortcutIcon(shortcut_info, shortcut_filename); | 652 std::string icon_name = CreateShortcutIcon(shortcut_info, shortcut_filename); |
606 | 653 |
607 std::string app_name = | 654 std::string app_name = |
608 web_app::GenerateApplicationNameFromInfo(shortcut_info); | 655 web_app::GenerateApplicationNameFromInfo(shortcut_info); |
609 std::string contents = ShellIntegrationLinux::GetDesktopFileContents( | 656 std::string contents = ShellIntegrationLinux::GetDesktopFileContents( |
610 shortcut_template, | 657 shortcut_template, |
611 app_name, | 658 app_name, |
(...skipping 10 matching lines...) Expand all Loading... | |
622 if (shortcut_info.create_on_desktop) | 669 if (shortcut_info.create_on_desktop) |
623 success = CreateShortcutOnDesktop(shortcut_filename, contents); | 670 success = CreateShortcutOnDesktop(shortcut_filename, contents); |
624 | 671 |
625 if (shortcut_info.create_in_applications_menu) | 672 if (shortcut_info.create_in_applications_menu) |
626 success = CreateShortcutInApplicationsMenu(shortcut_filename, contents) && | 673 success = CreateShortcutInApplicationsMenu(shortcut_filename, contents) && |
627 success; | 674 success; |
628 | 675 |
629 return success; | 676 return success; |
630 } | 677 } |
631 | 678 |
679 void DeleteDesktopShortcuts(const FilePath& profile_path, | |
680 const std::string& extension_id) { | |
681 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
682 | |
683 FilePath shortcut_filename = GetExtensionShortcutFilename( | |
684 profile_path, extension_id); | |
685 DCHECK(!shortcut_filename.empty()); | |
686 | |
687 DeleteShortcutOnDesktop(shortcut_filename); | |
688 DeleteShortcutInApplicationsMenu(shortcut_filename); | |
689 } | |
690 | |
632 } // namespace ShellIntegrationLinux | 691 } // namespace ShellIntegrationLinux |
OLD | NEW |