| Index: chrome/browser/shell_integration_linux.cc
|
| diff --git a/chrome/browser/shell_integration_linux.cc b/chrome/browser/shell_integration_linux.cc
|
| deleted file mode 100644
|
| index a97a072ae5622c6e678598059eca651c0bcf4c5a..0000000000000000000000000000000000000000
|
| --- a/chrome/browser/shell_integration_linux.cc
|
| +++ /dev/null
|
| @@ -1,733 +0,0 @@
|
| -// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "chrome/browser/shell_integration_linux.h"
|
| -
|
| -#include <fcntl.h>
|
| -#include <glib.h>
|
| -#include <stdlib.h>
|
| -#include <sys/stat.h>
|
| -#include <sys/types.h>
|
| -#include <unistd.h>
|
| -
|
| -#include <string>
|
| -#include <vector>
|
| -
|
| -#include "base/base_paths.h"
|
| -#include "base/command_line.h"
|
| -#include "base/environment.h"
|
| -#include "base/file_util.h"
|
| -#include "base/files/file_path.h"
|
| -#include "base/files/scoped_temp_dir.h"
|
| -#include "base/i18n/file_util_icu.h"
|
| -#include "base/message_loop.h"
|
| -#include "base/path_service.h"
|
| -#include "base/posix/eintr_wrapper.h"
|
| -#include "base/process_util.h"
|
| -#include "base/strings/string_number_conversions.h"
|
| -#include "base/strings/string_tokenizer.h"
|
| -#include "base/threading/thread.h"
|
| -#include "base/threading/thread_restrictions.h"
|
| -#include "base/utf_string_conversions.h"
|
| -#include "build/build_config.h"
|
| -#include "chrome/browser/web_applications/web_app.h"
|
| -#include "chrome/common/chrome_constants.h"
|
| -#include "content/public/browser/browser_thread.h"
|
| -#include "googleurl/src/gurl.h"
|
| -#include "ui/gfx/codec/png_codec.h"
|
| -#include "ui/gfx/image/image_skia.h"
|
| -#include "ui/gfx/image/image_skia_rep.h"
|
| -
|
| -using content::BrowserThread;
|
| -
|
| -namespace {
|
| -
|
| -// Helper to launch xdg scripts. We don't want them to ask any questions on the
|
| -// terminal etc. The function returns true if the utility launches and exits
|
| -// cleanly, in which case |exit_code| returns the utility's exit code.
|
| -bool LaunchXdgUtility(const std::vector<std::string>& argv, int* exit_code) {
|
| - // xdg-settings internally runs xdg-mime, which uses mv to move newly-created
|
| - // files on top of originals after making changes to them. In the event that
|
| - // the original files are owned by another user (e.g. root, which can happen
|
| - // if they are updated within sudo), mv will prompt the user to confirm if
|
| - // standard input is a terminal (otherwise it just does it). So make sure it's
|
| - // not, to avoid locking everything up waiting for mv.
|
| - *exit_code = EXIT_FAILURE;
|
| - int devnull = open("/dev/null", O_RDONLY);
|
| - if (devnull < 0)
|
| - return false;
|
| - base::FileHandleMappingVector no_stdin;
|
| - no_stdin.push_back(std::make_pair(devnull, STDIN_FILENO));
|
| -
|
| - base::ProcessHandle handle;
|
| - base::LaunchOptions options;
|
| - options.fds_to_remap = &no_stdin;
|
| - if (!base::LaunchProcess(argv, options, &handle)) {
|
| - close(devnull);
|
| - return false;
|
| - }
|
| - close(devnull);
|
| -
|
| - return base::WaitForExitCode(handle, exit_code);
|
| -}
|
| -
|
| -std::string CreateShortcutIcon(
|
| - const ShellIntegration::ShortcutInfo& shortcut_info,
|
| - const base::FilePath& shortcut_filename) {
|
| - if (shortcut_info.favicon.IsEmpty())
|
| - return std::string();
|
| -
|
| - // TODO(phajdan.jr): Report errors from this function, possibly as infobars.
|
| - base::ScopedTempDir temp_dir;
|
| - if (!temp_dir.CreateUniqueTempDir())
|
| - return std::string();
|
| -
|
| - base::FilePath temp_file_path = temp_dir.path().Append(
|
| - shortcut_filename.ReplaceExtension("png"));
|
| - std::string icon_name = temp_file_path.BaseName().RemoveExtension().value();
|
| -
|
| - std::vector<gfx::ImageSkiaRep> image_reps =
|
| - shortcut_info.favicon.ToImageSkia()->image_reps();
|
| - for (std::vector<gfx::ImageSkiaRep>::const_iterator it = image_reps.begin();
|
| - it != image_reps.end(); ++it) {
|
| - std::vector<unsigned char> png_data;
|
| - const SkBitmap& bitmap = it->sk_bitmap();
|
| - if (!gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &png_data)) {
|
| - // If the bitmap could not be encoded to PNG format, skip it.
|
| - LOG(WARNING) << "Could not encode icon " << icon_name << ".png at size "
|
| - << bitmap.width() << ".";
|
| - continue;
|
| - }
|
| - int bytes_written = file_util::WriteFile(temp_file_path,
|
| - reinterpret_cast<char*>(png_data.data()), png_data.size());
|
| -
|
| - if (bytes_written != static_cast<int>(png_data.size()))
|
| - return std::string();
|
| -
|
| - std::vector<std::string> argv;
|
| - argv.push_back("xdg-icon-resource");
|
| - argv.push_back("install");
|
| -
|
| - // Always install in user mode, even if someone runs the browser as root
|
| - // (people do that).
|
| - argv.push_back("--mode");
|
| - argv.push_back("user");
|
| -
|
| - argv.push_back("--size");
|
| - argv.push_back(base::IntToString(bitmap.width()));
|
| -
|
| - argv.push_back(temp_file_path.value());
|
| - argv.push_back(icon_name);
|
| - int exit_code;
|
| - if (!LaunchXdgUtility(argv, &exit_code) || exit_code) {
|
| - LOG(WARNING) << "Could not install icon " << icon_name << ".png at size "
|
| - << bitmap.width() << ".";
|
| - }
|
| - }
|
| - return icon_name;
|
| -}
|
| -
|
| -bool CreateShortcutOnDesktop(const base::FilePath& shortcut_filename,
|
| - const std::string& contents) {
|
| - // Make sure that we will later call openat in a secure way.
|
| - DCHECK_EQ(shortcut_filename.BaseName().value(), shortcut_filename.value());
|
| -
|
| - base::FilePath desktop_path;
|
| - if (!PathService::Get(base::DIR_USER_DESKTOP, &desktop_path))
|
| - return false;
|
| -
|
| - int desktop_fd = open(desktop_path.value().c_str(), O_RDONLY | O_DIRECTORY);
|
| - if (desktop_fd < 0)
|
| - return false;
|
| -
|
| - int fd = openat(desktop_fd, shortcut_filename.value().c_str(),
|
| - O_CREAT | O_EXCL | O_WRONLY,
|
| - S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
|
| - if (fd < 0) {
|
| - if (HANDLE_EINTR(close(desktop_fd)) < 0)
|
| - PLOG(ERROR) << "close";
|
| - return false;
|
| - }
|
| -
|
| - ssize_t bytes_written = file_util::WriteFileDescriptor(fd, contents.data(),
|
| - contents.length());
|
| - if (HANDLE_EINTR(close(fd)) < 0)
|
| - PLOG(ERROR) << "close";
|
| -
|
| - if (bytes_written != static_cast<ssize_t>(contents.length())) {
|
| - // Delete the file. No shortuct is better than corrupted one. Use unlinkat
|
| - // to make sure we're deleting the file in the directory we think we are.
|
| - // Even if an attacker manager to put something other at
|
| - // |shortcut_filename| we'll just undo his action.
|
| - unlinkat(desktop_fd, shortcut_filename.value().c_str(), 0);
|
| - }
|
| -
|
| - if (HANDLE_EINTR(close(desktop_fd)) < 0)
|
| - PLOG(ERROR) << "close";
|
| -
|
| - return true;
|
| -}
|
| -
|
| -void DeleteShortcutOnDesktop(const base::FilePath& shortcut_filename) {
|
| - base::FilePath desktop_path;
|
| - if (PathService::Get(base::DIR_USER_DESKTOP, &desktop_path))
|
| - file_util::Delete(desktop_path.Append(shortcut_filename), false);
|
| -}
|
| -
|
| -bool CreateShortcutInApplicationsMenu(const base::FilePath& shortcut_filename,
|
| - const std::string& contents) {
|
| - base::ScopedTempDir temp_dir;
|
| - if (!temp_dir.CreateUniqueTempDir())
|
| - return false;
|
| -
|
| - base::FilePath temp_file_path = temp_dir.path().Append(shortcut_filename);
|
| -
|
| - int bytes_written = file_util::WriteFile(temp_file_path, contents.data(),
|
| - contents.length());
|
| -
|
| - if (bytes_written != static_cast<int>(contents.length()))
|
| - return false;
|
| -
|
| - std::vector<std::string> argv;
|
| - argv.push_back("xdg-desktop-menu");
|
| - argv.push_back("install");
|
| -
|
| - // Always install in user mode, even if someone runs the browser as root
|
| - // (people do that).
|
| - argv.push_back("--mode");
|
| - argv.push_back("user");
|
| -
|
| - argv.push_back(temp_file_path.value());
|
| - int exit_code;
|
| - LaunchXdgUtility(argv, &exit_code);
|
| - return exit_code == 0;
|
| -}
|
| -
|
| -void DeleteShortcutInApplicationsMenu(const base::FilePath& shortcut_filename) {
|
| - std::vector<std::string> argv;
|
| - argv.push_back("xdg-desktop-menu");
|
| - argv.push_back("uninstall");
|
| -
|
| - // Uninstall in user mode, to match the install.
|
| - argv.push_back("--mode");
|
| - argv.push_back("user");
|
| -
|
| - // The file does not need to exist anywhere - xdg-desktop-menu will uninstall
|
| - // items from the menu with a matching name.
|
| - argv.push_back(shortcut_filename.value());
|
| - int exit_code;
|
| - LaunchXdgUtility(argv, &exit_code);
|
| -}
|
| -
|
| -// Quote a string such that it appears as one verbatim argument for the Exec
|
| -// key in a desktop file.
|
| -std::string QuoteArgForDesktopFileExec(const std::string& arg) {
|
| - // http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html
|
| -
|
| - // Quoting is only necessary if the argument has a reserved character.
|
| - if (arg.find_first_of(" \t\n\"'\\><~|&;$*?#()`") == std::string::npos)
|
| - return arg; // No quoting necessary.
|
| -
|
| - std::string quoted = "\"";
|
| - for (size_t i = 0; i < arg.size(); ++i) {
|
| - // Note that the set of backslashed characters is smaller than the
|
| - // set of reserved characters.
|
| - switch (arg[i]) {
|
| - case '"':
|
| - case '`':
|
| - case '$':
|
| - case '\\':
|
| - quoted += '\\';
|
| - break;
|
| - }
|
| - quoted += arg[i];
|
| - }
|
| - quoted += '"';
|
| -
|
| - return quoted;
|
| -}
|
| -
|
| -// Remove keys from the [Desktop Entry] that would be wrong if copied verbatim
|
| -// into the new .desktop file.
|
| -const char* kDesktopKeysToDelete[] = {
|
| - "GenericName",
|
| - "Comment",
|
| - "MimeType",
|
| - "X-Ayatana-Desktop-Shortcuts",
|
| - "StartupWMClass",
|
| - NULL
|
| -};
|
| -
|
| -const char kDesktopEntry[] = "Desktop Entry";
|
| -
|
| -const char kXdgOpenShebang[] = "#!/usr/bin/env xdg-open";
|
| -
|
| -const char kXdgSettings[] = "xdg-settings";
|
| -const char kXdgSettingsDefaultBrowser[] = "default-web-browser";
|
| -const char kXdgSettingsDefaultSchemeHandler[] = "default-url-scheme-handler";
|
| -
|
| -// Regex to match a localized key name such as "Name[en_AU]".
|
| -const char kLocalizedKeyRegex[] = "^[A-Za-z0-9\\-]+\\[[^\\]]*\\]$";
|
| -
|
| -} // namespace
|
| -
|
| -namespace {
|
| -
|
| -// Utility function to get the path to the version of a script shipped with
|
| -// Chrome. |script| gives the name of the script. |chrome_version| returns the
|
| -// path to the Chrome version of the script, and the return value of the
|
| -// function is true if the function is successful and the Chrome version is
|
| -// not the script found on the PATH.
|
| -bool GetChromeVersionOfScript(const std::string& script,
|
| - std::string* chrome_version) {
|
| - // Get the path to the Chrome version.
|
| - base::FilePath chrome_dir;
|
| - if (!PathService::Get(base::DIR_EXE, &chrome_dir))
|
| - return false;
|
| -
|
| - base::FilePath chrome_version_path = chrome_dir.Append(script);
|
| - *chrome_version = chrome_version_path.value();
|
| -
|
| - // Check if this is different to the one on path.
|
| - std::vector<std::string> argv;
|
| - argv.push_back("which");
|
| - argv.push_back(script);
|
| - std::string path_version;
|
| - if (base::GetAppOutput(CommandLine(argv), &path_version)) {
|
| - // Remove trailing newline
|
| - path_version.erase(path_version.length() - 1, 1);
|
| - base::FilePath path_version_path(path_version);
|
| - return (chrome_version_path != path_version_path);
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -// Value returned by xdg-settings if it can't understand our request.
|
| -const int EXIT_XDG_SETTINGS_SYNTAX_ERROR = 1;
|
| -
|
| -// We delegate the difficulty of setting the default browser and default url
|
| -// scheme handler in Linux desktop environments to an xdg utility, xdg-settings.
|
| -
|
| -// When calling this script we first try to use the script on PATH. If that
|
| -// fails we then try to use the script that we have included. This gives
|
| -// scripts on the system priority over ours, as distribution vendors may have
|
| -// tweaked the script, but still allows our copy to be used if the script on the
|
| -// system fails, as the system copy may be missing capabilities of the Chrome
|
| -// copy.
|
| -
|
| -// If |protocol| is empty this function sets Chrome as the default browser,
|
| -// otherwise it sets Chrome as the default handler application for |protocol|.
|
| -bool SetDefaultWebClient(const std::string& protocol) {
|
| -#if defined(OS_CHROMEOS)
|
| - return true;
|
| -#else
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
|
| -
|
| - scoped_ptr<base::Environment> env(base::Environment::Create());
|
| -
|
| - std::vector<std::string> argv;
|
| - argv.push_back(kXdgSettings);
|
| - argv.push_back("set");
|
| - if (protocol.empty()) {
|
| - argv.push_back(kXdgSettingsDefaultBrowser);
|
| - } else {
|
| - argv.push_back(kXdgSettingsDefaultSchemeHandler);
|
| - argv.push_back(protocol);
|
| - }
|
| - argv.push_back(ShellIntegrationLinux::GetDesktopName(env.get()));
|
| -
|
| - int exit_code;
|
| - bool ran_ok = LaunchXdgUtility(argv, &exit_code);
|
| - if (ran_ok && exit_code == EXIT_XDG_SETTINGS_SYNTAX_ERROR) {
|
| - if (GetChromeVersionOfScript(kXdgSettings, &argv[0])) {
|
| - ran_ok = LaunchXdgUtility(argv, &exit_code);
|
| - }
|
| - }
|
| -
|
| - return ran_ok && exit_code == EXIT_SUCCESS;
|
| -#endif
|
| -}
|
| -
|
| -// If |protocol| is empty this function checks if Chrome is the default browser,
|
| -// otherwise it checks if Chrome is the default handler application for
|
| -// |protocol|.
|
| -ShellIntegration::DefaultWebClientState GetIsDefaultWebClient(
|
| - const std::string& protocol) {
|
| -#if defined(OS_CHROMEOS)
|
| - return ShellIntegration::IS_DEFAULT;
|
| -#else
|
| - base::ThreadRestrictions::AssertIOAllowed();
|
| -
|
| - scoped_ptr<base::Environment> env(base::Environment::Create());
|
| -
|
| - std::vector<std::string> argv;
|
| - argv.push_back(kXdgSettings);
|
| - argv.push_back("check");
|
| - if (protocol.empty()) {
|
| - argv.push_back(kXdgSettingsDefaultBrowser);
|
| - } else {
|
| - argv.push_back(kXdgSettingsDefaultSchemeHandler);
|
| - argv.push_back(protocol);
|
| - }
|
| - argv.push_back(ShellIntegrationLinux::GetDesktopName(env.get()));
|
| -
|
| - std::string reply;
|
| - int success_code;
|
| - bool ran_ok = base::GetAppOutputWithExitCode(CommandLine(argv), &reply,
|
| - &success_code);
|
| - if (ran_ok && success_code == EXIT_XDG_SETTINGS_SYNTAX_ERROR) {
|
| - if (GetChromeVersionOfScript(kXdgSettings, &argv[0])) {
|
| - ran_ok = base::GetAppOutputWithExitCode(CommandLine(argv), &reply,
|
| - &success_code);
|
| - }
|
| - }
|
| -
|
| - if (!ran_ok || success_code != EXIT_SUCCESS) {
|
| - // xdg-settings failed: we can't determine or set the default browser.
|
| - return ShellIntegration::UNKNOWN_DEFAULT;
|
| - }
|
| -
|
| - // Allow any reply that starts with "yes".
|
| - return (reply.find("yes") == 0) ? ShellIntegration::IS_DEFAULT :
|
| - ShellIntegration::NOT_DEFAULT;
|
| -#endif
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -// static
|
| -ShellIntegration::DefaultWebClientSetPermission
|
| - ShellIntegration::CanSetAsDefaultBrowser() {
|
| - return SET_DEFAULT_UNATTENDED;
|
| -}
|
| -
|
| -// static
|
| -bool ShellIntegration::SetAsDefaultBrowser() {
|
| - return SetDefaultWebClient("");
|
| -}
|
| -
|
| -// static
|
| -bool ShellIntegration::SetAsDefaultProtocolClient(const std::string& protocol) {
|
| - return SetDefaultWebClient(protocol);
|
| -}
|
| -
|
| -// static
|
| -ShellIntegration::DefaultWebClientState ShellIntegration::GetDefaultBrowser() {
|
| - return GetIsDefaultWebClient("");
|
| -}
|
| -
|
| -// static
|
| -std::string ShellIntegration::GetApplicationForProtocol(const GURL& url) {
|
| - return std::string("xdg-open");
|
| -}
|
| -
|
| -// static
|
| -ShellIntegration::DefaultWebClientState
|
| -ShellIntegration::IsDefaultProtocolClient(const std::string& protocol) {
|
| - return GetIsDefaultWebClient(protocol);
|
| -}
|
| -
|
| -// static
|
| -bool ShellIntegration::IsFirefoxDefaultBrowser() {
|
| - std::vector<std::string> argv;
|
| - argv.push_back(kXdgSettings);
|
| - argv.push_back("get");
|
| - argv.push_back(kXdgSettingsDefaultBrowser);
|
| -
|
| - std::string browser;
|
| - // We don't care about the return value here.
|
| - base::GetAppOutput(CommandLine(argv), &browser);
|
| - return browser.find("irefox") != std::string::npos;
|
| -}
|
| -
|
| -namespace ShellIntegrationLinux {
|
| -
|
| -std::string GetDesktopName(base::Environment* env) {
|
| -#if defined(GOOGLE_CHROME_BUILD)
|
| - return "google-chrome.desktop";
|
| -#else // CHROMIUM_BUILD
|
| - // Allow $CHROME_DESKTOP to override the built-in value, so that development
|
| - // versions can set themselves as the default without interfering with
|
| - // non-official, packaged versions using the built-in value.
|
| - std::string name;
|
| - if (env->GetVar("CHROME_DESKTOP", &name) && !name.empty())
|
| - return name;
|
| - return "chromium-browser.desktop";
|
| -#endif
|
| -}
|
| -
|
| -bool GetDesktopShortcutTemplate(base::Environment* env,
|
| - std::string* output) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
|
| -
|
| - std::vector<base::FilePath> search_paths;
|
| -
|
| - std::string xdg_data_home;
|
| - if (env->GetVar("XDG_DATA_HOME", &xdg_data_home) &&
|
| - !xdg_data_home.empty()) {
|
| - search_paths.push_back(base::FilePath(xdg_data_home));
|
| - }
|
| -
|
| - std::string xdg_data_dirs;
|
| - if (env->GetVar("XDG_DATA_DIRS", &xdg_data_dirs) &&
|
| - !xdg_data_dirs.empty()) {
|
| - base::StringTokenizer tokenizer(xdg_data_dirs, ":");
|
| - while (tokenizer.GetNext()) {
|
| - base::FilePath data_dir(tokenizer.token());
|
| - search_paths.push_back(data_dir);
|
| - search_paths.push_back(data_dir.Append("applications"));
|
| - }
|
| - }
|
| -
|
| - // Add some fallback paths for systems which don't have XDG_DATA_DIRS or have
|
| - // it incomplete.
|
| - search_paths.push_back(base::FilePath("/usr/share/applications"));
|
| - search_paths.push_back(base::FilePath("/usr/local/share/applications"));
|
| -
|
| - std::string template_filename(GetDesktopName(env));
|
| - for (std::vector<base::FilePath>::const_iterator i = search_paths.begin();
|
| - i != search_paths.end(); ++i) {
|
| - base::FilePath path = i->Append(template_filename);
|
| - VLOG(1) << "Looking for desktop file template in " << path.value();
|
| - if (file_util::PathExists(path)) {
|
| - VLOG(1) << "Found desktop file template at " << path.value();
|
| - return file_util::ReadFileToString(path, output);
|
| - }
|
| - }
|
| -
|
| - LOG(ERROR) << "Could not find desktop file template.";
|
| - return false;
|
| -}
|
| -
|
| -base::FilePath GetWebShortcutFilename(const GURL& url) {
|
| - // Use a prefix, because xdg-desktop-menu requires it.
|
| - std::string filename =
|
| - std::string(chrome::kBrowserProcessExecutableName) + "-" + url.spec();
|
| - file_util::ReplaceIllegalCharactersInPath(&filename, '_');
|
| -
|
| - base::FilePath desktop_path;
|
| - if (!PathService::Get(base::DIR_USER_DESKTOP, &desktop_path))
|
| - return base::FilePath();
|
| -
|
| - base::FilePath filepath = desktop_path.Append(filename);
|
| - base::FilePath alternative_filepath(filepath.value() + ".desktop");
|
| - for (size_t i = 1; i < 100; ++i) {
|
| - if (file_util::PathExists(base::FilePath(alternative_filepath))) {
|
| - alternative_filepath = base::FilePath(
|
| - filepath.value() + "_" + base::IntToString(i) + ".desktop");
|
| - } else {
|
| - return base::FilePath(alternative_filepath).BaseName();
|
| - }
|
| - }
|
| -
|
| - return base::FilePath();
|
| -}
|
| -
|
| -base::FilePath GetExtensionShortcutFilename(const base::FilePath& profile_path,
|
| - const std::string& extension_id) {
|
| - DCHECK(!extension_id.empty());
|
| -
|
| - // Use a prefix, because xdg-desktop-menu requires it.
|
| - std::string filename(chrome::kBrowserProcessExecutableName);
|
| - filename.append("-")
|
| - .append(extension_id)
|
| - .append("-")
|
| - .append(profile_path.BaseName().value());
|
| - file_util::ReplaceIllegalCharactersInPath(&filename, '_');
|
| - return base::FilePath(filename.append(".desktop"));
|
| -}
|
| -
|
| -std::string GetDesktopFileContents(
|
| - const std::string& template_contents,
|
| - const std::string& app_name,
|
| - const GURL& url,
|
| - const std::string& extension_id,
|
| - const base::FilePath& extension_path,
|
| - const string16& title,
|
| - const std::string& icon_name,
|
| - const base::FilePath& profile_path) {
|
| - // Although not required by the spec, Nautilus on Ubuntu Karmic creates its
|
| - // launchers with an xdg-open shebang. Follow that convention.
|
| - std::string output_buffer = std::string(kXdgOpenShebang) + "\n";
|
| - if (template_contents.empty())
|
| - return output_buffer;
|
| -
|
| - // See http://standards.freedesktop.org/desktop-entry-spec/latest/
|
| - // http://developer.gnome.org/glib/unstable/glib-Key-value-file-parser.html
|
| - GKeyFile* key_file = g_key_file_new();
|
| - GError* err = NULL;
|
| - // Loading the data will strip translations and comments from the desktop
|
| - // file (which we want to do!)
|
| - if (!g_key_file_load_from_data(
|
| - key_file,
|
| - template_contents.c_str(),
|
| - template_contents.size(),
|
| - G_KEY_FILE_NONE,
|
| - &err)) {
|
| - NOTREACHED() << "Unable to read desktop file template:" << err->message;
|
| - g_error_free(err);
|
| - return output_buffer;
|
| - }
|
| -
|
| - // Remove all sections except for the Desktop Entry
|
| - gsize length = 0;
|
| - gchar** groups = g_key_file_get_groups(key_file, &length);
|
| - for (gsize i = 0; i < length; ++i) {
|
| - if (strcmp(groups[i], kDesktopEntry) != 0) {
|
| - g_key_file_remove_group(key_file, groups[i], NULL);
|
| - }
|
| - }
|
| - g_strfreev(groups);
|
| -
|
| - // Remove keys that we won't need.
|
| - for (const char** current_key = kDesktopKeysToDelete; *current_key;
|
| - ++current_key) {
|
| - g_key_file_remove_key(key_file, kDesktopEntry, *current_key, NULL);
|
| - }
|
| - // Remove all localized keys.
|
| - GRegex* localized_key_regex = g_regex_new(kLocalizedKeyRegex,
|
| - static_cast<GRegexCompileFlags>(0),
|
| - static_cast<GRegexMatchFlags>(0),
|
| - NULL);
|
| - gchar** keys = g_key_file_get_keys(key_file, kDesktopEntry, NULL, NULL);
|
| - for (gchar** keys_ptr = keys; *keys_ptr; ++keys_ptr) {
|
| - if (g_regex_match(localized_key_regex, *keys_ptr,
|
| - static_cast<GRegexMatchFlags>(0), NULL)) {
|
| - g_key_file_remove_key(key_file, kDesktopEntry, *keys_ptr, NULL);
|
| - }
|
| - }
|
| - g_strfreev(keys);
|
| - g_regex_unref(localized_key_regex);
|
| -
|
| - // Set the "Name" key.
|
| - 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();
|
| - }
|
| - g_key_file_set_string(key_file, kDesktopEntry, "Name", final_title.c_str());
|
| -
|
| - // Set the "Exec" key.
|
| - char* exec_c_string = g_key_file_get_string(key_file, kDesktopEntry, "Exec",
|
| - NULL);
|
| - if (exec_c_string) {
|
| - std::string exec_string(exec_c_string);
|
| - g_free(exec_c_string);
|
| - base::StringTokenizer exec_tokenizer(exec_string, " ");
|
| -
|
| - std::string final_path;
|
| - while (exec_tokenizer.GetNext() && exec_tokenizer.token() != "%U") {
|
| - if (!final_path.empty())
|
| - final_path += " ";
|
| - final_path += exec_tokenizer.token();
|
| - }
|
| - CommandLine cmd_line(CommandLine::NO_PROGRAM);
|
| - cmd_line = ShellIntegration::CommandLineArgsForLauncher(
|
| - url, extension_id, profile_path);
|
| - const CommandLine::SwitchMap& switch_map = cmd_line.GetSwitches();
|
| - for (CommandLine::SwitchMap::const_iterator i = switch_map.begin();
|
| - i != switch_map.end(); ++i) {
|
| - if (i->second.empty()) {
|
| - final_path += " --" + i->first;
|
| - } else {
|
| - final_path += " " + QuoteArgForDesktopFileExec("--" + i->first +
|
| - "=" + i->second);
|
| - }
|
| - }
|
| -
|
| - g_key_file_set_string(key_file, kDesktopEntry, "Exec", final_path.c_str());
|
| - }
|
| -
|
| - // Set the "Icon" key.
|
| - if (!icon_name.empty())
|
| - g_key_file_set_string(key_file, kDesktopEntry, "Icon", icon_name.c_str());
|
| -
|
| -#if defined(TOOLKIT_GTK)
|
| - std::string wmclass = web_app::GetWMClassFromAppName(app_name);
|
| - g_key_file_set_string(key_file, kDesktopEntry, "StartupWMClass",
|
| - wmclass.c_str());
|
| -#endif
|
| -
|
| - length = 0;
|
| - gchar* data_dump = g_key_file_to_data(key_file, &length, NULL);
|
| - if (data_dump) {
|
| - // If strlen(data_dump[0]) == 0, this check will fail.
|
| - if (data_dump[0] == '\n') {
|
| - // Older versions of glib produce a leading newline. If this is the case,
|
| - // remove it to avoid double-newline after the shebang.
|
| - output_buffer += (data_dump + 1);
|
| - } else {
|
| - output_buffer += data_dump;
|
| - }
|
| - g_free(data_dump);
|
| - }
|
| -
|
| - g_key_file_free(key_file);
|
| - return output_buffer;
|
| -}
|
| -
|
| -bool CreateDesktopShortcut(
|
| - const ShellIntegration::ShortcutInfo& shortcut_info,
|
| - const std::string& shortcut_template) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
|
| -
|
| - base::FilePath shortcut_filename;
|
| - if (!shortcut_info.extension_id.empty()) {
|
| - shortcut_filename = GetExtensionShortcutFilename(
|
| - shortcut_info.profile_path, shortcut_info.extension_id);
|
| - // For extensions we do not want duplicate shortcuts. So, delete any that
|
| - // already exist and replace them.
|
| - if (shortcut_info.create_on_desktop)
|
| - DeleteShortcutOnDesktop(shortcut_filename);
|
| - if (shortcut_info.create_in_applications_menu)
|
| - DeleteShortcutInApplicationsMenu(shortcut_filename);
|
| - } else {
|
| - shortcut_filename = GetWebShortcutFilename(shortcut_info.url);
|
| - }
|
| - if (shortcut_filename.empty())
|
| - return false;
|
| -
|
| - std::string icon_name = CreateShortcutIcon(shortcut_info, shortcut_filename);
|
| -
|
| - std::string app_name =
|
| - web_app::GenerateApplicationNameFromInfo(shortcut_info);
|
| - std::string contents = ShellIntegrationLinux::GetDesktopFileContents(
|
| - shortcut_template,
|
| - app_name,
|
| - shortcut_info.url,
|
| - shortcut_info.extension_id,
|
| - shortcut_info.extension_path,
|
| - shortcut_info.title,
|
| - icon_name,
|
| - shortcut_info.profile_path);
|
| -
|
| - bool success = true;
|
| -
|
| - if (shortcut_info.create_on_desktop)
|
| - success = CreateShortcutOnDesktop(shortcut_filename, contents);
|
| -
|
| - if (shortcut_info.create_in_applications_menu)
|
| - success = CreateShortcutInApplicationsMenu(shortcut_filename, contents) &&
|
| - success;
|
| -
|
| - return success;
|
| -}
|
| -
|
| -void DeleteDesktopShortcuts(const base::FilePath& profile_path,
|
| - const std::string& extension_id) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
|
| -
|
| - base::FilePath shortcut_filename = GetExtensionShortcutFilename(
|
| - profile_path, extension_id);
|
| - DCHECK(!shortcut_filename.empty());
|
| -
|
| - DeleteShortcutOnDesktop(shortcut_filename);
|
| - DeleteShortcutInApplicationsMenu(shortcut_filename);
|
| -}
|
| -
|
| -} // namespace ShellIntegrationLinux
|
|
|