[M85 Dev][EFL] Fix errors to generate ninja files
[platform/framework/web/chromium-efl.git] / chrome / browser / shell_integration_linux.cc
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
3 // found in the LICENSE file.
4
5 #include "chrome/browser/shell_integration_linux.h"
6
7 #include <fcntl.h>
8 #include <stddef.h>
9 #include <stdlib.h>
10 #include <sys/stat.h>
11 #include <sys/types.h>
12 #include <unistd.h>
13
14 #include <memory>
15 #include <sstream>
16 #include <string>
17 #include <utility>
18 #include <vector>
19
20 #include "base/base_paths.h"
21 #include "base/command_line.h"
22 #include "base/environment.h"
23 #include "base/files/file_enumerator.h"
24 #include "base/files/file_path.h"
25 #include "base/files/file_util.h"
26 #include "base/i18n/file_util_icu.h"
27 #include "base/logging.h"
28 #include "base/memory/ref_counted_memory.h"
29 #include "base/nix/xdg_util.h"
30 #include "base/path_service.h"
31 #include "base/posix/eintr_wrapper.h"
32 #include "base/process/kill.h"
33 #include "base/process/launch.h"
34 #include "base/strings/string_number_conversions.h"
35 #include "base/strings/string_tokenizer.h"
36 #include "base/strings/string_util.h"
37 #include "base/strings/stringprintf.h"
38 #include "base/strings/utf_string_conversions.h"
39 #include "base/threading/scoped_blocking_call.h"
40 #include "base/threading/thread.h"
41 #include "base/threading/thread_restrictions.h"
42 #include "build/branding_buildflags.h"
43 #include "build/build_config.h"
44 #include "chrome/browser/shell_integration.h"
45 #include "chrome/common/buildflags.h"
46 #include "chrome/common/channel_info.h"
47 #include "chrome/common/chrome_constants.h"
48 #include "chrome/common/chrome_switches.h"
49 #include "chrome/grit/chrome_unscaled_resources.h"
50 #include "components/version_info/version_info.h"
51 #include "ui/base/resource/resource_bundle.h"
52 #include "ui/gfx/image/image_family.h"
53 #include "url/gurl.h"
54
55 #if defined(USE_GLIB)
56 #include <glib.h>
57 #endif
58
59 namespace shell_integration_linux {
60
61 const char kXdgSettings[] = "xdg-settings";
62 const char kXdgSettingsDefaultBrowser[] = "default-web-browser";
63 const char kXdgSettingsDefaultSchemeHandler[] = "default-url-scheme-handler";
64
65 // Utility function to get the path to the version of a script shipped with
66 // Chrome. |script| gives the name of the script. |chrome_version| returns the
67 // path to the Chrome version of the script, and the return value of the
68 // function is true if the function is successful and the Chrome version is
69 // not the script found on the PATH.
70 bool GetChromeVersionOfScript(const std::string& script,
71                               std::string* chrome_version) {
72   // Get the path to the Chrome version.
73   base::FilePath chrome_dir;
74   if (!base::PathService::Get(base::DIR_EXE, &chrome_dir))
75     return false;
76
77   base::FilePath chrome_version_path = chrome_dir.Append(script);
78   *chrome_version = chrome_version_path.value();
79
80   // Check if this is different to the one on path.
81   std::vector<std::string> argv;
82   argv.push_back("which");
83   argv.push_back(script);
84   std::string path_version;
85   if (base::GetAppOutput(base::CommandLine(argv), &path_version)) {
86     // Remove trailing newline
87     path_version.erase(path_version.length() - 1, 1);
88     base::FilePath path_version_path(path_version);
89     return (chrome_version_path != path_version_path);
90   }
91   return false;
92 }
93
94 // Value returned by xdg-settings if it can't understand our request.
95 const int EXIT_XDG_SETTINGS_SYNTAX_ERROR = 1;
96
97 // We delegate the difficulty of setting the default browser and default url
98 // scheme handler in Linux desktop environments to an xdg utility, xdg-settings.
99
100 // When calling this script we first try to use the script on PATH. If that
101 // fails we then try to use the script that we have included. This gives
102 // scripts on the system priority over ours, as distribution vendors may have
103 // tweaked the script, but still allows our copy to be used if the script on the
104 // system fails, as the system copy may be missing capabilities of the Chrome
105 // copy.
106
107 // If |protocol| is empty this function sets Chrome as the default browser,
108 // otherwise it sets Chrome as the default handler application for |protocol|.
109 bool SetDefaultWebClient(const std::string& protocol) {
110 #if defined(OS_CHROMEOS)
111   return true;
112 #else
113   std::unique_ptr<base::Environment> env(base::Environment::Create());
114
115   std::vector<std::string> argv;
116   argv.push_back(kXdgSettings);
117   argv.push_back("set");
118   if (protocol.empty()) {
119     argv.push_back(kXdgSettingsDefaultBrowser);
120   } else {
121     argv.push_back(kXdgSettingsDefaultSchemeHandler);
122     argv.push_back(protocol);
123   }
124   argv.push_back(chrome::GetDesktopName(env.get()));
125
126   int exit_code;
127   bool ran_ok = LaunchXdgUtility(argv, &exit_code);
128   if (ran_ok && exit_code == EXIT_XDG_SETTINGS_SYNTAX_ERROR) {
129     if (GetChromeVersionOfScript(kXdgSettings, &argv[0])) {
130       ran_ok = LaunchXdgUtility(argv, &exit_code);
131     }
132   }
133
134   return ran_ok && exit_code == EXIT_SUCCESS;
135 #endif
136 }
137
138 // If |protocol| is empty this function checks if Chrome is the default browser,
139 // otherwise it checks if Chrome is the default handler application for
140 // |protocol|.
141 shell_integration::DefaultWebClientState GetIsDefaultWebClient(
142     const std::string& protocol) {
143 #if defined(OS_CHROMEOS)
144   return shell_integration::UNKNOWN_DEFAULT;
145 #else
146   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
147                                                 base::BlockingType::MAY_BLOCK);
148
149   std::unique_ptr<base::Environment> env(base::Environment::Create());
150
151   std::vector<std::string> argv;
152   argv.push_back(kXdgSettings);
153   argv.push_back("check");
154   if (protocol.empty()) {
155     argv.push_back(kXdgSettingsDefaultBrowser);
156   } else {
157     argv.push_back(kXdgSettingsDefaultSchemeHandler);
158     argv.push_back(protocol);
159   }
160   argv.push_back(chrome::GetDesktopName(env.get()));
161
162   std::string reply;
163   int success_code;
164   bool ran_ok = base::GetAppOutputWithExitCode(base::CommandLine(argv), &reply,
165                                                &success_code);
166   if (ran_ok && success_code == EXIT_XDG_SETTINGS_SYNTAX_ERROR) {
167     if (GetChromeVersionOfScript(kXdgSettings, &argv[0])) {
168       ran_ok = base::GetAppOutputWithExitCode(base::CommandLine(argv), &reply,
169                                               &success_code);
170     }
171   }
172
173   if (!ran_ok || success_code != EXIT_SUCCESS) {
174     // xdg-settings failed: we can't determine or set the default browser.
175     return shell_integration::UNKNOWN_DEFAULT;
176   }
177
178   // Allow any reply that starts with "yes".
179   return base::StartsWith(reply, "yes", base::CompareCase::SENSITIVE)
180              ? shell_integration::IS_DEFAULT
181              : shell_integration::NOT_DEFAULT;
182 #endif
183 }
184
185 // https://wiki.gnome.org/Projects/GnomeShell/ApplicationBased
186 // The WM_CLASS property should be set to the same as the *.desktop file without
187 // the .desktop extension.  We cannot simply use argv[0] in this case, because
188 // on the stable channel, the executable name is google-chrome-stable, but the
189 // desktop file is google-chrome.desktop.
190 std::string GetDesktopBaseName(const std::string& desktop_file_name) {
191   static const char kDesktopExtension[] = ".desktop";
192   if (base::EndsWith(desktop_file_name, kDesktopExtension,
193                      base::CompareCase::SENSITIVE)) {
194     return desktop_file_name.substr(
195         0, desktop_file_name.length() - strlen(kDesktopExtension));
196   }
197   return desktop_file_name;
198 }
199
200 namespace {
201
202 #if defined(USE_GLIB)
203 // Quote a string such that it appears as one verbatim argument for the Exec
204 // key in a desktop file.
205 std::string QuoteArgForDesktopFileExec(const std::string& arg) {
206   // http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html
207
208   // Quoting is only necessary if the argument has a reserved character.
209   if (arg.find_first_of(" \t\n\"'\\><~|&;$*?#()`") == std::string::npos)
210     return arg;  // No quoting necessary.
211
212   std::string quoted = "\"";
213   for (size_t i = 0; i < arg.size(); ++i) {
214     // Note that the set of backslashed characters is smaller than the
215     // set of reserved characters.
216     switch (arg[i]) {
217       case '"':
218       case '`':
219       case '$':
220       case '\\':
221         quoted += '\\';
222         break;
223     }
224     quoted += arg[i];
225   }
226   quoted += '"';
227
228   return quoted;
229 }
230
231 // Quote a command line so it is suitable for use as the Exec key in a desktop
232 // file. Note: This should be used instead of GetCommandLineString, which does
233 // not properly quote the string; this function is designed for the Exec key.
234 std::string QuoteCommandLineForDesktopFileExec(
235     const base::CommandLine& command_line) {
236   // http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html
237
238   std::string quoted_path;
239   const base::CommandLine::StringVector& argv = command_line.argv();
240   for (auto i = argv.begin(); i != argv.end(); ++i) {
241     if (i != argv.begin())
242       quoted_path += " ";
243     quoted_path += QuoteArgForDesktopFileExec(*i);
244   }
245
246   return quoted_path;
247 }
248 #endif
249
250 #if defined(USE_GLIB)
251 const char kDesktopEntry[] = "Desktop Entry";
252 const char kXdgOpenShebang[] = "#!/usr/bin/env xdg-open";
253 #endif
254
255 }  // namespace
256
257 // Allows LaunchXdgUtility to join a process.
258 // thread_restrictions.h assumes it to be in shell_integration_linux namespace.
259 class LaunchXdgUtilityScopedAllowBaseSyncPrimitives
260     : public base::ScopedAllowBaseSyncPrimitives {};
261
262 bool LaunchXdgUtility(const std::vector<std::string>& argv, int* exit_code) {
263   // xdg-settings internally runs xdg-mime, which uses mv to move newly-created
264   // files on top of originals after making changes to them. In the event that
265   // the original files are owned by another user (e.g. root, which can happen
266   // if they are updated within sudo), mv will prompt the user to confirm if
267   // standard input is a terminal (otherwise it just does it). So make sure it's
268   // not, to avoid locking everything up waiting for mv.
269   *exit_code = EXIT_FAILURE;
270   int devnull = open("/dev/null", O_RDONLY);
271   if (devnull < 0)
272     return false;
273
274   base::LaunchOptions options;
275   options.fds_to_remap.push_back(std::make_pair(devnull, STDIN_FILENO));
276   base::Process process = base::LaunchProcess(argv, options);
277   close(devnull);
278   if (!process.IsValid())
279     return false;
280   LaunchXdgUtilityScopedAllowBaseSyncPrimitives allow_base_sync_primitives;
281   return process.WaitForExit(exit_code);
282 }
283
284 std::string GetWMClassFromAppName(std::string app_name) {
285   base::i18n::ReplaceIllegalCharactersInPath(&app_name, '_');
286   base::TrimString(app_name, "_", &app_name);
287   return app_name;
288 }
289
290 base::FilePath GetDataWriteLocation(base::Environment* env) {
291   return base::nix::GetXDGDirectory(env, "XDG_DATA_HOME", ".local/share");
292 }
293
294 std::vector<base::FilePath> GetDataSearchLocations(base::Environment* env) {
295   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
296                                                 base::BlockingType::MAY_BLOCK);
297
298   std::vector<base::FilePath> search_paths;
299   base::FilePath write_location = GetDataWriteLocation(env);
300   search_paths.push_back(write_location);
301
302   std::string xdg_data_dirs;
303   if (env->GetVar("XDG_DATA_DIRS", &xdg_data_dirs) && !xdg_data_dirs.empty()) {
304     base::StringTokenizer tokenizer(xdg_data_dirs, ":");
305     while (tokenizer.GetNext()) {
306       base::FilePath data_dir(tokenizer.token());
307       search_paths.push_back(data_dir);
308     }
309   } else {
310     search_paths.push_back(base::FilePath("/usr/local/share"));
311     search_paths.push_back(base::FilePath("/usr/share"));
312   }
313
314   return search_paths;
315 }
316
317 namespace internal {
318
319 // Get the value of NoDisplay from the [Desktop Entry] section of a .desktop
320 // file, given in |shortcut_contents|. If the key is not found, returns false.
321 bool GetNoDisplayFromDesktopFile(const std::string& shortcut_contents) {
322 #if defined(USE_GLIB)
323   // An empty file causes a crash with glib <= 2.32, so special case here.
324   if (shortcut_contents.empty())
325     return false;
326
327   GKeyFile* key_file = g_key_file_new();
328   GError* err = NULL;
329   if (!g_key_file_load_from_data(key_file, shortcut_contents.c_str(),
330                                  shortcut_contents.size(), G_KEY_FILE_NONE,
331                                  &err)) {
332     LOG(WARNING) << "Unable to read desktop file template: " << err->message;
333     g_error_free(err);
334     g_key_file_free(key_file);
335     return false;
336   }
337
338   bool nodisplay = false;
339   char* nodisplay_c_string = g_key_file_get_string(key_file, kDesktopEntry,
340                                                    "NoDisplay", &err);
341   if (nodisplay_c_string) {
342     if (!g_strcmp0(nodisplay_c_string, "true"))
343       nodisplay = true;
344     g_free(nodisplay_c_string);
345   } else {
346     g_error_free(err);
347   }
348
349   g_key_file_free(key_file);
350   return nodisplay;
351 #else
352   NOTIMPLEMENTED();
353   return false;
354 #endif
355 }
356
357 // Gets the path to the Chrome executable or wrapper script.
358 // Returns an empty path if the executable path could not be found, which should
359 // never happen.
360 base::FilePath GetChromeExePath() {
361   // Try to get the name of the wrapper script that launched Chrome.
362   std::unique_ptr<base::Environment> environment(base::Environment::Create());
363   std::string wrapper_script;
364   if (environment->GetVar("CHROME_WRAPPER", &wrapper_script))
365     return base::FilePath(wrapper_script);
366
367   // Just return the name of the executable path for Chrome.
368   base::FilePath chrome_exe_path;
369   base::PathService::Get(base::FILE_EXE, &chrome_exe_path);
370   return chrome_exe_path;
371 }
372
373 std::string GetProgramClassName(const base::CommandLine& command_line,
374                                 const std::string& desktop_file_name) {
375   std::string class_name = GetDesktopBaseName(desktop_file_name);
376   std::string user_data_dir =
377       command_line.GetSwitchValueNative(switches::kUserDataDir);
378   // If the user launches with e.g. --user-data-dir=/tmp/my-user-data, set the
379   // class name to "Chrome (/tmp/my-user-data)".  The class name will show up in
380   // the alt-tab list in gnome-shell if you're running a binary that doesn't
381   // have a matching .desktop file.
382   return user_data_dir.empty()
383              ? class_name
384              : class_name + " (" + user_data_dir + ")";
385 }
386
387 std::string GetProgramClassClass(const base::CommandLine& command_line,
388                                  const std::string& desktop_file_name) {
389   if (command_line.HasSwitch(switches::kWmClass))
390     return command_line.GetSwitchValueASCII(switches::kWmClass);
391   std::string class_class = GetDesktopBaseName(desktop_file_name);
392   if (!class_class.empty()) {
393     // Capitalize the first character like gtk does.
394     class_class[0] = base::ToUpperASCII(class_class[0]);
395   }
396   return class_class;
397 }
398
399 }  // namespace internal
400
401 std::string GetProgramClassName() {
402   std::unique_ptr<base::Environment> env(base::Environment::Create());
403   return internal::GetProgramClassName(*base::CommandLine::ForCurrentProcess(),
404                                        chrome::GetDesktopName(env.get()));
405 }
406
407 std::string GetProgramClassClass() {
408   std::unique_ptr<base::Environment> env(base::Environment::Create());
409   return internal::GetProgramClassClass(*base::CommandLine::ForCurrentProcess(),
410                                         chrome::GetDesktopName(env.get()));
411 }
412
413 std::string GetIconName() {
414 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
415   return "google-chrome";
416 #else  // BUILDFLAG(CHROMIUM_BRANDING)
417   return "chromium-browser";
418 #endif
419 }
420
421 bool GetExistingShortcutContents(base::Environment* env,
422                                  const base::FilePath& desktop_filename,
423                                  std::string* output) {
424   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
425                                                 base::BlockingType::MAY_BLOCK);
426
427   std::vector<base::FilePath> search_paths = GetDataSearchLocations(env);
428
429   for (std::vector<base::FilePath>::const_iterator i = search_paths.begin();
430        i != search_paths.end(); ++i) {
431     base::FilePath path = i->Append("applications").Append(desktop_filename);
432     VLOG(1) << "Looking for desktop file in " << path.value();
433     if (base::PathExists(path)) {
434       VLOG(1) << "Found desktop file at " << path.value();
435       return base::ReadFileToString(path, output);
436     }
437   }
438
439   return false;
440 }
441
442 base::FilePath GetWebShortcutFilename(const GURL& url) {
443   // Use a prefix, because xdg-desktop-menu requires it.
444   std::string filename =
445       std::string(chrome::kBrowserProcessExecutableName) + "-" + url.spec();
446   base::i18n::ReplaceIllegalCharactersInPath(&filename, '_');
447
448   base::FilePath desktop_path;
449   if (!base::PathService::Get(base::DIR_USER_DESKTOP, &desktop_path))
450     return base::FilePath();
451
452   base::FilePath filepath = desktop_path.Append(filename);
453   base::FilePath alternative_filepath(filepath.value() + ".desktop");
454   for (size_t i = 1; i < 100; ++i) {
455     if (base::PathExists(base::FilePath(alternative_filepath))) {
456       alternative_filepath = base::FilePath(
457           filepath.value() + "_" + base::NumberToString(i) + ".desktop");
458     } else {
459       return base::FilePath(alternative_filepath).BaseName();
460     }
461   }
462
463   return base::FilePath();
464 }
465
466 std::vector<base::FilePath> GetExistingProfileShortcutFilenames(
467     const base::FilePath& profile_path,
468     const base::FilePath& directory) {
469   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
470                                                 base::BlockingType::MAY_BLOCK);
471
472   // Use a prefix, because xdg-desktop-menu requires it.
473   std::string prefix(chrome::kBrowserProcessExecutableName);
474   prefix.append("-");
475   std::string suffix("-");
476   suffix.append(profile_path.BaseName().value());
477   base::i18n::ReplaceIllegalCharactersInPath(&suffix, '_');
478   // Spaces in filenames break xdg-desktop-menu
479   // (see https://bugs.freedesktop.org/show_bug.cgi?id=66605).
480   base::ReplaceChars(suffix, " ", "_", &suffix);
481   std::string glob = prefix + "*" + suffix + ".desktop";
482
483   base::FileEnumerator files(directory, false, base::FileEnumerator::FILES,
484                              glob);
485   base::FilePath shortcut_file = files.Next();
486   std::vector<base::FilePath> shortcut_paths;
487   while (!shortcut_file.empty()) {
488     shortcut_paths.push_back(shortcut_file.BaseName());
489     shortcut_file = files.Next();
490   }
491   return shortcut_paths;
492 }
493
494 std::string GetDesktopFileContents(const base::FilePath& chrome_exe_path,
495                                    const std::string& app_name,
496                                    const GURL& url,
497                                    const std::string& extension_id,
498                                    const base::string16& title,
499                                    const std::string& icon_name,
500                                    const base::FilePath& profile_path,
501                                    const std::string& categories,
502                                    const std::string& mime_type,
503                                    bool no_display) {
504   base::CommandLine cmd_line = shell_integration::CommandLineArgsForLauncher(
505       url, extension_id, profile_path);
506   cmd_line.SetProgram(chrome_exe_path);
507   return GetDesktopFileContentsForCommand(cmd_line, app_name, url, title,
508                                           icon_name, categories, mime_type,
509                                           no_display);
510 }
511
512 std::string GetDesktopFileContentsForCommand(
513     const base::CommandLine& command_line,
514     const std::string& app_name,
515     const GURL& url,
516     const base::string16& title,
517     const std::string& icon_name,
518     const std::string& categories,
519     const std::string& mime_type,
520     bool no_display) {
521 #if defined(USE_GLIB)
522   // Although not required by the spec, Nautilus on Ubuntu Karmic creates its
523   // launchers with an xdg-open shebang. Follow that convention.
524   std::string output_buffer = std::string(kXdgOpenShebang) + "\n";
525
526   // See http://standards.freedesktop.org/desktop-entry-spec/latest/
527   GKeyFile* key_file = g_key_file_new();
528
529   // Set keys with fixed values.
530   g_key_file_set_string(key_file, kDesktopEntry, "Version", "1.0");
531   g_key_file_set_string(key_file, kDesktopEntry, "Terminal", "false");
532   g_key_file_set_string(key_file, kDesktopEntry, "Type", "Application");
533
534   // Set the "Name" key.
535   std::string final_title = base::UTF16ToUTF8(title);
536   // Make sure no endline characters can slip in and possibly introduce
537   // additional lines (like Exec, which makes it a security risk). Also
538   // use the URL as a default when the title is empty.
539   if (final_title.empty() ||
540       final_title.find("\n") != std::string::npos ||
541       final_title.find("\r") != std::string::npos) {
542     final_title = url.spec();
543   }
544   g_key_file_set_string(key_file, kDesktopEntry, "Name", final_title.c_str());
545
546   base::CommandLine modified_command_line(command_line);
547
548   // Set the "MimeType" key.
549   if (!mime_type.empty() && mime_type.find("\n") == std::string::npos &&
550       mime_type.find("\r") == std::string::npos) {
551     g_key_file_set_string(key_file, kDesktopEntry, "MimeType",
552                           mime_type.c_str());
553
554     // Some Linux Desktop Environments don't show file handlers unless they
555     // specify where to place file arguments.
556     // Note: We only include this parameter if the application is actually able
557     // to handle files, to prevent it showing up in the list of all applications
558     // which can handle files.
559     modified_command_line.AppendArg("%F");
560   }
561
562   // Set the "Exec" key.
563   std::string final_path =
564       QuoteCommandLineForDesktopFileExec(modified_command_line);
565   g_key_file_set_string(key_file, kDesktopEntry, "Exec", final_path.c_str());
566
567   // Set the "Icon" key.
568   if (!icon_name.empty()) {
569     g_key_file_set_string(key_file, kDesktopEntry, "Icon", icon_name.c_str());
570   } else {
571     g_key_file_set_string(key_file, kDesktopEntry, "Icon",
572                           GetIconName().c_str());
573   }
574
575   // Set the "Categories" key.
576   if (!categories.empty()) {
577     g_key_file_set_string(
578         key_file, kDesktopEntry, "Categories", categories.c_str());
579   }
580
581   // Set the "NoDisplay" key.
582   if (no_display)
583     g_key_file_set_string(key_file, kDesktopEntry, "NoDisplay", "true");
584
585   std::string wmclass = GetWMClassFromAppName(app_name);
586   g_key_file_set_string(key_file, kDesktopEntry, "StartupWMClass",
587                         wmclass.c_str());
588
589   gsize length = 0;
590   gchar* data_dump = g_key_file_to_data(key_file, &length, NULL);
591   if (data_dump) {
592     // If strlen(data_dump[0]) == 0, this check will fail.
593     if (data_dump[0] == '\n') {
594       // Older versions of glib produce a leading newline. If this is the case,
595       // remove it to avoid double-newline after the shebang.
596       output_buffer += (data_dump + 1);
597     } else {
598       output_buffer += data_dump;
599     }
600     g_free(data_dump);
601   }
602
603   g_key_file_free(key_file);
604   return output_buffer;
605 #else
606   NOTIMPLEMENTED();
607   return std::string();
608 #endif
609 }
610
611 std::string GetDirectoryFileContents(const base::string16& title,
612                                      const std::string& icon_name) {
613 #if defined(USE_GLIB)
614   // See http://standards.freedesktop.org/desktop-entry-spec/latest/
615   GKeyFile* key_file = g_key_file_new();
616
617   g_key_file_set_string(key_file, kDesktopEntry, "Version", "1.0");
618   g_key_file_set_string(key_file, kDesktopEntry, "Type", "Directory");
619   std::string final_title = base::UTF16ToUTF8(title);
620   g_key_file_set_string(key_file, kDesktopEntry, "Name", final_title.c_str());
621   if (!icon_name.empty()) {
622     g_key_file_set_string(key_file, kDesktopEntry, "Icon", icon_name.c_str());
623   } else {
624     g_key_file_set_string(key_file, kDesktopEntry, "Icon",
625                           GetIconName().c_str());
626   }
627
628   gsize length = 0;
629   gchar* data_dump = g_key_file_to_data(key_file, &length, NULL);
630   std::string output_buffer;
631   if (data_dump) {
632     // If strlen(data_dump[0]) == 0, this check will fail.
633     if (data_dump[0] == '\n') {
634       // Older versions of glib produce a leading newline. If this is the case,
635       // remove it to avoid double-newline after the shebang.
636       output_buffer += (data_dump + 1);
637     } else {
638       output_buffer += data_dump;
639     }
640     g_free(data_dump);
641   }
642
643   g_key_file_free(key_file);
644   return output_buffer;
645 #else
646   NOTIMPLEMENTED();
647   return std::string();
648 #endif
649 }
650
651 base::FilePath GetMimeTypesRegistrationFilename(
652     const base::FilePath& profile_path,
653     const web_app::AppId& app_id) {
654   DCHECK(!profile_path.empty() && !app_id.empty());
655
656   // Use a prefix to clearly group files created by Chrome.
657   std::string filename = base::StringPrintf(
658       "%s-%s-%s%s", chrome::kBrowserProcessExecutableName, app_id.c_str(),
659       profile_path.BaseName().value().c_str(), ".xml");
660
661   // Replace illegal characters and spaces in |filename|.
662   base::i18n::ReplaceIllegalCharactersInPath(&filename, '_');
663   base::ReplaceChars(filename, " ", "_", &filename);
664
665   return base::FilePath(filename);
666 }
667
668 std::string GetMimeTypesRegistrationFileContents(
669     const apps::FileHandlers& file_handlers) {
670   std::stringstream ss;
671   ss << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
672         "<mime-info "
673         "xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">\n";
674
675   for (const auto& file_handler : file_handlers) {
676     for (const auto& accept_entry : file_handler.accept) {
677       ss << "  <mime-type type=\"" << accept_entry.mime_type + "\">\n";
678       for (const auto& file_extension : accept_entry.file_extensions)
679         ss << "    <glob pattern=\"*" << file_extension << "\"/>\n";
680       ss << "  </mime-type>\n";
681     }
682   }
683
684   ss << "</mime-info>\n";
685   return ss.str();
686 }
687
688 }  // namespace shell_integration_linux
689
690 namespace shell_integration {
691
692 bool SetAsDefaultBrowser() {
693   return shell_integration_linux::SetDefaultWebClient(std::string());
694 }
695
696 bool SetAsDefaultProtocolClient(const std::string& protocol) {
697   return shell_integration_linux::SetDefaultWebClient(protocol);
698 }
699
700 DefaultWebClientSetPermission GetDefaultWebClientSetPermission() {
701   return SET_DEFAULT_UNATTENDED;
702 }
703
704 base::string16 GetApplicationNameForProtocol(const GURL& url) {
705   return base::ASCIIToUTF16("xdg-open");
706 }
707
708 DefaultWebClientState GetDefaultBrowser() {
709   return shell_integration_linux::GetIsDefaultWebClient(std::string());
710 }
711
712 bool IsFirefoxDefaultBrowser() {
713   std::vector<std::string> argv;
714   argv.push_back(shell_integration_linux::kXdgSettings);
715   argv.push_back("get");
716   argv.push_back(shell_integration_linux::kXdgSettingsDefaultBrowser);
717
718   std::string browser;
719   // We don't care about the return value here.
720   base::GetAppOutput(base::CommandLine(argv), &browser);
721   return browser.find("irefox") != std::string::npos;
722 }
723
724 DefaultWebClientState IsDefaultProtocolClient(const std::string& protocol) {
725   return shell_integration_linux::GetIsDefaultWebClient(protocol);
726 }
727
728 }  // namespace shell_integration