diff options
author | phajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-12 21:34:49 +0000 |
---|---|---|
committer | phajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-12 21:34:49 +0000 |
commit | b96aa938b7f86b67f7d4c167e713f5c23fae01d6 (patch) | |
tree | 92c58342b82e416a5ffd29a7c283fdcfe9c0c82b /chrome/browser/shell_integration_linux.cc | |
parent | 4778bf3be094a4faac5e622a5f18be308908e53c (diff) | |
download | chromium_src-b96aa938b7f86b67f7d4c167e713f5c23fae01d6.zip chromium_src-b96aa938b7f86b67f7d4c167e713f5c23fae01d6.tar.gz chromium_src-b96aa938b7f86b67f7d4c167e713f5c23fae01d6.tar.bz2 |
First step to create application shortcuts on Linux.
Create a working desktop shortcut. For now it displays no UI, but the backend works.
TEST=none
http://crbug.com/17251
Review URL: http://codereview.chromium.org/164280
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@23226 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/shell_integration_linux.cc')
-rw-r--r-- | chrome/browser/shell_integration_linux.cc | 136 |
1 files changed, 135 insertions, 1 deletions
diff --git a/chrome/browser/shell_integration_linux.cc b/chrome/browser/shell_integration_linux.cc index fc5e053..48abd49 100644 --- a/chrome/browser/shell_integration_linux.cc +++ b/chrome/browser/shell_integration_linux.cc @@ -12,9 +12,22 @@ #include <vector> +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/message_loop.h" +#include "base/path_service.h" #include "base/process_util.h" +#include "base/string_tokenizer.h" +#include "base/string_util.h" +#include "base/task.h" +#include "base/thread.h" +#include "chrome/browser/browser_process.h" +#include "chrome/common/chrome_paths.h" +#include "googleurl/src/gurl.h" -static const char* GetDesktopName() { +namespace { + +const char* GetDesktopName() { #if defined(GOOGLE_CHROME_BUILD) return "google-chrome.desktop"; #else // CHROMIUM_BUILD @@ -31,6 +44,72 @@ static const char* GetDesktopName() { #endif } +bool GetDesktopShortcutTemplate(std::string* output) { + std::vector<std::string> search_paths; + + const char* xdg_data_home = getenv("XDG_DATA_HOME"); + if (xdg_data_home) + search_paths.push_back(xdg_data_home); + + const char* xdg_data_dirs = getenv("XDG_DATA_DIRS"); + if (xdg_data_dirs) { + StringTokenizer tokenizer(xdg_data_dirs, ":"); + while (tokenizer.GetNext()) { + search_paths.push_back(tokenizer.token()); + } + } + + std::string template_filename(GetDesktopName()); + for (std::vector<std::string>::const_iterator i = search_paths.begin(); + i != search_paths.end(); ++i) { + FilePath path = FilePath(*i).Append(template_filename); + if (file_util::PathExists(path)) + return file_util::ReadFileToString(path, output); + } + + return false; +} + +class CreateDesktopShortcutTask : public Task { + public: + CreateDesktopShortcutTask(const GURL& url, const string16& title) + : url_(url), + title_(title) { + } + + virtual void Run() { + // TODO(phajdan.jr): Report errors from this function, possibly as infobars. + FilePath desktop_path; + if (!PathService::Get(chrome::DIR_USER_DESKTOP, &desktop_path)) + return; + desktop_path = + desktop_path.Append(ShellIntegration::GetDesktopShortcutFilename(url_)); + + if (file_util::PathExists(desktop_path)) + return; + + std::string template_contents; + if (!GetDesktopShortcutTemplate(&template_contents)) + return; + + std::string contents = ShellIntegration::GetDesktopFileContents( + template_contents, url_, title_); + int bytes_written = file_util::WriteFile(desktop_path, contents.data(), + contents.length()); + if (bytes_written != static_cast<int>(contents.length())) { + file_util::Delete(desktop_path, false); + } + } + + private: + const GURL url_; // URL of the web application. + const string16 title_; // Title displayed to the user. + + DISALLOW_COPY_AND_ASSIGN(CreateDesktopShortcutTask); +}; + +} // namespace + // We delegate the difficult of setting the default browser in Linux desktop // environments to a new xdg utility, xdg-settings. We'll have to include a copy // of it for this to work, obviously, but that's actually the suggested approach @@ -98,3 +177,58 @@ bool ShellIntegration::IsFirefoxDefaultBrowser() { base::GetAppOutput(CommandLine(argv), &browser); return browser.find("irefox") != std::string::npos; } + +FilePath ShellIntegration::GetDesktopShortcutFilename(const GURL& url) { + std::wstring filename = UTF8ToWide(url.spec()) + L".desktop"; + file_util::ReplaceIllegalCharacters(&filename, '_'); + + // Return BaseName to be absolutely sure we're not vulnerable to a directory + // traversal attack. + return FilePath::FromWStringHack(filename).BaseName(); +} + +std::string ShellIntegration::GetDesktopFileContents( + const std::string& template_contents, const GURL& url, + const string16& title) { + // See http://standards.freedesktop.org/desktop-entry-spec/latest/ + std::string output_buffer; + StringTokenizer tokenizer(template_contents, "\n"); + while (tokenizer.GetNext()) { + // TODO(phajdan.jr): Add the icon. + + if (tokenizer.token().substr(0, 5) == "Exec=") { + std::string exec_path = tokenizer.token().substr(5); + StringTokenizer exec_tokenizer(exec_path, " "); + std::string final_path; + while (exec_tokenizer.GetNext()) { + if (exec_tokenizer.token() != "%U") + final_path += exec_tokenizer.token() + " "; + } + std::string app_switch(StringPrintf("\"--app=%s\"", + url.spec().c_str())); + ReplaceSubstringsAfterOffset(&app_switch, 0, "%", "%%"); + output_buffer += std::string("Exec=") + final_path + app_switch + "\n"; + } else if (tokenizer.token().substr(0, 5) == "Name=") { + std::string final_title = UTF16ToUTF8(title); + // Make sure no endline characters can slip in and possibly introduce + // additional lines (like Exec, which makes it a security risk). Also + // use the URL as a default when the title is empty. + if (final_title.empty() || + final_title.find("\n") != std::string::npos || + final_title.find("\r") != std::string::npos) + final_title = url.spec(); + output_buffer += StringPrintf("Name=%s\n", final_title.c_str()); + } else if (tokenizer.token().substr(0, 8) == "Comment=") { + // Skip the line. + } else { + output_buffer += tokenizer.token() + "\n"; + } + } + return output_buffer; +} + +void ShellIntegration::CreateDesktopShortcut(const GURL& url, + const string16& title) { + g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE, + new CreateDesktopShortcutTask(url, title)); +} |