• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/shell_integration.h"
6 
7 #include <map>
8 
9 #include "base/file_path.h"
10 #include "base/file_util.h"
11 #include "base/memory/scoped_temp_dir.h"
12 #include "base/message_loop.h"
13 #include "base/stl_util-inl.h"
14 #include "base/string_util.h"
15 #include "base/utf_string_conversions.h"
16 #include "chrome/browser/web_applications/web_app.h"
17 #include "chrome/common/chrome_constants.h"
18 #include "chrome/common/chrome_paths_internal.h"
19 #include "content/browser/browser_thread.h"
20 #include "googleurl/src/gurl.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 
23 #if defined(OS_WIN)
24 #include "chrome/installer/util/browser_distribution.h"
25 #elif defined(OS_LINUX)
26 #include "base/environment.h"
27 #endif  // defined(OS_LINUX)
28 
29 #define FPL FILE_PATH_LITERAL
30 
31 #if defined(OS_LINUX)
32 namespace {
33 
34 // Provides mock environment variables values based on a stored map.
35 class MockEnvironment : public base::Environment {
36  public:
MockEnvironment()37   MockEnvironment() {}
38 
Set(const std::string & name,const std::string & value)39   void Set(const std::string& name, const std::string& value) {
40     variables_[name] = value;
41   }
42 
GetVar(const char * variable_name,std::string * result)43   virtual bool GetVar(const char* variable_name, std::string* result) {
44     if (ContainsKey(variables_, variable_name)) {
45       *result = variables_[variable_name];
46       return true;
47     }
48 
49     return false;
50   }
51 
SetVar(const char * variable_name,const std::string & new_value)52   virtual bool SetVar(const char* variable_name, const std::string& new_value) {
53     ADD_FAILURE();
54     return false;
55   }
56 
UnSetVar(const char * variable_name)57   virtual bool UnSetVar(const char* variable_name) {
58     ADD_FAILURE();
59     return false;
60   }
61 
62  private:
63   std::map<std::string, std::string> variables_;
64 
65   DISALLOW_COPY_AND_ASSIGN(MockEnvironment);
66 };
67 
68 }  // namespace
69 
TEST(ShellIntegrationTest,GetDesktopShortcutTemplate)70 TEST(ShellIntegrationTest, GetDesktopShortcutTemplate) {
71 #if defined(GOOGLE_CHROME_BUILD)
72   const char kTemplateFilename[] = "google-chrome.desktop";
73 #else  // CHROMIUM_BUILD
74   const char kTemplateFilename[] = "chromium-browser.desktop";
75 #endif
76 
77   const char kTestData1[] = "a magical testing string";
78   const char kTestData2[] = "a different testing string";
79 
80   MessageLoop message_loop;
81   BrowserThread file_thread(BrowserThread::FILE, &message_loop);
82 
83   {
84     ScopedTempDir temp_dir;
85     ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
86 
87     MockEnvironment env;
88     env.Set("XDG_DATA_HOME", temp_dir.path().value());
89     ASSERT_TRUE(file_util::WriteFile(
90         temp_dir.path().AppendASCII(kTemplateFilename),
91         kTestData1, strlen(kTestData1)));
92     std::string contents;
93     ASSERT_TRUE(ShellIntegration::GetDesktopShortcutTemplate(&env,
94                                                              &contents));
95     EXPECT_EQ(kTestData1, contents);
96   }
97 
98   {
99     ScopedTempDir temp_dir;
100     ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
101 
102     MockEnvironment env;
103     env.Set("XDG_DATA_DIRS", temp_dir.path().value());
104     ASSERT_TRUE(file_util::CreateDirectory(
105         temp_dir.path().AppendASCII("applications")));
106     ASSERT_TRUE(file_util::WriteFile(
107         temp_dir.path().AppendASCII("applications")
108             .AppendASCII(kTemplateFilename),
109         kTestData2, strlen(kTestData2)));
110     std::string contents;
111     ASSERT_TRUE(ShellIntegration::GetDesktopShortcutTemplate(&env,
112                                                              &contents));
113     EXPECT_EQ(kTestData2, contents);
114   }
115 
116   {
117     ScopedTempDir temp_dir;
118     ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
119 
120     MockEnvironment env;
121     env.Set("XDG_DATA_DIRS", temp_dir.path().value() + ":" +
122                    temp_dir.path().AppendASCII("applications").value());
123     ASSERT_TRUE(file_util::CreateDirectory(
124         temp_dir.path().AppendASCII("applications")));
125     ASSERT_TRUE(file_util::WriteFile(
126         temp_dir.path().AppendASCII(kTemplateFilename),
127         kTestData1, strlen(kTestData1)));
128     ASSERT_TRUE(file_util::WriteFile(
129         temp_dir.path().AppendASCII("applications")
130             .AppendASCII(kTemplateFilename),
131         kTestData2, strlen(kTestData2)));
132     std::string contents;
133     ASSERT_TRUE(ShellIntegration::GetDesktopShortcutTemplate(&env,
134                                                              &contents));
135     EXPECT_EQ(kTestData1, contents);
136   }
137 }
138 
TEST(ShellIntegrationTest,GetDesktopShortcutFilename)139 TEST(ShellIntegrationTest, GetDesktopShortcutFilename) {
140   const struct {
141     const FilePath::CharType* path;
142     const char* url;
143   } test_cases[] = {
144     { FPL("http___foo_.desktop"), "http://foo" },
145     { FPL("http___foo_bar_.desktop"), "http://foo/bar/" },
146     { FPL("http___foo_bar_a=b&c=d.desktop"), "http://foo/bar?a=b&c=d" },
147 
148     // Now we're starting to be more evil...
149     { FPL("http___foo_.desktop"), "http://foo/bar/baz/../../../../../" },
150     { FPL("http___foo_.desktop"), "http://foo/bar/././../baz/././../" },
151     { FPL("http___.._.desktop"), "http://../../../../" },
152   };
153   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); i++) {
154     EXPECT_EQ(std::string(chrome::kBrowserProcessExecutableName) + "-" +
155               test_cases[i].path,
156               ShellIntegration::GetDesktopShortcutFilename(
157                   GURL(test_cases[i].url)).value()) <<
158         " while testing " << test_cases[i].url;
159   }
160 }
161 
TEST(ShellIntegrationTest,GetDesktopFileContents)162 TEST(ShellIntegrationTest, GetDesktopFileContents) {
163   const struct {
164     const char* url;
165     const char* title;
166     const char* icon_name;
167     const char* template_contents;
168     const char* expected_output;
169   } test_cases[] = {
170     // Dumb case.
171     { "ignored", "ignored", "ignored", "", "#!/usr/bin/env xdg-open\n" },
172 
173     // Real-world case.
174     { "http://gmail.com",
175       "GMail",
176       "chrome-http__gmail.com",
177 
178       "[Desktop Entry]\n"
179       "Version=1.0\n"
180       "Encoding=UTF-8\n"
181       "Name=Google Chrome\n"
182       "Comment=The web browser from Google\n"
183       "Exec=/opt/google/chrome/google-chrome %U\n"
184       "Terminal=false\n"
185       "Icon=/opt/google/chrome/product_logo_48.png\n"
186       "Type=Application\n"
187       "Categories=Application;Network;WebBrowser;\n"
188       "MimeType=text/html;text/xml;application/xhtml_xml;\n",
189 
190       "#!/usr/bin/env xdg-open\n"
191       "[Desktop Entry]\n"
192       "Version=1.0\n"
193       "Encoding=UTF-8\n"
194       "Name=GMail\n"
195       "Exec=/opt/google/chrome/google-chrome --app=http://gmail.com/\n"
196       "Terminal=false\n"
197       "Icon=chrome-http__gmail.com\n"
198       "Type=Application\n"
199       "Categories=Application;Network;WebBrowser;\n"
200       "StartupWMClass=gmail.com\n"
201     },
202 
203     // Make sure we don't insert duplicate shebangs.
204     { "http://gmail.com",
205       "GMail",
206       "chrome-http__gmail.com",
207 
208       "#!/some/shebang\n"
209       "Name=Google Chrome\n"
210       "Exec=/opt/google/chrome/google-chrome %U\n",
211 
212       "#!/usr/bin/env xdg-open\n"
213       "Name=GMail\n"
214       "Exec=/opt/google/chrome/google-chrome --app=http://gmail.com/\n"
215       "StartupWMClass=gmail.com\n"
216     },
217 
218     // Make sure i18n-ed comments are removed.
219     { "http://gmail.com",
220       "GMail",
221       "chrome-http__gmail.com",
222 
223       "Name=Google Chrome\n"
224       "Exec=/opt/google/chrome/google-chrome %U\n"
225       "Comment[pl]=Jakis komentarz.\n",
226 
227       "#!/usr/bin/env xdg-open\n"
228       "Name=GMail\n"
229       "Exec=/opt/google/chrome/google-chrome --app=http://gmail.com/\n"
230       "StartupWMClass=gmail.com\n"
231     },
232 
233     // Make sure that empty icons are replaced by the chrome icon.
234     { "http://gmail.com",
235       "GMail",
236       "",
237 
238       "Name=Google Chrome\n"
239       "Exec=/opt/google/chrome/google-chrome %U\n"
240       "Comment[pl]=Jakis komentarz.\n"
241       "Icon=/opt/google/chrome/product_logo_48.png\n",
242 
243       "#!/usr/bin/env xdg-open\n"
244       "Name=GMail\n"
245       "Exec=/opt/google/chrome/google-chrome --app=http://gmail.com/\n"
246       "Icon=/opt/google/chrome/product_logo_48.png\n"
247       "StartupWMClass=gmail.com\n"
248     },
249 
250     // Now we're starting to be more evil...
251     { "http://evil.com/evil --join-the-b0tnet",
252       "Ownz0red\nExec=rm -rf /",
253       "chrome-http__evil.com_evil",
254 
255       "Name=Google Chrome\n"
256       "Exec=/opt/google/chrome/google-chrome %U\n",
257 
258       "#!/usr/bin/env xdg-open\n"
259       "Name=http://evil.com/evil%20--join-the-b0tnet\n"
260       "Exec=/opt/google/chrome/google-chrome "
261       "--app=http://evil.com/evil%20--join-the-b0tnet\n"
262       "StartupWMClass=evil.com__evil%20--join-the-b0tnet\n"
263     },
264     { "http://evil.com/evil; rm -rf /; \"; rm -rf $HOME >ownz0red",
265       "Innocent Title",
266       "chrome-http__evil.com_evil",
267 
268       "Name=Google Chrome\n"
269       "Exec=/opt/google/chrome/google-chrome %U\n",
270 
271       "#!/usr/bin/env xdg-open\n"
272       "Name=Innocent Title\n"
273       "Exec=/opt/google/chrome/google-chrome "
274       "\"--app=http://evil.com/evil;%20rm%20-rf%20/;%20%22;%20rm%20"
275       // Note: $ is escaped as \$ within an arg to Exec, and then
276       // the \ is escaped as \\ as all strings in a Desktop file should
277       // be; finally, \\ becomes \\\\ when represented in a C++ string!
278       "-rf%20\\\\$HOME%20%3Eownz0red\"\n"
279       "StartupWMClass=evil.com__evil;%20rm%20-rf%20_;%20%22;%20"
280       "rm%20-rf%20$HOME%20%3Eownz0red\n"
281     },
282     { "http://evil.com/evil | cat `echo ownz0red` >/dev/null",
283       "Innocent Title",
284       "chrome-http__evil.com_evil",
285 
286       "Name=Google Chrome\n"
287       "Exec=/opt/google/chrome/google-chrome %U\n",
288 
289       "#!/usr/bin/env xdg-open\n"
290       "Name=Innocent Title\n"
291       "Exec=/opt/google/chrome/google-chrome "
292       "--app=http://evil.com/evil%20%7C%20cat%20%60echo%20ownz0red"
293       "%60%20%3E/dev/null\n"
294       "StartupWMClass=evil.com__evil%20%7C%20cat%20%60echo%20ownz0red"
295       "%60%20%3E_dev_null\n"
296     },
297   };
298   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); i++) {
299     SCOPED_TRACE(i);
300     EXPECT_EQ(
301         test_cases[i].expected_output,
302         ShellIntegration::GetDesktopFileContents(
303             test_cases[i].template_contents,
304             web_app::GenerateApplicationNameFromURL(GURL(test_cases[i].url)),
305             GURL(test_cases[i].url),
306             "",
307             ASCIIToUTF16(test_cases[i].title),
308             test_cases[i].icon_name));
309   }
310 }
311 #elif defined(OS_WIN)
TEST(ShellIntegrationTest,GetChromiumAppIdTest)312 TEST(ShellIntegrationTest, GetChromiumAppIdTest) {
313   // Empty profile path should get chrome::kBrowserAppID
314   FilePath empty_path;
315   EXPECT_EQ(BrowserDistribution::GetDistribution()->GetBrowserAppId(),
316             ShellIntegration::GetChromiumAppId(empty_path));
317 
318   // Default profile path should get chrome::kBrowserAppID
319   FilePath default_user_data_dir;
320   chrome::GetDefaultUserDataDirectory(&default_user_data_dir);
321   FilePath default_profile_path =
322       default_user_data_dir.AppendASCII(chrome::kNotSignedInProfile);
323   EXPECT_EQ(BrowserDistribution::GetDistribution()->GetBrowserAppId(),
324             ShellIntegration::GetChromiumAppId(default_profile_path));
325 
326   // Non-default profile path should get chrome::kBrowserAppID joined with
327   // profile info.
328   FilePath profile_path(FILE_PATH_LITERAL("root"));
329   profile_path = profile_path.Append(FILE_PATH_LITERAL("udd"));
330   profile_path = profile_path.Append(FILE_PATH_LITERAL("User Data - Test"));
331   EXPECT_EQ(BrowserDistribution::GetDistribution()->GetBrowserAppId() +
332             L".udd.UserDataTest",
333             ShellIntegration::GetChromiumAppId(profile_path));
334 }
335 #endif
336