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

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: 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 that we certainly don't want.
Evan Martin 2011/05/11 21:29:06 Can you make this comment describe what this is?
208 // construct in a desktop file. (Note that for Exec= lines this 209 const char* kDesktopKeysToDelete[] = {
209 // should be used in conjunction with QuoteArgForDesktopFileExec, 210 "GenericName",
210 // possibly escaping a backslash twice.) 211 "Comment",
211 std::string EscapeStringForDesktopFile(const std::string& arg) { 212 "MimeType",
212 // http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s03.html 213 "X-Ayatana-Desktop-Shortcuts",
213 if (arg.find('\\') == std::string::npos) 214 "StartupWMClass", // read only on TOOLKIT_GTK
Evan Martin 2011/05/11 21:29:06 what does this comment mean?
214 return arg; 215 NULL
216 };
215 217
216 std::string escaped; 218 const char* kDesktopEntry = "Desktop Entry";
217 for (size_t i = 0; i < arg.size(); ++i) { 219
218 if (arg[i] == '\\') 220 const char* kXdgOpenShebang = "#!/usr/bin/env xdg-open\n";
219 escaped += '\\';
220 escaped += arg[i];
221 }
222 return escaped;
223 }
224 221
225 } // namespace 222 } // namespace
226 223
227 // static 224 // static
228 std::string ShellIntegration::GetDesktopName(base::Environment* env) { 225 std::string ShellIntegration::GetDesktopName(base::Environment* env) {
229 #if defined(GOOGLE_CHROME_BUILD) 226 #if defined(GOOGLE_CHROME_BUILD)
230 return "google-chrome.desktop"; 227 return "google-chrome.desktop";
231 #else // CHROMIUM_BUILD 228 #else // CHROMIUM_BUILD
232 // Allow $CHROME_DESKTOP to override the built-in value, so that development 229 // Allow $CHROME_DESKTOP to override the built-in value, so that development
233 // versions can set themselves as the default without interfering with 230 // versions can set themselves as the default without interfering with
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after
361 } 358 }
362 359
363 // static 360 // static
364 std::string ShellIntegration::GetDesktopFileContents( 361 std::string ShellIntegration::GetDesktopFileContents(
365 const std::string& template_contents, 362 const std::string& template_contents,
366 const std::string& app_name, 363 const std::string& app_name,
367 const GURL& url, 364 const GURL& url,
368 const std::string& extension_id, 365 const std::string& extension_id,
369 const string16& title, 366 const string16& title,
370 const std::string& icon_name) { 367 const std::string& icon_name) {
368 if (template_contents.empty())
369 return kXdgOpenShebang;
370
371 // See http://standards.freedesktop.org/desktop-entry-spec/latest/ 371 // See http://standards.freedesktop.org/desktop-entry-spec/latest/
372 // Although not required by the spec, Nautilus on Ubuntu Karmic creates its 372 // http://developer.gnome.org/glib/unstable/glib-Key-value-file-parser.html
Evan Martin 2011/05/11 21:29:06 Oh wow, I had no idea this existed!
373 // launchers with an xdg-open shebang. Follow that convention. 373 GKeyFile* key_file = g_key_file_new();
374 std::string output_buffer("#!/usr/bin/env xdg-open\n"); 374 GError* err = NULL;
375 StringTokenizer tokenizer(template_contents, "\n"); 375 // Loading the data will strip translations and comments from the desktop
376 while (tokenizer.GetNext()) { 376 // file (which we want to do!)
377 if (tokenizer.token().substr(0, 5) == "Exec=") { 377 if (!g_key_file_load_from_data(
378 std::string exec_path = tokenizer.token().substr(5); 378 key_file,
379 StringTokenizer exec_tokenizer(exec_path, " "); 379 template_contents.c_str(),
380 std::string final_path; 380 template_contents.size(),
381 while (exec_tokenizer.GetNext() && exec_tokenizer.token() != "%U") { 381 G_KEY_FILE_NONE,
382 if (!final_path.empty()) 382 &err)) {
383 final_path += " "; 383 NOTREACHED() << "Unable to read desktop file template:" << err->message;
384 final_path += exec_tokenizer.token(); 384 g_error_free(err);
385 } 385 return kXdgOpenShebang;
386 CommandLine cmd_line = 386 }
387 ShellIntegration::CommandLineArgsForLauncher(url, extension_id); 387
388 const CommandLine::SwitchMap& switch_map = cmd_line.GetSwitches(); 388 // Remove all sections except for the Desktop Entry
389 for (CommandLine::SwitchMap::const_iterator i = switch_map.begin(); 389 gsize length = 0;
390 i != switch_map.end(); ++i) { 390 gchar** groups = g_key_file_get_groups(key_file, &length);
391 if (i->second.empty()) { 391 for (gsize i = 0; i < length; ++i) {
392 final_path += " --" + i->first; 392 if (strcmp(groups[i], kDesktopEntry) != 0) {
393 } else { 393 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 } 394 }
427 } 395 }
396 g_strfreev(groups);
397
398 // Remove keys that we won't need.
399 for (const char** current_key = kDesktopKeysToDelete; *current_key;
400 ++current_key) {
401 g_key_file_remove_key(key_file, kDesktopEntry, *current_key, NULL);
402 }
403
404 // Set the "Name" key.
405 std::string final_title = UTF16ToUTF8(title);
406 // Make sure no endline characters can slip in and possibly introduce
407 // additional lines (like Exec, which makes it a security risk). Also
408 // use the URL as a default when the title is empty.
409 if (final_title.empty() ||
410 final_title.find("\n") != std::string::npos ||
411 final_title.find("\r") != std::string::npos) {
412 final_title = url.spec();
Evan Martin 2011/05/11 21:29:06 Is it possible that url.spec() has these bad chara
Elliot Glaysher 2011/05/11 21:52:29 No, but it would if we called invalid_spec(). On d
413 }
414 g_key_file_set_string(key_file, kDesktopEntry, "Name", final_title.c_str());
415
416 // Set the "Exec" key.
417 char* exec_c_string = g_key_file_get_string(key_file, kDesktopEntry, "Exec",
418 NULL);
419 if (exec_c_string) {
420 std::string exec_string(exec_c_string);
421 g_free(exec_c_string);
422 StringTokenizer exec_tokenizer(exec_string, " ");
423
424 std::string final_path;
425 while (exec_tokenizer.GetNext() && exec_tokenizer.token() != "%U") {
426 if (!final_path.empty())
427 final_path += " ";
428 final_path += exec_tokenizer.token();
429 }
430 CommandLine cmd_line =
431 ShellIntegration::CommandLineArgsForLauncher(url, extension_id);
432 const CommandLine::SwitchMap& switch_map = cmd_line.GetSwitches();
433 for (CommandLine::SwitchMap::const_iterator i = switch_map.begin();
434 i != switch_map.end(); ++i) {
435 if (i->second.empty()) {
436 final_path += " --" + i->first;
437 } else {
438 final_path += " " + QuoteArgForDesktopFileExec("--" + i->first +
439 "=" + i->second);
440 }
441 }
442
443 g_key_file_set_string(key_file, kDesktopEntry, "Exec", final_path.c_str());
Elliot Glaysher 2011/05/11 01:07:27 This level of quoting is done by the library. (We
Evan Martin 2011/05/11 21:29:06 Are the unit tests correct? Better to be correct
Elliot Glaysher 2011/05/11 21:52:29 Yes. It's just a weird artifact of where gkeyparse
444 }
445
446 // Set the "Icon" key.
447 if (!icon_name.empty())
448 g_key_file_set_string(key_file, kDesktopEntry, "Icon", icon_name.c_str());
428 449
429 #if defined(TOOLKIT_USES_GTK) 450 #if defined(TOOLKIT_USES_GTK)
430 std::string wmclass = web_app::GetWMClassFromAppName(app_name); 451 std::string wmclass = web_app::GetWMClassFromAppName(app_name);
431 if (!wmclass.empty()) { 452 g_key_file_set_string(key_file, kDesktopEntry, "StartupWMClass",
432 output_buffer += StringPrintf("StartupWMClass=%s\n", wmclass.c_str()); 453 wmclass.c_str());
433 }
434 #endif 454 #endif
435 455
456 // Although not required by the spec, Nautilus on Ubuntu Karmic creates its
457 // launchers with an xdg-open shebang. Follow that convention.
458 std::string output_buffer("#!/usr/bin/env xdg-open");
Evan Martin 2011/05/11 21:29:06 did you intend to use your kShebang constant?
Elliot Glaysher 2011/05/11 21:52:29 No, because that constant has a newline. Updated t
459 length = 0;
460 gchar* data_dump = g_key_file_to_data(key_file, &length, NULL);
461 if (data_dump) {
462 output_buffer += data_dump;
463 g_free(data_dump);
464 }
465
466 g_key_file_free(key_file);
436 return output_buffer; 467 return output_buffer;
437 } 468 }
438 469
439 // static 470 // static
440 void ShellIntegration::CreateDesktopShortcut( 471 void ShellIntegration::CreateDesktopShortcut(
441 const ShortcutInfo& shortcut_info, const std::string& shortcut_template) { 472 const ShortcutInfo& shortcut_info, const std::string& shortcut_template) {
442 // TODO(phajdan.jr): Report errors from this function, possibly as infobars. 473 // TODO(phajdan.jr): Report errors from this function, possibly as infobars.
443 474
444 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 475 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
445 476
(...skipping 12 matching lines...) Expand all
458 shortcut_info.extension_id, 489 shortcut_info.extension_id,
459 shortcut_info.title, 490 shortcut_info.title,
460 icon_name); 491 icon_name);
461 492
462 if (shortcut_info.create_on_desktop) 493 if (shortcut_info.create_on_desktop)
463 CreateShortcutOnDesktop(shortcut_filename, contents); 494 CreateShortcutOnDesktop(shortcut_filename, contents);
464 495
465 if (shortcut_info.create_in_applications_menu) 496 if (shortcut_info.create_in_applications_menu)
466 CreateShortcutInApplicationsMenu(shortcut_filename, contents); 497 CreateShortcutInApplicationsMenu(shortcut_filename, contents);
467 } 498 }
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