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

Side by Side Diff: chrome/browser/shell_integration_linux.cc

Issue 7000018: GTK: Use glib's desktop file parser instead of our hand rolled one. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Updated comments Created 9 years, 7 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | chrome/browser/shell_integration_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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.h" 5 #include "chrome/browser/shell_integration.h"
6 6
7 #include <fcntl.h> 7 #include <fcntl.h>
8 #include <glib.h>
8 #include <stdlib.h> 9 #include <stdlib.h>
9 #include <sys/stat.h> 10 #include <sys/stat.h>
10 #include <sys/types.h> 11 #include <sys/types.h>
11 #include <unistd.h> 12 #include <unistd.h>
12 13
13 #include <string> 14 #include <string>
14 #include <vector> 15 #include <vector>
15 16
16 #include "base/command_line.h" 17 #include "base/command_line.h"
17 #include "base/eintr_wrapper.h" 18 #include "base/eintr_wrapper.h"
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after
197 quoted += '\\'; 198 quoted += '\\';
198 break; 199 break;
199 } 200 }
200 quoted += arg[i]; 201 quoted += arg[i];
201 } 202 }
202 quoted += '"'; 203 quoted += '"';
203 204
204 return quoted; 205 return quoted;
205 } 206 }
206 207
207 // Escape a string if needed for the right side of a Key=Value 208 // Remove keys from the [Desktop Entry] that would be wrong if copied verbatim
208 // construct in a desktop file. (Note that for Exec= lines this 209 // into the new .desktop file.
209 // should be used in conjunction with QuoteArgForDesktopFileExec, 210 const char* kDesktopKeysToDelete[] = {
210 // possibly escaping a backslash twice.) 211 "GenericName",
211 std::string EscapeStringForDesktopFile(const std::string& arg) { 212 "Comment",
212 // http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s03.html 213 "MimeType",
213 if (arg.find('\\') == std::string::npos) 214 "X-Ayatana-Desktop-Shortcuts",
214 return arg; 215 "StartupWMClass",
216 NULL
217 };
215 218
216 std::string escaped; 219 const char* kDesktopEntry = "Desktop Entry";
217 for (size_t i = 0; i < arg.size(); ++i) { 220
218 if (arg[i] == '\\') 221 const char* kXdgOpenShebang = "#!/usr/bin/env xdg-open";
219 escaped += '\\';
220 escaped += arg[i];
221 }
222 return escaped;
223 }
224 222
225 } // namespace 223 } // namespace
226 224
227 // static 225 // static
228 std::string ShellIntegration::GetDesktopName(base::Environment* env) { 226 std::string ShellIntegration::GetDesktopName(base::Environment* env) {
229 #if defined(GOOGLE_CHROME_BUILD) 227 #if defined(GOOGLE_CHROME_BUILD)
230 return "google-chrome.desktop"; 228 return "google-chrome.desktop";
231 #else // CHROMIUM_BUILD 229 #else // CHROMIUM_BUILD
232 // Allow $CHROME_DESKTOP to override the built-in value, so that development 230 // Allow $CHROME_DESKTOP to override the built-in value, so that development
233 // versions can set themselves as the default without interfering with 231 // versions can set themselves as the default without interfering with
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after
361 } 359 }
362 360
363 // static 361 // static
364 std::string ShellIntegration::GetDesktopFileContents( 362 std::string ShellIntegration::GetDesktopFileContents(
365 const std::string& template_contents, 363 const std::string& template_contents,
366 const std::string& app_name, 364 const std::string& app_name,
367 const GURL& url, 365 const GURL& url,
368 const std::string& extension_id, 366 const std::string& extension_id,
369 const string16& title, 367 const string16& title,
370 const std::string& icon_name) { 368 const std::string& icon_name) {
369 if (template_contents.empty())
370 return std::string(kXdgOpenShebang) + "\n";
371
371 // See http://standards.freedesktop.org/desktop-entry-spec/latest/ 372 // See http://standards.freedesktop.org/desktop-entry-spec/latest/
372 // Although not required by the spec, Nautilus on Ubuntu Karmic creates its 373 // http://developer.gnome.org/glib/unstable/glib-Key-value-file-parser.html
373 // launchers with an xdg-open shebang. Follow that convention. 374 GKeyFile* key_file = g_key_file_new();
374 std::string output_buffer("#!/usr/bin/env xdg-open\n"); 375 GError* err = NULL;
375 StringTokenizer tokenizer(template_contents, "\n"); 376 // Loading the data will strip translations and comments from the desktop
376 while (tokenizer.GetNext()) { 377 // file (which we want to do!)
377 if (tokenizer.token().substr(0, 5) == "Exec=") { 378 if (!g_key_file_load_from_data(
378 std::string exec_path = tokenizer.token().substr(5); 379 key_file,
379 StringTokenizer exec_tokenizer(exec_path, " "); 380 template_contents.c_str(),
380 std::string final_path; 381 template_contents.size(),
381 while (exec_tokenizer.GetNext() && exec_tokenizer.token() != "%U") { 382 G_KEY_FILE_NONE,
382 if (!final_path.empty()) 383 &err)) {
383 final_path += " "; 384 NOTREACHED() << "Unable to read desktop file template:" << err->message;
384 final_path += exec_tokenizer.token(); 385 g_error_free(err);
385 } 386 return std::string(kXdgOpenShebang) + "\n";
386 CommandLine cmd_line = 387 }
387 ShellIntegration::CommandLineArgsForLauncher(url, extension_id); 388
388 const CommandLine::SwitchMap& switch_map = cmd_line.GetSwitches(); 389 // Remove all sections except for the Desktop Entry
389 for (CommandLine::SwitchMap::const_iterator i = switch_map.begin(); 390 gsize length = 0;
390 i != switch_map.end(); ++i) { 391 gchar** groups = g_key_file_get_groups(key_file, &length);
391 if (i->second.empty()) { 392 for (gsize i = 0; i < length; ++i) {
392 final_path += " --" + i->first; 393 if (strcmp(groups[i], kDesktopEntry) != 0) {
393 } else { 394 g_key_file_remove_group(key_file, groups[i], NULL);
394 final_path += " " + QuoteArgForDesktopFileExec("--" + i->first +
395 "=" + i->second);
396 }
397 }
398 output_buffer += std::string("Exec=") +
399 EscapeStringForDesktopFile(final_path) + "\n";
400 } else if (tokenizer.token().substr(0, 5) == "Name=") {
401 std::string final_title = UTF16ToUTF8(title);
402 // Make sure no endline characters can slip in and possibly introduce
403 // additional lines (like Exec, which makes it a security risk). Also
404 // use the URL as a default when the title is empty.
405 if (final_title.empty() ||
406 final_title.find("\n") != std::string::npos ||
407 final_title.find("\r") != std::string::npos) {
408 final_title = url.spec();
409 }
410 output_buffer += StringPrintf("Name=%s\n", final_title.c_str());
411 } else if (tokenizer.token().substr(0, 11) == "GenericName" ||
412 tokenizer.token().substr(0, 7) == "Comment" ||
413 tokenizer.token().substr(0, 1) == "#") {
414 // Skip comment lines.
415 } else if (tokenizer.token().substr(0, 9) == "MimeType=") {
416 // Skip MimeType lines, they are only relevant for a web browser
417 // shortcut, not a web application shortcut.
418 } else if (tokenizer.token().substr(0, 15) == "StartupWMClass=") {
419 // Skip StartupWMClass; it will certainly be wrong since we emit a
420 // different one based on the app name below.
421 } else if (tokenizer.token().substr(0, 5) == "Icon=" &&
422 !icon_name.empty()) {
423 output_buffer += StringPrintf("Icon=%s\n", icon_name.c_str());
424 } else {
425 output_buffer += tokenizer.token() + "\n";
426 } 395 }
427 } 396 }
397 g_strfreev(groups);
398
399 // Remove keys that we won't need.
400 for (const char** current_key = kDesktopKeysToDelete; *current_key;
401 ++current_key) {
402 g_key_file_remove_key(key_file, kDesktopEntry, *current_key, NULL);
403 }
404
405 // Set the "Name" key.
406 std::string final_title = UTF16ToUTF8(title);
407 // Make sure no endline characters can slip in and possibly introduce
408 // additional lines (like Exec, which makes it a security risk). Also
409 // use the URL as a default when the title is empty.
410 if (final_title.empty() ||
411 final_title.find("\n") != std::string::npos ||
412 final_title.find("\r") != std::string::npos) {
413 final_title = url.spec();
414 }
415 g_key_file_set_string(key_file, kDesktopEntry, "Name", final_title.c_str());
416
417 // Set the "Exec" key.
418 char* exec_c_string = g_key_file_get_string(key_file, kDesktopEntry, "Exec",
419 NULL);
420 if (exec_c_string) {
421 std::string exec_string(exec_c_string);
422 g_free(exec_c_string);
423 StringTokenizer exec_tokenizer(exec_string, " ");
424
425 std::string final_path;
426 while (exec_tokenizer.GetNext() && exec_tokenizer.token() != "%U") {
427 if (!final_path.empty())
428 final_path += " ";
429 final_path += exec_tokenizer.token();
430 }
431 CommandLine cmd_line =
432 ShellIntegration::CommandLineArgsForLauncher(url, extension_id);
433 const CommandLine::SwitchMap& switch_map = cmd_line.GetSwitches();
434 for (CommandLine::SwitchMap::const_iterator i = switch_map.begin();
435 i != switch_map.end(); ++i) {
436 if (i->second.empty()) {
437 final_path += " --" + i->first;
438 } else {
439 final_path += " " + QuoteArgForDesktopFileExec("--" + i->first +
440 "=" + i->second);
441 }
442 }
443
444 g_key_file_set_string(key_file, kDesktopEntry, "Exec", final_path.c_str());
445 }
446
447 // Set the "Icon" key.
448 if (!icon_name.empty())
449 g_key_file_set_string(key_file, kDesktopEntry, "Icon", icon_name.c_str());
428 450
429 #if defined(TOOLKIT_USES_GTK) 451 #if defined(TOOLKIT_USES_GTK)
430 std::string wmclass = web_app::GetWMClassFromAppName(app_name); 452 std::string wmclass = web_app::GetWMClassFromAppName(app_name);
431 if (!wmclass.empty()) { 453 g_key_file_set_string(key_file, kDesktopEntry, "StartupWMClass",
432 output_buffer += StringPrintf("StartupWMClass=%s\n", wmclass.c_str()); 454 wmclass.c_str());
433 }
434 #endif 455 #endif
435 456
457 // Although not required by the spec, Nautilus on Ubuntu Karmic creates its
458 // launchers with an xdg-open shebang. Follow that convention.
459 std::string output_buffer = kXdgOpenShebang;
Evan Martin 2011/05/11 21:54:15 Do you need a newline here? I guess the tests mus
460 length = 0;
461 gchar* data_dump = g_key_file_to_data(key_file, &length, NULL);
462 if (data_dump) {
463 output_buffer += data_dump;
464 g_free(data_dump);
465 }
466
467 g_key_file_free(key_file);
436 return output_buffer; 468 return output_buffer;
437 } 469 }
438 470
439 // static 471 // static
440 void ShellIntegration::CreateDesktopShortcut( 472 void ShellIntegration::CreateDesktopShortcut(
441 const ShortcutInfo& shortcut_info, const std::string& shortcut_template) { 473 const ShortcutInfo& shortcut_info, const std::string& shortcut_template) {
442 // TODO(phajdan.jr): Report errors from this function, possibly as infobars. 474 // TODO(phajdan.jr): Report errors from this function, possibly as infobars.
443 475
444 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 476 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
445 477
(...skipping 12 matching lines...) Expand all
458 shortcut_info.extension_id, 490 shortcut_info.extension_id,
459 shortcut_info.title, 491 shortcut_info.title,
460 icon_name); 492 icon_name);
461 493
462 if (shortcut_info.create_on_desktop) 494 if (shortcut_info.create_on_desktop)
463 CreateShortcutOnDesktop(shortcut_filename, contents); 495 CreateShortcutOnDesktop(shortcut_filename, contents);
464 496
465 if (shortcut_info.create_in_applications_menu) 497 if (shortcut_info.create_in_applications_menu)
466 CreateShortcutInApplicationsMenu(shortcut_filename, contents); 498 CreateShortcutInApplicationsMenu(shortcut_filename, contents);
467 } 499 }
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/shell_integration_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698