• 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/common/extensions/extension.h"
6 
7 #if defined(TOOLKIT_GTK)
8 #include <gtk/gtk.h>
9 #endif
10 
11 #include "base/format_macros.h"
12 #include "base/file_path.h"
13 #include "base/file_util.h"
14 #include "base/i18n/rtl.h"
15 #include "base/path_service.h"
16 #include "base/string_number_conversions.h"
17 #include "base/string_util.h"
18 #include "base/utf_string_conversions.h"
19 #include "chrome/common/chrome_paths.h"
20 #include "chrome/common/extensions/extension_action.h"
21 #include "chrome/common/extensions/extension_constants.h"
22 #include "chrome/common/extensions/extension_error_utils.h"
23 #include "chrome/common/extensions/extension_resource.h"
24 #include "chrome/common/url_constants.h"
25 #include "content/common/json_value_serializer.h"
26 #include "googleurl/src/gurl.h"
27 #include "net/base/mime_sniffer.h"
28 #include "skia/ext/image_operations.h"
29 #include "chrome/test/ui_test_utils.h"
30 #include "net/base/mock_host_resolver.h"
31 #include "testing/gtest/include/gtest/gtest.h"
32 #include "third_party/skia/include/core/SkBitmap.h"
33 #include "ui/base/l10n/l10n_util.h"
34 #include "ui/gfx/codec/png_codec.h"
35 
36 namespace keys = extension_manifest_keys;
37 namespace values = extension_manifest_values;
38 namespace errors = extension_manifest_errors;
39 
40 namespace {
41 
CompareLists(const std::vector<std::string> & expected,const std::vector<std::string> & actual)42 void CompareLists(const std::vector<std::string>& expected,
43                   const std::vector<std::string>& actual) {
44   ASSERT_EQ(expected.size(), actual.size());
45 
46   for (size_t i = 0; i < expected.size(); ++i) {
47     EXPECT_EQ(expected[i], actual[i]);
48   }
49 }
50 
AddPattern(ExtensionExtent * extent,const std::string & pattern)51 static void AddPattern(ExtensionExtent* extent, const std::string& pattern) {
52   int schemes = URLPattern::SCHEME_ALL;
53   extent->AddPattern(URLPattern(schemes, pattern));
54 }
55 
56 }
57 
58 class ExtensionTest : public testing::Test {
59 };
60 
61 // We persist location values in the preferences, so this is a sanity test that
62 // someone doesn't accidentally change them.
TEST(ExtensionTest,LocationValuesTest)63 TEST(ExtensionTest, LocationValuesTest) {
64   ASSERT_EQ(0, Extension::INVALID);
65   ASSERT_EQ(1, Extension::INTERNAL);
66   ASSERT_EQ(2, Extension::EXTERNAL_PREF);
67   ASSERT_EQ(3, Extension::EXTERNAL_REGISTRY);
68   ASSERT_EQ(4, Extension::LOAD);
69   ASSERT_EQ(5, Extension::COMPONENT);
70   ASSERT_EQ(6, Extension::EXTERNAL_PREF_DOWNLOAD);
71   ASSERT_EQ(7, Extension::EXTERNAL_POLICY_DOWNLOAD);
72 }
73 
TEST(ExtensionTest,LocationPriorityTest)74 TEST(ExtensionTest, LocationPriorityTest) {
75   for (int i = 0; i < Extension::NUM_LOCATIONS; i++) {
76     Extension::Location loc = static_cast<Extension::Location>(i);
77 
78     // INVALID is not a valid location.
79     if (loc == Extension::INVALID)
80       continue;
81 
82     // Comparing a location that has no rank will hit a CHECK. Do a
83     // compare with every valid location, to be sure each one is covered.
84 
85     // Check that no install source can override a componenet extension.
86     ASSERT_EQ(Extension::COMPONENT,
87               Extension::GetHigherPriorityLocation(Extension::COMPONENT, loc));
88     ASSERT_EQ(Extension::COMPONENT,
89               Extension::GetHigherPriorityLocation(loc, Extension::COMPONENT));
90 
91     // Check that any source can override a user install. This might change
92     // in the future, in which case this test should be updated.
93     ASSERT_EQ(loc,
94               Extension::GetHigherPriorityLocation(Extension::INTERNAL, loc));
95     ASSERT_EQ(loc,
96               Extension::GetHigherPriorityLocation(loc, Extension::INTERNAL));
97   }
98 
99   // Check a few interesting cases that we know can happen:
100   ASSERT_EQ(Extension::EXTERNAL_POLICY_DOWNLOAD,
101             Extension::GetHigherPriorityLocation(
102                 Extension::EXTERNAL_POLICY_DOWNLOAD,
103                 Extension::EXTERNAL_PREF));
104 
105   ASSERT_EQ(Extension::EXTERNAL_PREF,
106             Extension::GetHigherPriorityLocation(
107                 Extension::INTERNAL,
108                 Extension::EXTERNAL_PREF));
109 }
110 
111 
112 // Please don't put any more manifest tests here!!
113 // Move them to extension_manifest_unittest.cc instead and make them use the
114 // more data-driven style there instead.
115 // Bug: http://crbug.com/38462
116 
117 
TEST(ExtensionTest,InitFromValueInvalid)118 TEST(ExtensionTest, InitFromValueInvalid) {
119 #if defined(OS_WIN)
120   FilePath path(FILE_PATH_LITERAL("c:\\foo"));
121 #elif defined(OS_POSIX)
122   FilePath path(FILE_PATH_LITERAL("/foo"));
123 #endif
124   scoped_refptr<Extension> extension_ptr(new Extension(path,
125                                                        Extension::INVALID));
126   Extension& extension = *extension_ptr;
127   int error_code = 0;
128   std::string error;
129 
130   // Start with a valid extension manifest
131   FilePath extensions_path;
132   ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &extensions_path));
133   extensions_path = extensions_path.AppendASCII("extensions")
134       .AppendASCII("good")
135       .AppendASCII("Extensions")
136       .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
137       .AppendASCII("1.0.0.0")
138       .Append(Extension::kManifestFilename);
139 
140   JSONFileValueSerializer serializer(extensions_path);
141   scoped_ptr<DictionaryValue> valid_value(
142       static_cast<DictionaryValue*>(serializer.Deserialize(&error_code,
143                                                            &error)));
144   EXPECT_EQ("", error);
145   EXPECT_EQ(0, error_code);
146   ASSERT_TRUE(valid_value.get());
147   ASSERT_TRUE(extension.InitFromValue(*valid_value, Extension::REQUIRE_KEY,
148                                       &error));
149   ASSERT_EQ("", error);
150   EXPECT_EQ("en_US", extension.default_locale());
151 
152   scoped_ptr<DictionaryValue> input_value;
153 
154   // Test missing and invalid versions
155   input_value.reset(valid_value->DeepCopy());
156   input_value->Remove(keys::kVersion, NULL);
157   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
158                                        &error));
159   EXPECT_EQ(errors::kInvalidVersion, error);
160 
161   input_value->SetInteger(keys::kVersion, 42);
162   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
163                                        &error));
164   EXPECT_EQ(errors::kInvalidVersion, error);
165 
166   // Test missing and invalid names.
167   input_value.reset(valid_value->DeepCopy());
168   input_value->Remove(keys::kName, NULL);
169   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
170                                        &error));
171   EXPECT_EQ(errors::kInvalidName, error);
172 
173   input_value->SetInteger(keys::kName, 42);
174   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
175                                        &error));
176   EXPECT_EQ(errors::kInvalidName, error);
177 
178   // Test invalid description
179   input_value.reset(valid_value->DeepCopy());
180   input_value->SetInteger(keys::kDescription, 42);
181   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
182                                        &error));
183   EXPECT_EQ(errors::kInvalidDescription, error);
184 
185   // Test invalid icons
186   input_value.reset(valid_value->DeepCopy());
187   input_value->SetInteger(keys::kIcons, 42);
188   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
189                                        &error));
190   EXPECT_EQ(errors::kInvalidIcons, error);
191 
192   // Test invalid icon paths
193   input_value.reset(valid_value->DeepCopy());
194   DictionaryValue* icons = NULL;
195   input_value->GetDictionary(keys::kIcons, &icons);
196   ASSERT_FALSE(NULL == icons);
197   icons->SetInteger(base::IntToString(128), 42);
198   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
199                                        &error));
200   EXPECT_TRUE(MatchPattern(error, errors::kInvalidIconPath));
201 
202   // Test invalid user scripts list
203   input_value.reset(valid_value->DeepCopy());
204   input_value->SetInteger(keys::kContentScripts, 42);
205   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
206                                        &error));
207   EXPECT_EQ(errors::kInvalidContentScriptsList, error);
208 
209   // Test invalid user script item
210   input_value.reset(valid_value->DeepCopy());
211   ListValue* content_scripts = NULL;
212   input_value->GetList(keys::kContentScripts, &content_scripts);
213   ASSERT_FALSE(NULL == content_scripts);
214   content_scripts->Set(0, Value::CreateIntegerValue(42));
215   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
216                                        &error));
217   EXPECT_TRUE(MatchPattern(error, errors::kInvalidContentScript));
218 
219   // Test missing and invalid matches array
220   input_value.reset(valid_value->DeepCopy());
221   input_value->GetList(keys::kContentScripts, &content_scripts);
222   DictionaryValue* user_script = NULL;
223   content_scripts->GetDictionary(0, &user_script);
224   user_script->Remove(keys::kMatches, NULL);
225   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
226                                        &error));
227   EXPECT_TRUE(MatchPattern(error, errors::kInvalidMatches));
228 
229   user_script->Set(keys::kMatches, Value::CreateIntegerValue(42));
230   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
231                                        &error));
232   EXPECT_TRUE(MatchPattern(error, errors::kInvalidMatches));
233 
234   ListValue* matches = new ListValue;
235   user_script->Set(keys::kMatches, matches);
236   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
237                                        &error));
238   EXPECT_TRUE(MatchPattern(error, errors::kInvalidMatchCount));
239 
240   // Test invalid match element
241   matches->Set(0, Value::CreateIntegerValue(42));
242   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
243                                        &error));
244   EXPECT_TRUE(MatchPattern(error, errors::kInvalidMatch));
245 
246   matches->Set(0, Value::CreateStringValue("chrome://*/*"));
247   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
248                                        &error));
249   EXPECT_TRUE(MatchPattern(error, errors::kInvalidMatch));
250 
251   // Test missing and invalid files array
252   input_value.reset(valid_value->DeepCopy());
253   input_value->GetList(keys::kContentScripts, &content_scripts);
254   content_scripts->GetDictionary(0, &user_script);
255   user_script->Remove(keys::kJs, NULL);
256   user_script->Remove(keys::kCss, NULL);
257   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
258                                        &error));
259   EXPECT_TRUE(MatchPattern(error, errors::kMissingFile));
260 
261   user_script->Set(keys::kJs, Value::CreateIntegerValue(42));
262   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
263                                        &error));
264   EXPECT_TRUE(MatchPattern(error, errors::kInvalidJsList));
265 
266   user_script->Set(keys::kCss, new ListValue);
267   user_script->Set(keys::kJs, new ListValue);
268   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
269                                        &error));
270   EXPECT_TRUE(MatchPattern(error, errors::kMissingFile));
271   user_script->Remove(keys::kCss, NULL);
272 
273   ListValue* files = new ListValue;
274   user_script->Set(keys::kJs, files);
275   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
276                                        &error));
277   EXPECT_TRUE(MatchPattern(error, errors::kMissingFile));
278 
279   // Test invalid file element
280   files->Set(0, Value::CreateIntegerValue(42));
281   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
282                                        &error));
283   EXPECT_TRUE(MatchPattern(error, errors::kInvalidJs));
284 
285   user_script->Remove(keys::kJs, NULL);
286   // Test the css element
287   user_script->Set(keys::kCss, Value::CreateIntegerValue(42));
288   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
289                                        &error));
290   EXPECT_TRUE(MatchPattern(error, errors::kInvalidCssList));
291 
292   // Test invalid file element
293   ListValue* css_files = new ListValue;
294   user_script->Set(keys::kCss, css_files);
295   css_files->Set(0, Value::CreateIntegerValue(42));
296   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
297                                        &error));
298   EXPECT_TRUE(MatchPattern(error, errors::kInvalidCss));
299 
300   // Test missing and invalid permissions array
301   input_value.reset(valid_value->DeepCopy());
302   EXPECT_TRUE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
303                                       &error));
304 
305   ListValue* permissions = NULL;
306   input_value->GetList(keys::kPermissions, &permissions);
307   ASSERT_FALSE(NULL == permissions);
308 
309   permissions = new ListValue;
310   input_value->Set(keys::kPermissions, permissions);
311   EXPECT_TRUE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
312                                       &error));
313 
314   input_value->Set(keys::kPermissions, Value::CreateIntegerValue(9));
315   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
316                                        &error));
317   EXPECT_TRUE(MatchPattern(error, errors::kInvalidPermissions));
318 
319   input_value.reset(valid_value->DeepCopy());
320   input_value->GetList(keys::kPermissions, &permissions);
321   permissions->Set(0, Value::CreateIntegerValue(24));
322   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
323                                        &error));
324   EXPECT_TRUE(MatchPattern(error, errors::kInvalidPermission));
325 
326   // We allow unknown API permissions, so this will be valid until we better
327   // distinguish between API and host permissions.
328   permissions->Set(0, Value::CreateStringValue("www.google.com"));
329   EXPECT_TRUE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
330                                       &error));
331 
332   // Multiple page actions are not allowed.
333   input_value.reset(valid_value->DeepCopy());
334   DictionaryValue* action = new DictionaryValue;
335   action->SetString(keys::kPageActionId, "MyExtensionActionId");
336   action->SetString(keys::kName, "MyExtensionActionName");
337   ListValue* action_list = new ListValue;
338   action_list->Append(action->DeepCopy());
339   action_list->Append(action);
340   input_value->Set(keys::kPageActions, action_list);
341   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
342                                        &error));
343   EXPECT_STREQ(errors::kInvalidPageActionsListSize, error.c_str());
344 
345   // Test invalid options page url.
346   input_value.reset(valid_value->DeepCopy());
347   input_value->Set(keys::kOptionsPage, Value::CreateNullValue());
348   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
349                                        &error));
350   EXPECT_TRUE(MatchPattern(error, errors::kInvalidOptionsPage));
351 
352   // Test invalid/empty default locale.
353   input_value.reset(valid_value->DeepCopy());
354   input_value->Set(keys::kDefaultLocale, Value::CreateIntegerValue(5));
355   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
356                                        &error));
357   EXPECT_TRUE(MatchPattern(error, errors::kInvalidDefaultLocale));
358 
359   input_value->Set(keys::kDefaultLocale, Value::CreateStringValue(""));
360   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
361                                        &error));
362   EXPECT_TRUE(MatchPattern(error, errors::kInvalidDefaultLocale));
363 
364   // Test invalid minimum_chrome_version.
365   input_value.reset(valid_value->DeepCopy());
366   input_value->Set(keys::kMinimumChromeVersion, Value::CreateIntegerValue(42));
367   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
368                                        &error));
369   EXPECT_TRUE(MatchPattern(error, errors::kInvalidMinimumChromeVersion));
370 
371 #if !defined(OS_MACOSX)
372   // TODO(aa): The version isn't stamped into the unit test binary on mac.
373   input_value->Set(keys::kMinimumChromeVersion,
374                    Value::CreateStringValue("88.8"));
375   EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY,
376                                        &error));
377   EXPECT_TRUE(MatchPattern(error, errors::kChromeVersionTooLow));
378 #endif
379 }
380 
TEST(ExtensionTest,InitFromValueValid)381 TEST(ExtensionTest, InitFromValueValid) {
382 #if defined(OS_WIN)
383   FilePath path(FILE_PATH_LITERAL("C:\\foo"));
384 #elif defined(OS_POSIX)
385   FilePath path(FILE_PATH_LITERAL("/foo"));
386 #endif
387   scoped_refptr<Extension> extension_ptr(new Extension(path,
388                                                        Extension::INVALID));
389   Extension& extension = *extension_ptr;
390   std::string error;
391   DictionaryValue input_value;
392 
393   // Test minimal extension
394   input_value.SetString(keys::kVersion, "1.0.0.0");
395   input_value.SetString(keys::kName, "my extension");
396 
397   EXPECT_TRUE(extension.InitFromValue(input_value, Extension::NO_FLAGS,
398                                       &error));
399   EXPECT_EQ("", error);
400   EXPECT_TRUE(Extension::IdIsValid(extension.id()));
401   EXPECT_EQ("1.0.0.0", extension.VersionString());
402   EXPECT_EQ("my extension", extension.name());
403   EXPECT_EQ(extension.id(), extension.url().host());
404   EXPECT_EQ(path.value(), extension.path().value());
405 
406   // Test permissions scheme.
407   ListValue* permissions = new ListValue;
408   permissions->Set(0, Value::CreateStringValue("file:///C:/foo.txt"));
409   input_value.Set(keys::kPermissions, permissions);
410 
411   // We allow unknown API permissions, so this will be valid until we better
412   // distinguish between API and host permissions.
413   EXPECT_TRUE(extension.InitFromValue(input_value, Extension::NO_FLAGS,
414                                       &error));
415   input_value.Remove(keys::kPermissions, NULL);
416 
417   // Test with an options page.
418   input_value.SetString(keys::kOptionsPage, "options.html");
419   EXPECT_TRUE(extension.InitFromValue(input_value, Extension::NO_FLAGS,
420                                       &error));
421   EXPECT_EQ("", error);
422   EXPECT_EQ("chrome-extension", extension.options_url().scheme());
423   EXPECT_EQ("/options.html", extension.options_url().path());
424 
425   // Test that an empty list of page actions does not stop a browser action
426   // from being loaded.
427   ListValue* empty_list = new ListValue;
428   input_value.Set(keys::kPageActions, empty_list);
429   EXPECT_TRUE(extension.InitFromValue(input_value, Extension::NO_FLAGS,
430                                       &error));
431   EXPECT_EQ("", error);
432 
433 #if !defined(OS_MACOSX)
434   // TODO(aa): The version isn't stamped into the unit test binary on mac.
435   // Test with a minimum_chrome_version.
436   input_value.SetString(keys::kMinimumChromeVersion, "1.0");
437   EXPECT_TRUE(extension.InitFromValue(input_value, Extension::NO_FLAGS,
438                                       &error));
439   EXPECT_EQ("", error);
440   // The minimum chrome version is not stored in the Extension object.
441 #endif
442 }
443 
TEST(ExtensionTest,InitFromValueValidNameInRTL)444 TEST(ExtensionTest, InitFromValueValidNameInRTL) {
445 #if defined(TOOLKIT_GTK)
446   GtkTextDirection gtk_dir = gtk_widget_get_default_direction();
447   gtk_widget_set_default_direction(GTK_TEXT_DIR_RTL);
448 #else
449   std::string locale = l10n_util::GetApplicationLocale("");
450   base::i18n::SetICUDefaultLocale("he");
451 #endif
452 
453 #if defined(OS_WIN)
454   FilePath path(FILE_PATH_LITERAL("C:\\foo"));
455 #elif defined(OS_POSIX)
456   FilePath path(FILE_PATH_LITERAL("/foo"));
457 #endif
458   scoped_refptr<Extension> extension_ptr(new Extension(path,
459                                                        Extension::INVALID));
460   Extension& extension = *extension_ptr;
461   std::string error;
462   DictionaryValue input_value;
463 
464   input_value.SetString(keys::kVersion, "1.0.0.0");
465   // No strong RTL characters in name.
466   std::wstring name(L"Dictionary (by Google)");
467   input_value.SetString(keys::kName, WideToUTF16Hack(name));
468   EXPECT_TRUE(extension.InitFromValue(input_value, Extension::NO_FLAGS,
469                                       &error));
470   EXPECT_EQ("", error);
471   std::wstring localized_name(name);
472   base::i18n::AdjustStringForLocaleDirection(&localized_name);
473   EXPECT_EQ(localized_name, UTF8ToWide(extension.name()));
474 
475   // Strong RTL characters in name.
476   name = L"Dictionary (\x05D1\x05D2"L" Google)";
477   input_value.SetString(keys::kName, WideToUTF16Hack(name));
478   EXPECT_TRUE(extension.InitFromValue(input_value, Extension::NO_FLAGS,
479                                       &error));
480   EXPECT_EQ("", error);
481   localized_name = name;
482   base::i18n::AdjustStringForLocaleDirection(&localized_name);
483   EXPECT_EQ(localized_name, UTF8ToWide(extension.name()));
484 
485   // Reset locale.
486 #if defined(TOOLKIT_GTK)
487   gtk_widget_set_default_direction(gtk_dir);
488 #else
489   base::i18n::SetICUDefaultLocale(locale);
490 #endif
491 }
492 
TEST(ExtensionTest,GetResourceURLAndPath)493 TEST(ExtensionTest, GetResourceURLAndPath) {
494 #if defined(OS_WIN)
495   FilePath path(FILE_PATH_LITERAL("C:\\foo"));
496 #elif defined(OS_POSIX)
497   FilePath path(FILE_PATH_LITERAL("/foo"));
498 #endif
499   DictionaryValue input_value;
500   input_value.SetString(keys::kVersion, "1.0.0.0");
501   input_value.SetString(keys::kName, "my extension");
502   scoped_refptr<Extension> extension(Extension::Create(path,
503       Extension::INVALID, input_value, Extension::STRICT_ERROR_CHECKS, NULL));
504   EXPECT_TRUE(extension.get());
505 
506   EXPECT_EQ(extension->url().spec() + "bar/baz.js",
507             Extension::GetResourceURL(extension->url(), "bar/baz.js").spec());
508   EXPECT_EQ(extension->url().spec() + "baz.js",
509             Extension::GetResourceURL(extension->url(),
510                                       "bar/../baz.js").spec());
511   EXPECT_EQ(extension->url().spec() + "baz.js",
512             Extension::GetResourceURL(extension->url(), "../baz.js").spec());
513 }
514 
TEST(ExtensionTest,LoadPageActionHelper)515 TEST(ExtensionTest, LoadPageActionHelper) {
516 #if defined(OS_WIN)
517     FilePath path(base::StringPrintf(L"c:\\extension"));
518 #else
519     FilePath path(base::StringPrintf("/extension"));
520 #endif
521   scoped_refptr<Extension> extension_ptr(new Extension(path,
522                                                        Extension::INVALID));
523   Extension& extension = *extension_ptr;
524   std::string error_msg;
525   scoped_ptr<ExtensionAction> action;
526   DictionaryValue input;
527 
528   // First try with an empty dictionary.
529   action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
530   ASSERT_TRUE(action != NULL);
531   ASSERT_TRUE(error_msg.empty());
532 
533   // Now setup some values to use in the action.
534   const std::string id("MyExtensionActionId");
535   const std::string name("MyExtensionActionName");
536   std::string img1("image1.png");
537   std::string img2("image2.png");
538 
539   // Add the dictionary for the contextual action.
540   input.SetString(keys::kPageActionId, id);
541   input.SetString(keys::kName, name);
542   ListValue* icons = new ListValue;
543   icons->Set(0, Value::CreateStringValue(img1));
544   icons->Set(1, Value::CreateStringValue(img2));
545   input.Set(keys::kPageActionIcons, icons);
546 
547   // Parse and read back the values from the object.
548   action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
549   ASSERT_TRUE(NULL != action.get());
550   ASSERT_TRUE(error_msg.empty());
551   ASSERT_EQ(id, action->id());
552   // No title, so fall back to name.
553   ASSERT_EQ(name, action->GetTitle(1));
554   ASSERT_EQ(2u, action->icon_paths()->size());
555   ASSERT_EQ(img1, (*action->icon_paths())[0]);
556   ASSERT_EQ(img2, (*action->icon_paths())[1]);
557 
558   // Explicitly set the same type and parse again.
559   input.SetString(keys::kType, values::kPageActionTypeTab);
560   action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
561   ASSERT_TRUE(NULL != action.get());
562   ASSERT_TRUE(error_msg.empty());
563 
564   // Make a deep copy of the input and remove one key at a time and see if we
565   // get the right error.
566   scoped_ptr<DictionaryValue> copy;
567 
568   // First remove id key.
569   copy.reset(input.DeepCopy());
570   copy->Remove(keys::kPageActionId, NULL);
571   action.reset(extension.LoadExtensionActionHelper(copy.get(), &error_msg));
572   ASSERT_TRUE(NULL != action.get());
573 
574   // Then remove the name key. It's optional, so no error.
575   copy.reset(input.DeepCopy());
576   copy->Remove(keys::kName, NULL);
577   action.reset(extension.LoadExtensionActionHelper(copy.get(), &error_msg));
578   ASSERT_TRUE(NULL != action.get());
579   ASSERT_TRUE(action->GetTitle(1).empty());
580   ASSERT_TRUE(error_msg.empty());
581 
582   // Then remove the icon paths key.
583   copy.reset(input.DeepCopy());
584   copy->Remove(keys::kPageActionIcons, NULL);
585   action.reset(extension.LoadExtensionActionHelper(copy.get(), &error_msg));
586   ASSERT_TRUE(NULL != action.get());
587   error_msg = "";
588 
589   // Now test that we can parse the new format for page actions.
590 
591   // Now setup some values to use in the page action.
592   const std::string kTitle("MyExtensionActionTitle");
593   const std::string kIcon("image1.png");
594   const std::string kPopupHtmlFile("a_popup.html");
595 
596   // Add the dictionary for the contextual action.
597   input.Clear();
598   input.SetString(keys::kPageActionDefaultTitle, kTitle);
599   input.SetString(keys::kPageActionDefaultIcon, kIcon);
600 
601   // Parse and read back the values from the object.
602   action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
603   ASSERT_TRUE(action.get());
604   ASSERT_TRUE(error_msg.empty());
605   ASSERT_EQ(kTitle, action->GetTitle(1));
606   ASSERT_EQ(0u, action->icon_paths()->size());
607 
608   // Invalid title should give an error even with a valid name.
609   input.Clear();
610   input.SetInteger(keys::kPageActionDefaultTitle, 42);
611   input.SetString(keys::kName, name);
612   action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
613   ASSERT_TRUE(NULL == action.get());
614   ASSERT_STREQ(errors::kInvalidPageActionDefaultTitle, error_msg.c_str());
615   error_msg = "";
616 
617   // Invalid name should give an error only with no title.
618   input.SetString(keys::kPageActionDefaultTitle, kTitle);
619   input.SetInteger(keys::kName, 123);
620   action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
621   ASSERT_TRUE(NULL != action.get());
622   ASSERT_EQ(kTitle, action->GetTitle(1));
623   ASSERT_TRUE(error_msg.empty());
624 
625   input.Remove(keys::kPageActionDefaultTitle, NULL);
626   action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
627   ASSERT_TRUE(NULL == action.get());
628   ASSERT_STREQ(errors::kInvalidPageActionName, error_msg.c_str());
629   error_msg = "";
630 
631   // Test that keys "popup" and "default_popup" both work, but can not
632   // be used at the same time.
633   input.Clear();
634   input.SetString(keys::kPageActionDefaultTitle, kTitle);
635   input.SetString(keys::kPageActionDefaultIcon, kIcon);
636 
637   // LoadExtensionActionHelper expects the extension member |extension_url|
638   // to be set.
639   extension.extension_url_ =
640       GURL(std::string(chrome::kExtensionScheme) +
641            chrome::kStandardSchemeSeparator +
642            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/");
643 
644   // Add key "popup", expect success.
645   input.SetString(keys::kPageActionPopup, kPopupHtmlFile);
646   action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
647   ASSERT_TRUE(NULL != action.get());
648   ASSERT_TRUE(error_msg.empty());
649   ASSERT_STREQ(
650       extension.url().Resolve(kPopupHtmlFile).spec().c_str(),
651       action->GetPopupUrl(ExtensionAction::kDefaultTabId).spec().c_str());
652 
653   // Add key "default_popup", expect failure.
654   input.SetString(keys::kPageActionDefaultPopup, kPopupHtmlFile);
655   action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
656   ASSERT_TRUE(NULL == action.get());
657   ASSERT_STREQ(
658       ExtensionErrorUtils::FormatErrorMessage(
659           errors::kInvalidPageActionOldAndNewKeys,
660           keys::kPageActionDefaultPopup,
661           keys::kPageActionPopup).c_str(),
662       error_msg.c_str());
663   error_msg = "";
664 
665   // Remove key "popup", expect success.
666   input.Remove(keys::kPageActionPopup, NULL);
667   action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
668   ASSERT_TRUE(NULL != action.get());
669   ASSERT_TRUE(error_msg.empty());
670   ASSERT_STREQ(
671       extension.url().Resolve(kPopupHtmlFile).spec().c_str(),
672       action->GetPopupUrl(ExtensionAction::kDefaultTabId).spec().c_str());
673 
674   // Setting default_popup to "" is the same as having no popup.
675   input.Remove(keys::kPageActionDefaultPopup, NULL);
676   input.SetString(keys::kPageActionDefaultPopup, "");
677   action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
678   ASSERT_TRUE(NULL != action.get());
679   ASSERT_TRUE(error_msg.empty());
680   EXPECT_FALSE(action->HasPopup(ExtensionAction::kDefaultTabId));
681   ASSERT_STREQ(
682       "",
683       action->GetPopupUrl(ExtensionAction::kDefaultTabId).spec().c_str());
684 
685   // Setting popup to "" is the same as having no popup.
686   input.Remove(keys::kPageActionDefaultPopup, NULL);
687   input.SetString(keys::kPageActionPopup, "");
688   action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
689   ASSERT_TRUE(NULL != action.get());
690   ASSERT_TRUE(error_msg.empty());
691   EXPECT_FALSE(action->HasPopup(ExtensionAction::kDefaultTabId));
692   ASSERT_STREQ(
693       "",
694       action->GetPopupUrl(ExtensionAction::kDefaultTabId).spec().c_str());
695 }
696 
TEST(ExtensionTest,IdIsValid)697 TEST(ExtensionTest, IdIsValid) {
698   EXPECT_TRUE(Extension::IdIsValid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
699   EXPECT_TRUE(Extension::IdIsValid("pppppppppppppppppppppppppppppppp"));
700   EXPECT_TRUE(Extension::IdIsValid("abcdefghijklmnopabcdefghijklmnop"));
701   EXPECT_TRUE(Extension::IdIsValid("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"));
702   EXPECT_FALSE(Extension::IdIsValid("abcdefghijklmnopabcdefghijklmno"));
703   EXPECT_FALSE(Extension::IdIsValid("abcdefghijklmnopabcdefghijklmnopa"));
704   EXPECT_FALSE(Extension::IdIsValid("0123456789abcdef0123456789abcdef"));
705   EXPECT_FALSE(Extension::IdIsValid("abcdefghijklmnopabcdefghijklmnoq"));
706   EXPECT_FALSE(Extension::IdIsValid("abcdefghijklmnopabcdefghijklmno0"));
707 }
708 
TEST(ExtensionTest,GenerateID)709 TEST(ExtensionTest, GenerateID) {
710   const uint8 public_key_info[] = {
711     0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
712     0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81,
713     0x89, 0x02, 0x81, 0x81, 0x00, 0xb8, 0x7f, 0x2b, 0x20, 0xdc, 0x7c, 0x9b,
714     0x0c, 0xdc, 0x51, 0x61, 0x99, 0x0d, 0x36, 0x0f, 0xd4, 0x66, 0x88, 0x08,
715     0x55, 0x84, 0xd5, 0x3a, 0xbf, 0x2b, 0xa4, 0x64, 0x85, 0x7b, 0x0c, 0x04,
716     0x13, 0x3f, 0x8d, 0xf4, 0xbc, 0x38, 0x0d, 0x49, 0xfe, 0x6b, 0xc4, 0x5a,
717     0xb0, 0x40, 0x53, 0x3a, 0xd7, 0x66, 0x09, 0x0f, 0x9e, 0x36, 0x74, 0x30,
718     0xda, 0x8a, 0x31, 0x4f, 0x1f, 0x14, 0x50, 0xd7, 0xc7, 0x20, 0x94, 0x17,
719     0xde, 0x4e, 0xb9, 0x57, 0x5e, 0x7e, 0x0a, 0xe5, 0xb2, 0x65, 0x7a, 0x89,
720     0x4e, 0xb6, 0x47, 0xff, 0x1c, 0xbd, 0xb7, 0x38, 0x13, 0xaf, 0x47, 0x85,
721     0x84, 0x32, 0x33, 0xf3, 0x17, 0x49, 0xbf, 0xe9, 0x96, 0xd0, 0xd6, 0x14,
722     0x6f, 0x13, 0x8d, 0xc5, 0xfc, 0x2c, 0x72, 0xba, 0xac, 0xea, 0x7e, 0x18,
723     0x53, 0x56, 0xa6, 0x83, 0xa2, 0xce, 0x93, 0x93, 0xe7, 0x1f, 0x0f, 0xe6,
724     0x0f, 0x02, 0x03, 0x01, 0x00, 0x01
725   };
726 
727   std::string extension_id;
728   EXPECT_TRUE(
729       Extension::GenerateId(
730           std::string(reinterpret_cast<const char*>(&public_key_info[0]),
731                       arraysize(public_key_info)),
732           &extension_id));
733   EXPECT_EQ("melddjfinppjdikinhbgehiennejpfhp", extension_id);
734 }
735 
TEST(ExtensionTest,UpdateUrls)736 TEST(ExtensionTest, UpdateUrls) {
737   // Test several valid update urls
738   std::vector<std::string> valid;
739   valid.push_back("http://test.com");
740   valid.push_back("http://test.com/");
741   valid.push_back("http://test.com/update");
742   valid.push_back("http://test.com/update?check=true");
743   for (size_t i = 0; i < valid.size(); i++) {
744     GURL url(valid[i]);
745     EXPECT_TRUE(url.is_valid());
746 
747     DictionaryValue input_value;
748 #if defined(OS_WIN)
749     // (Why %Iu below?  This is the single file in the whole code base that
750     // might make use of a WidePRIuS; let's not encourage any more.)
751     FilePath path(base::StringPrintf(L"c:\\extension%Iu", i));
752 #else
753     FilePath path(base::StringPrintf("/extension%" PRIuS, i));
754 #endif
755     std::string error;
756 
757     input_value.SetString(keys::kVersion, "1.0");
758     input_value.SetString(keys::kName, "Test");
759     input_value.SetString(keys::kUpdateURL, url.spec());
760 
761     scoped_refptr<Extension> extension(Extension::Create(
762         path, Extension::INVALID, input_value, Extension::STRICT_ERROR_CHECKS,
763         &error));
764     EXPECT_TRUE(extension.get()) << error;
765   }
766 
767   // Test some invalid update urls
768   std::vector<std::string> invalid;
769   invalid.push_back("");
770   invalid.push_back("test.com");
771   valid.push_back("http://test.com/update#whatever");
772   for (size_t i = 0; i < invalid.size(); i++) {
773     DictionaryValue input_value;
774 #if defined(OS_WIN)
775     // (Why %Iu below?  This is the single file in the whole code base that
776     // might make use of a WidePRIuS; let's not encourage any more.)
777     FilePath path(base::StringPrintf(L"c:\\extension%Iu", i));
778 #else
779     FilePath path(base::StringPrintf("/extension%" PRIuS, i));
780 #endif
781     std::string error;
782     input_value.SetString(keys::kVersion, "1.0");
783     input_value.SetString(keys::kName, "Test");
784     input_value.SetString(keys::kUpdateURL, invalid[i]);
785 
786     scoped_refptr<Extension> extension(Extension::Create(
787         path, Extension::INVALID, input_value, Extension::STRICT_ERROR_CHECKS,
788         &error));
789     EXPECT_FALSE(extension.get());
790     EXPECT_TRUE(MatchPattern(error, errors::kInvalidUpdateURL));
791   }
792 }
793 
794 // This test ensures that the mimetype sniffing code stays in sync with the
795 // actual crx files that we test other parts of the system with.
TEST(ExtensionTest,MimeTypeSniffing)796 TEST(ExtensionTest, MimeTypeSniffing) {
797   FilePath path;
798   ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &path));
799   path = path.AppendASCII("extensions").AppendASCII("good.crx");
800 
801   std::string data;
802   ASSERT_TRUE(file_util::ReadFileToString(path, &data));
803 
804   std::string result;
805   EXPECT_TRUE(net::SniffMimeType(data.c_str(), data.size(),
806               GURL("http://www.example.com/foo.crx"), "", &result));
807   EXPECT_EQ(std::string(Extension::kMimeType), result);
808 
809   data.clear();
810   result.clear();
811   path = path.DirName().AppendASCII("bad_magic.crx");
812   ASSERT_TRUE(file_util::ReadFileToString(path, &data));
813   EXPECT_TRUE(net::SniffMimeType(data.c_str(), data.size(),
814               GURL("http://www.example.com/foo.crx"), "", &result));
815   EXPECT_EQ("application/octet-stream", result);
816 }
817 
LoadManifest(const std::string & dir,const std::string & test_file,int extra_flags)818 static scoped_refptr<Extension> LoadManifest(const std::string& dir,
819                                              const std::string& test_file,
820                                              int extra_flags) {
821   FilePath path;
822   PathService::Get(chrome::DIR_TEST_DATA, &path);
823   path = path.AppendASCII("extensions")
824              .AppendASCII(dir)
825              .AppendASCII(test_file);
826 
827   JSONFileValueSerializer serializer(path);
828   std::string error;
829   scoped_ptr<Value> result(serializer.Deserialize(NULL, &error));
830   if (!result.get()) {
831     EXPECT_EQ("", error);
832     return NULL;
833   }
834 
835   scoped_refptr<Extension> extension = Extension::Create(
836       path.DirName(), Extension::INVALID,
837       *static_cast<DictionaryValue*>(result.get()),
838       Extension::STRICT_ERROR_CHECKS | extra_flags, &error);
839   EXPECT_TRUE(extension) << error;
840   return extension;
841 }
842 
LoadManifest(const std::string & dir,const std::string & test_file)843 static scoped_refptr<Extension> LoadManifest(const std::string& dir,
844                                              const std::string& test_file) {
845   return LoadManifest(dir, test_file, Extension::NO_FLAGS);
846 }
847 
TEST(ExtensionTest,EffectiveHostPermissions)848 TEST(ExtensionTest, EffectiveHostPermissions) {
849   scoped_refptr<Extension> extension;
850   ExtensionExtent hosts;
851 
852   extension = LoadManifest("effective_host_permissions", "empty.json");
853   EXPECT_EQ(0u, extension->GetEffectiveHostPermissions().patterns().size());
854   EXPECT_FALSE(hosts.ContainsURL(GURL("http://www.google.com")));
855   EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts());
856 
857   extension = LoadManifest("effective_host_permissions", "one_host.json");
858   hosts = extension->GetEffectiveHostPermissions();
859   EXPECT_TRUE(hosts.ContainsURL(GURL("http://www.google.com")));
860   EXPECT_FALSE(hosts.ContainsURL(GURL("https://www.google.com")));
861   EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts());
862 
863   extension = LoadManifest("effective_host_permissions",
864                            "one_host_wildcard.json");
865   hosts = extension->GetEffectiveHostPermissions();
866   EXPECT_TRUE(hosts.ContainsURL(GURL("http://google.com")));
867   EXPECT_TRUE(hosts.ContainsURL(GURL("http://foo.google.com")));
868   EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts());
869 
870   extension = LoadManifest("effective_host_permissions", "two_hosts.json");
871   hosts = extension->GetEffectiveHostPermissions();
872   EXPECT_TRUE(hosts.ContainsURL(GURL("http://www.google.com")));
873   EXPECT_TRUE(hosts.ContainsURL(GURL("http://www.reddit.com")));
874   EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts());
875 
876   extension = LoadManifest("effective_host_permissions",
877                            "https_not_considered.json");
878   hosts = extension->GetEffectiveHostPermissions();
879   EXPECT_TRUE(hosts.ContainsURL(GURL("http://google.com")));
880   EXPECT_TRUE(hosts.ContainsURL(GURL("https://google.com")));
881   EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts());
882 
883   extension = LoadManifest("effective_host_permissions",
884                            "two_content_scripts.json");
885   hosts = extension->GetEffectiveHostPermissions();
886   EXPECT_TRUE(hosts.ContainsURL(GURL("http://google.com")));
887   EXPECT_TRUE(hosts.ContainsURL(GURL("http://www.reddit.com")));
888   EXPECT_TRUE(hosts.ContainsURL(GURL("http://news.ycombinator.com")));
889   EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts());
890 
891   extension = LoadManifest("effective_host_permissions", "all_hosts.json");
892   hosts = extension->GetEffectiveHostPermissions();
893   EXPECT_TRUE(hosts.ContainsURL(GURL("http://test/")));
894   EXPECT_FALSE(hosts.ContainsURL(GURL("https://test/")));
895   EXPECT_TRUE(hosts.ContainsURL(GURL("http://www.google.com")));
896   EXPECT_TRUE(extension->HasEffectiveAccessToAllHosts());
897 
898   extension = LoadManifest("effective_host_permissions", "all_hosts2.json");
899   hosts = extension->GetEffectiveHostPermissions();
900   EXPECT_TRUE(hosts.ContainsURL(GURL("http://test/")));
901   EXPECT_TRUE(hosts.ContainsURL(GURL("http://www.google.com")));
902   EXPECT_TRUE(extension->HasEffectiveAccessToAllHosts());
903 
904   extension = LoadManifest("effective_host_permissions", "all_hosts3.json");
905   hosts = extension->GetEffectiveHostPermissions();
906   EXPECT_FALSE(hosts.ContainsURL(GURL("http://test/")));
907   EXPECT_TRUE(hosts.ContainsURL(GURL("https://test/")));
908   EXPECT_TRUE(hosts.ContainsURL(GURL("http://www.google.com")));
909   EXPECT_TRUE(extension->HasEffectiveAccessToAllHosts());
910 }
911 
TEST(ExtensionTest,IsPrivilegeIncrease)912 TEST(ExtensionTest, IsPrivilegeIncrease) {
913   const struct {
914     const char* base_name;
915     // Increase these sizes if you have more than 10.
916     const char* granted_apis[10];
917     const char* granted_hosts[10];
918     bool full_access;
919     bool expect_increase;
920   } kTests[] = {
921     { "allhosts1", {NULL}, {"http://*/", NULL}, false,
922       false },  // all -> all
923     { "allhosts2", {NULL}, {"http://*/", NULL}, false,
924       false },  // all -> one
925     { "allhosts3", {NULL}, {NULL}, false, true },  // one -> all
926     { "hosts1", {NULL},
927       {"http://www.google.com/", "http://www.reddit.com/", NULL}, false,
928       false },  // http://a,http://b -> http://a,http://b
929     { "hosts2", {NULL},
930       {"http://www.google.com/", "http://www.reddit.com/", NULL}, false,
931       true },  // http://a,http://b -> https://a,http://*.b
932     { "hosts3", {NULL},
933       {"http://www.google.com/", "http://www.reddit.com/", NULL}, false,
934       false },  // http://a,http://b -> http://a
935     { "hosts4", {NULL},
936       {"http://www.google.com/", NULL}, false,
937       true },  // http://a -> http://a,http://b
938     { "hosts5", {"tabs", "notifications", NULL},
939       {"http://*.example.com/", "http://*.example.com/*",
940        "http://*.example.co.uk/*", "http://*.example.com.au/*",
941        NULL}, false,
942       false },  // http://a,b,c -> http://a,b,c + https://a,b,c
943     { "hosts6", {"tabs", "notifications", NULL},
944       {"http://*.example.com/", "http://*.example.com/*", NULL}, false,
945       false },  // http://a.com -> http://a.com + http://a.co.uk
946     { "permissions1", {"tabs", NULL},
947       {NULL}, false, false },  // tabs -> tabs
948     { "permissions2", {"tabs", NULL},
949       {NULL}, false, true },  // tabs -> tabs,bookmarks
950     { "permissions3", {NULL},
951       {"http://*/*", NULL},
952       false, true },  // http://a -> http://a,tabs
953     { "permissions5", {"bookmarks", NULL},
954       {NULL}, false, true },  // bookmarks -> bookmarks,history
955 #if !defined(OS_CHROMEOS)  // plugins aren't allowed in ChromeOS
956     { "permissions4", {NULL},
957       {NULL}, true, false },  // plugin -> plugin,tabs
958     { "plugin1", {NULL},
959       {NULL}, true, false },  // plugin -> plugin
960     { "plugin2", {NULL},
961       {NULL}, true, false },  // plugin -> none
962     { "plugin3", {NULL},
963       {NULL}, false, true },  // none -> plugin
964 #endif
965     { "storage", {NULL},
966       {NULL}, false, false },  // none -> storage
967     { "notifications", {NULL},
968       {NULL}, false, false }  // none -> notifications
969   };
970 
971   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) {
972     scoped_refptr<Extension> old_extension(
973         LoadManifest("allow_silent_upgrade",
974                      std::string(kTests[i].base_name) + "_old.json"));
975     scoped_refptr<Extension> new_extension(
976         LoadManifest("allow_silent_upgrade",
977                      std::string(kTests[i].base_name) + "_new.json"));
978 
979     std::set<std::string> granted_apis;
980     for (size_t j = 0; kTests[i].granted_apis[j] != NULL; ++j)
981       granted_apis.insert(kTests[i].granted_apis[j]);
982 
983     ExtensionExtent granted_hosts;
984     for (size_t j = 0; kTests[i].granted_hosts[j] != NULL; ++j)
985       AddPattern(&granted_hosts, kTests[i].granted_hosts[j]);
986 
987     EXPECT_TRUE(new_extension.get()) << kTests[i].base_name << "_new.json";
988     if (!new_extension.get())
989       continue;
990 
991     EXPECT_EQ(kTests[i].expect_increase,
992               Extension::IsPrivilegeIncrease(kTests[i].full_access,
993                                              granted_apis,
994                                              granted_hosts,
995                                              new_extension.get()))
996         << kTests[i].base_name;
997   }
998 }
999 
TEST(ExtensionTest,PermissionMessages)1000 TEST(ExtensionTest, PermissionMessages) {
1001   // Ensure that all permissions that needs to show install UI actually have
1002   // strings associated with them.
1003 
1004   std::set<std::string> skip;
1005 
1006   // These are considered "nuisance" or "trivial" permissions that don't need
1007   // a prompt.
1008   skip.insert(Extension::kContextMenusPermission);
1009   skip.insert(Extension::kIdlePermission);
1010   skip.insert(Extension::kNotificationPermission);
1011   skip.insert(Extension::kUnlimitedStoragePermission);
1012   skip.insert(Extension::kContentSettingsPermission);
1013 
1014   // TODO(erikkay) add a string for this permission.
1015   skip.insert(Extension::kBackgroundPermission);
1016 
1017   // The cookie permission does nothing unless you have associated host
1018   // permissions.
1019   skip.insert(Extension::kCookiePermission);
1020 
1021   // The proxy permission is warned as part of host permission checks.
1022   skip.insert(Extension::kProxyPermission);
1023 
1024   // This permission requires explicit user action (context menu handler)
1025   // so we won't prompt for it for now.
1026   skip.insert(Extension::kFileBrowserHandlerPermission);
1027 
1028   // If you've turned on the experimental command-line flag, we don't need
1029   // to warn you further.
1030   skip.insert(Extension::kExperimentalPermission);
1031 
1032   // These are only usable by component extensions.
1033   skip.insert(Extension::kWebstorePrivatePermission);
1034   skip.insert(Extension::kFileBrowserPrivatePermission);
1035   skip.insert(Extension::kChromeosInfoPrivatePermissions);
1036 
1037   const Extension::PermissionMessage::MessageId ID_NONE =
1038       Extension::PermissionMessage::ID_NONE;
1039 
1040   for (size_t i = 0; i < Extension::kNumPermissions; ++i) {
1041     Extension::Permission permission = Extension::kPermissions[i];
1042     if (skip.count(permission.name)) {
1043       EXPECT_EQ(ID_NONE, permission.message_id)
1044           << "unexpected message_id for " << permission.name;
1045     } else {
1046       EXPECT_NE(ID_NONE, permission.message_id)
1047           << "missing message_id for " << permission.name;
1048     }
1049   }
1050 }
1051 
1052 // Returns a copy of |source| resized to |size| x |size|.
ResizedCopy(const SkBitmap & source,int size)1053 static SkBitmap ResizedCopy(const SkBitmap& source, int size) {
1054   return skia::ImageOperations::Resize(source,
1055                                        skia::ImageOperations::RESIZE_LANCZOS3,
1056                                        size,
1057                                        size);
1058 }
1059 
SizeEquals(const SkBitmap & bitmap,const gfx::Size & size)1060 static bool SizeEquals(const SkBitmap& bitmap, const gfx::Size& size) {
1061   return bitmap.width() == size.width() && bitmap.height() == size.height();
1062 }
1063 
TEST(ExtensionTest,ImageCaching)1064 TEST(ExtensionTest, ImageCaching) {
1065   FilePath path;
1066   ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &path));
1067   path = path.AppendASCII("extensions");
1068 
1069   // Initialize the Extension.
1070   std::string errors;
1071   DictionaryValue values;
1072   values.SetString(keys::kName, "test");
1073   values.SetString(keys::kVersion, "0.1");
1074   scoped_refptr<Extension> extension(Extension::Create(
1075       path, Extension::INVALID, values, Extension::STRICT_ERROR_CHECKS,
1076       &errors));
1077   ASSERT_TRUE(extension.get());
1078 
1079   // Create an ExtensionResource pointing at an icon.
1080   FilePath icon_relative_path(FILE_PATH_LITERAL("icon3.png"));
1081   ExtensionResource resource(extension->id(),
1082                              extension->path(),
1083                              icon_relative_path);
1084 
1085   // Read in the icon file.
1086   FilePath icon_absolute_path = extension->path().Append(icon_relative_path);
1087   std::string raw_png;
1088   ASSERT_TRUE(file_util::ReadFileToString(icon_absolute_path, &raw_png));
1089   SkBitmap image;
1090   ASSERT_TRUE(gfx::PNGCodec::Decode(
1091       reinterpret_cast<const unsigned char*>(raw_png.data()),
1092       raw_png.length(),
1093       &image));
1094 
1095   // Make sure the icon file is the size we expect.
1096   gfx::Size original_size(66, 66);
1097   ASSERT_EQ(image.width(), original_size.width());
1098   ASSERT_EQ(image.height(), original_size.height());
1099 
1100   // Create two resized versions at size 16x16 and 24x24.
1101   SkBitmap image16 = ResizedCopy(image, 16);
1102   SkBitmap image24 = ResizedCopy(image, 24);
1103 
1104   gfx::Size size16(16, 16);
1105   gfx::Size size24(24, 24);
1106 
1107   // Cache the 16x16 copy.
1108   EXPECT_FALSE(extension->HasCachedImage(resource, size16));
1109   extension->SetCachedImage(resource, image16, original_size);
1110   EXPECT_TRUE(extension->HasCachedImage(resource, size16));
1111   EXPECT_TRUE(SizeEquals(extension->GetCachedImage(resource, size16), size16));
1112   EXPECT_FALSE(extension->HasCachedImage(resource, size24));
1113   EXPECT_FALSE(extension->HasCachedImage(resource, original_size));
1114 
1115   // Cache the 24x24 copy.
1116   extension->SetCachedImage(resource, image24, original_size);
1117   EXPECT_TRUE(extension->HasCachedImage(resource, size24));
1118   EXPECT_TRUE(SizeEquals(extension->GetCachedImage(resource, size24), size24));
1119   EXPECT_FALSE(extension->HasCachedImage(resource, original_size));
1120 
1121   // Cache the original, and verify that it gets returned when we ask for a
1122   // max_size that is larger than the original.
1123   gfx::Size size128(128, 128);
1124   EXPECT_TRUE(image.width() < size128.width() &&
1125               image.height() < size128.height());
1126   extension->SetCachedImage(resource, image, original_size);
1127   EXPECT_TRUE(extension->HasCachedImage(resource, original_size));
1128   EXPECT_TRUE(extension->HasCachedImage(resource, size128));
1129   EXPECT_TRUE(SizeEquals(extension->GetCachedImage(resource, original_size),
1130                          original_size));
1131   EXPECT_TRUE(SizeEquals(extension->GetCachedImage(resource, size128),
1132                          original_size));
1133   EXPECT_EQ(extension->GetCachedImage(resource, original_size).getPixels(),
1134             extension->GetCachedImage(resource, size128).getPixels());
1135 }
1136 
1137 // Tests that the old permission name "unlimited_storage" still works for
1138 // backwards compatibility (we renamed it to "unlimitedStorage").
TEST(ExtensionTest,OldUnlimitedStoragePermission)1139 TEST(ExtensionTest, OldUnlimitedStoragePermission) {
1140   ScopedTempDir directory;
1141   ASSERT_TRUE(directory.CreateUniqueTempDir());
1142   FilePath extension_path = directory.path();
1143   DictionaryValue dictionary;
1144 
1145   // The two required keys.
1146   dictionary.SetString(extension_manifest_keys::kName, "test");
1147   dictionary.SetString(extension_manifest_keys::kVersion, "0.1");
1148 
1149   // Create a permissions list containing "unlimited_storage" and add it.
1150   ListValue* permissions = new ListValue();
1151   const char* old_unlimited = "unlimited_storage";
1152   EXPECT_STREQ(old_unlimited, Extension::kOldUnlimitedStoragePermission);
1153   permissions->Append(Value::CreateStringValue(old_unlimited));
1154   dictionary.Set(extension_manifest_keys::kPermissions, permissions);
1155 
1156   // Initialize the extension and make sure the permission for unlimited storage
1157   // is present.
1158   std::string errors;
1159   scoped_refptr<Extension> extension(Extension::Create(
1160       extension_path, Extension::INVALID, dictionary,
1161       Extension::STRICT_ERROR_CHECKS, &errors));
1162   EXPECT_TRUE(extension.get());
1163   EXPECT_TRUE(extension->HasApiPermission(
1164       Extension::kUnlimitedStoragePermission));
1165 }
1166 
1167 // This tests the API permissions with an empty manifest (one that just
1168 // specifies a name and a version and nothing else).
TEST(ExtensionTest,ApiPermissions)1169 TEST(ExtensionTest, ApiPermissions) {
1170   const struct {
1171     const char* permission_name;
1172     bool expect_success;
1173   } kTests[] = {
1174     // Negative test.
1175     { "non_existing_permission", false },
1176     // Test default module/package permission.
1177     { "browserAction",  true },
1178     { "browserActions", true },
1179     { "devtools",       true },
1180     { "extension",      true },
1181     { "i18n",           true },
1182     { "pageAction",     true },
1183     { "pageActions",    true },
1184     { "test",           true },
1185     // Some negative tests.
1186     { "bookmarks",      false },
1187     { "cookies",        false },
1188     { "history",        false },
1189     { "tabs.onUpdated", false },
1190     // Make sure we find the module name after stripping '.' and '/'.
1191     { "browserAction/abcd/onClick",  true },
1192     { "browserAction.abcd.onClick",  true },
1193     // Test Tabs functions.
1194     { "tabs.create",      true},
1195     { "tabs.update",      true},
1196     { "tabs.getSelected", false},
1197   };
1198 
1199   scoped_refptr<Extension> extension;
1200   extension = LoadManifest("empty_manifest", "empty.json");
1201 
1202   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) {
1203     EXPECT_EQ(kTests[i].expect_success,
1204               extension->HasApiPermission(kTests[i].permission_name))
1205                   << "Permission being tested: " << kTests[i].permission_name;
1206   }
1207 }
1208 
TEST(ExtensionTest,GetHostPermissionMessages_ManyHosts)1209 TEST(ExtensionTest, GetHostPermissionMessages_ManyHosts) {
1210   scoped_refptr<Extension> extension;
1211   extension = LoadManifest("permissions", "many-hosts.json");
1212   std::vector<string16> warnings = extension->GetPermissionMessageStrings();
1213   ASSERT_EQ(1u, warnings.size());
1214   EXPECT_EQ("Your data on www.google.com and encrypted.google.com",
1215             UTF16ToUTF8(warnings[0]));
1216 }
1217 
TEST(ExtensionTest,GetPermissionMessages_Plugins)1218 TEST(ExtensionTest, GetPermissionMessages_Plugins) {
1219   scoped_refptr<Extension> extension;
1220   extension = LoadManifest("permissions", "plugins.json");
1221   std::vector<string16> warnings = extension->GetPermissionMessageStrings();
1222   // We don't parse the plugins key on Chrome OS, so it should not ask for any
1223   // permissions.
1224 #if defined(OS_CHROMEOS)
1225   ASSERT_EQ(0u, warnings.size());
1226 #else
1227   ASSERT_EQ(1u, warnings.size());
1228   EXPECT_EQ("All data on your computer and the websites you visit",
1229             UTF16ToUTF8(warnings[0]));
1230 #endif
1231 }
1232 
TEST(ExtensionTest,WantsFileAccess)1233 TEST(ExtensionTest, WantsFileAccess) {
1234   scoped_refptr<Extension> extension;
1235   GURL file_url("file:///etc/passwd");
1236 
1237   // <all_urls> permission
1238   extension = LoadManifest("permissions", "permissions_all_urls.json");
1239   EXPECT_TRUE(extension->wants_file_access());
1240   EXPECT_FALSE(extension->CanExecuteScriptOnPage(file_url, NULL, NULL));
1241   extension = LoadManifest(
1242       "permissions", "permissions_all_urls.json", Extension::ALLOW_FILE_ACCESS);
1243   EXPECT_TRUE(extension->wants_file_access());
1244   EXPECT_TRUE(extension->CanExecuteScriptOnPage(file_url, NULL, NULL));
1245 
1246   // file:///* permission
1247   extension = LoadManifest("permissions", "permissions_file_scheme.json");
1248   EXPECT_TRUE(extension->wants_file_access());
1249   EXPECT_FALSE(extension->CanExecuteScriptOnPage(file_url, NULL, NULL));
1250   extension = LoadManifest("permissions", "permissions_file_scheme.json",
1251       Extension::ALLOW_FILE_ACCESS);
1252   EXPECT_TRUE(extension->wants_file_access());
1253   EXPECT_TRUE(extension->CanExecuteScriptOnPage(file_url, NULL, NULL));
1254 
1255   // http://* permission
1256   extension = LoadManifest("permissions", "permissions_http_scheme.json");
1257   EXPECT_FALSE(extension->wants_file_access());
1258   EXPECT_FALSE(extension->CanExecuteScriptOnPage(file_url, NULL, NULL));
1259   extension = LoadManifest("permissions", "permissions_http_scheme.json",
1260       Extension::ALLOW_FILE_ACCESS);
1261   EXPECT_FALSE(extension->wants_file_access());
1262   EXPECT_FALSE(extension->CanExecuteScriptOnPage(file_url, NULL, NULL));
1263 
1264   // <all_urls> content script match
1265   extension = LoadManifest("permissions", "content_script_all_urls.json");
1266   EXPECT_TRUE(extension->wants_file_access());
1267   EXPECT_FALSE(extension->CanExecuteScriptOnPage(
1268       file_url, &extension->content_scripts()[0], NULL));
1269   extension = LoadManifest("permissions", "content_script_all_urls.json",
1270       Extension::ALLOW_FILE_ACCESS);
1271   EXPECT_TRUE(extension->wants_file_access());
1272   EXPECT_TRUE(extension->CanExecuteScriptOnPage(
1273       file_url, &extension->content_scripts()[0], NULL));
1274 
1275   // file:///* content script match
1276   extension = LoadManifest("permissions", "content_script_file_scheme.json");
1277   EXPECT_TRUE(extension->wants_file_access());
1278   EXPECT_FALSE(extension->CanExecuteScriptOnPage(
1279       file_url, &extension->content_scripts()[0], NULL));
1280   extension = LoadManifest("permissions", "content_script_file_scheme.json",
1281       Extension::ALLOW_FILE_ACCESS);
1282   EXPECT_TRUE(extension->wants_file_access());
1283   EXPECT_TRUE(extension->CanExecuteScriptOnPage(
1284       file_url, &extension->content_scripts()[0], NULL));
1285 
1286   // http://* content script match
1287   extension = LoadManifest("permissions", "content_script_http_scheme.json");
1288   EXPECT_FALSE(extension->wants_file_access());
1289   EXPECT_FALSE(extension->CanExecuteScriptOnPage(
1290       file_url, &extension->content_scripts()[0], NULL));
1291   extension = LoadManifest("permissions", "content_script_http_scheme.json",
1292       Extension::ALLOW_FILE_ACCESS);
1293   EXPECT_FALSE(extension->wants_file_access());
1294   EXPECT_FALSE(extension->CanExecuteScriptOnPage(
1295       file_url, &extension->content_scripts()[0], NULL));
1296 }
1297 
1298 // Base class for testing the CanExecuteScriptOnPage and CanCaptureVisiblePage
1299 // methods of Extension for extensions with various permissions.
1300 class ExtensionScriptAndCaptureVisibleTest : public testing::Test {
1301  public:
ExtensionScriptAndCaptureVisibleTest()1302   ExtensionScriptAndCaptureVisibleTest() {
1303     PathService::Get(chrome::DIR_TEST_DATA, &dirpath_);
1304   }
1305 
MakeExtension(const std::string & permissions,Extension::Location location)1306   scoped_refptr<Extension> MakeExtension(const std::string& permissions,
1307                                          Extension::Location location) {
1308     // Replace single-quotes with double-quotes in permissions, since JSON
1309     // mandates double-quotes.
1310     std::string munged_permissions = permissions;
1311     ReplaceSubstringsAfterOffset(&munged_permissions, 0, "'", "\"");
1312 
1313     DictionaryValue dictionary;
1314     dictionary.SetString(keys::kName, "permission test");
1315     dictionary.SetString(keys::kVersion, "1");
1316     std::string error;
1317     JSONStringValueSerializer serializer(munged_permissions);
1318     scoped_ptr<Value> permission_value(serializer.Deserialize(NULL, &error));
1319     EXPECT_EQ("", error);
1320     if (!permission_value.get())
1321       return NULL;
1322     EXPECT_TRUE(permission_value->IsType(Value::TYPE_LIST));
1323     dictionary.Set(keys::kPermissions, permission_value.release());
1324 
1325     FilePath dirpath;
1326     PathService::Get(chrome::DIR_TEST_DATA, &dirpath);
1327     dirpath = dirpath.AppendASCII("extensions").AppendASCII("permissions");
1328 
1329     scoped_refptr<Extension> extension =  Extension::Create(
1330         dirpath,
1331         location,
1332         dictionary,
1333         Extension::STRICT_ERROR_CHECKS,
1334         &error);
1335     if (!extension)
1336       VLOG(1) << error;
1337     return extension;
1338   }
1339 
Allowed(const Extension * extension,const GURL & url)1340   bool Allowed(const Extension* extension, const GURL& url) {
1341     return (extension->CanExecuteScriptOnPage(url, NULL, NULL) &&
1342             extension->CanCaptureVisiblePage(url, NULL));
1343   }
1344 
CaptureOnly(const Extension * extension,const GURL & url)1345   bool CaptureOnly(const Extension* extension, const GURL& url) {
1346     return !extension->CanExecuteScriptOnPage(url, NULL, NULL) &&
1347         extension->CanCaptureVisiblePage(url, NULL);
1348   }
1349 
Blocked(const Extension * extension,const GURL & url)1350   bool Blocked(const Extension* extension, const GURL& url) {
1351     return !(extension->CanExecuteScriptOnPage(url, NULL, NULL) ||
1352              extension->CanCaptureVisiblePage(url, NULL));
1353   }
1354 
1355  protected:
1356   FilePath dirpath_;
1357 };
1358 
TEST_F(ExtensionScriptAndCaptureVisibleTest,Permissions)1359 TEST_F(ExtensionScriptAndCaptureVisibleTest, Permissions) {
1360   scoped_refptr<Extension> extension;
1361   // URLs that are "safe" to provide scripting and capture visible tab access
1362   // to if the permissions allow it.
1363   GURL http_url("http://www.google.com");
1364   GURL https_url("https://www.google.com");
1365   GURL file_url("file:///foo/bar");
1366 
1367   // We should allow host permission but not scripting permission for favicon
1368   // urls.
1369   GURL favicon_url("chrome://favicon/http://www.google.com");
1370 
1371   std::string dummy_id =
1372       Extension::GenerateIdForPath(FilePath(FILE_PATH_LITERAL("whatever")));
1373 
1374   // URLs that regular extensions should never get access to.
1375   GURL extension_url("chrome-extension://" + dummy_id);
1376   GURL settings_url("chrome://settings");
1377   GURL about_url("about:flags");
1378 
1379   // Test <all_urls> for regular extensions.
1380   extension = MakeExtension("['tabs','<all_urls>']", Extension::INTERNAL);
1381   EXPECT_TRUE(Allowed(extension, http_url));
1382   EXPECT_TRUE(Allowed(extension, https_url));
1383   EXPECT_TRUE(Blocked(extension, file_url));
1384   EXPECT_TRUE(Blocked(extension, settings_url));
1385   EXPECT_TRUE(CaptureOnly(extension, favicon_url));
1386   EXPECT_TRUE(Blocked(extension, about_url));
1387   EXPECT_TRUE(Blocked(extension, extension_url));
1388 
1389   EXPECT_FALSE(extension->HasHostPermission(settings_url));
1390   EXPECT_FALSE(extension->HasHostPermission(about_url));
1391   EXPECT_TRUE(extension->HasHostPermission(favicon_url));
1392 
1393   // Test * for scheme, which implies just the http/https schemes.
1394   extension = MakeExtension("['tabs','*://*/']", Extension::INTERNAL);
1395   EXPECT_TRUE(Allowed(extension, http_url));
1396   EXPECT_TRUE(Allowed(extension, https_url));
1397   EXPECT_TRUE(Blocked(extension, settings_url));
1398   EXPECT_TRUE(Blocked(extension, about_url));
1399   EXPECT_TRUE(Blocked(extension, file_url));
1400   EXPECT_TRUE(Blocked(extension, favicon_url));
1401   extension = MakeExtension("['tabs','*://settings/*']", Extension::INTERNAL);
1402   EXPECT_TRUE(Blocked(extension, settings_url));
1403 
1404   // Having chrome://*/ should not work for regular extensions. Note that
1405   // for favicon access, we require the explicit pattern chrome://favicon/*.
1406   extension = MakeExtension("['tabs','chrome://*/']",
1407                             Extension::INTERNAL);
1408   EXPECT_TRUE(extension == NULL);
1409 
1410   // Having chrome://favicon/* should not give you chrome://*
1411   extension = MakeExtension("['tabs','chrome://favicon/*']",
1412                             Extension::INTERNAL);
1413   EXPECT_TRUE(Blocked(extension, settings_url));
1414   EXPECT_TRUE(CaptureOnly(extension, favicon_url));
1415   EXPECT_TRUE(Blocked(extension, about_url));
1416   EXPECT_TRUE(extension->HasHostPermission(favicon_url));
1417 
1418   // Having http://favicon should not give you chrome://favicon
1419   extension = MakeExtension("['tabs', 'http://favicon/']", Extension::INTERNAL);
1420   EXPECT_TRUE(Blocked(extension, settings_url));
1421   EXPECT_TRUE(Blocked(extension, favicon_url));
1422 
1423   // Component extensions with <all_urls> should get everything.
1424   extension = MakeExtension("['tabs','<all_urls>']", Extension::COMPONENT);
1425   EXPECT_TRUE(Allowed(extension, http_url));
1426   EXPECT_TRUE(Allowed(extension, https_url));
1427   EXPECT_TRUE(Allowed(extension, settings_url));
1428   EXPECT_TRUE(Allowed(extension, about_url));
1429   EXPECT_TRUE(Allowed(extension, favicon_url));
1430   EXPECT_TRUE(extension->HasHostPermission(favicon_url));
1431 
1432   // Component extensions should only get access to what they ask for.
1433   extension = MakeExtension("['tabs', 'http://www.google.com/']",
1434                             Extension::COMPONENT);
1435   EXPECT_TRUE(Allowed(extension, http_url));
1436   EXPECT_TRUE(Blocked(extension, https_url));
1437   EXPECT_TRUE(Blocked(extension, file_url));
1438   EXPECT_TRUE(Blocked(extension, settings_url));
1439   EXPECT_TRUE(Blocked(extension, favicon_url));
1440   EXPECT_TRUE(Blocked(extension, about_url));
1441   EXPECT_TRUE(Blocked(extension, extension_url));
1442   EXPECT_FALSE(extension->HasHostPermission(settings_url));
1443 }
1444 
1445 
TEST(ExtensionTest,GetDistinctHostsForDisplay)1446 TEST(ExtensionTest, GetDistinctHostsForDisplay) {
1447   std::vector<std::string> expected;
1448   expected.push_back("www.foo.com");
1449   expected.push_back("www.bar.com");
1450   expected.push_back("www.baz.com");
1451   URLPatternList actual;
1452 
1453   {
1454     SCOPED_TRACE("no dupes");
1455 
1456     // Simple list with no dupes.
1457     actual.push_back(
1458         URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path"));
1459     actual.push_back(
1460         URLPattern(URLPattern::SCHEME_HTTP, "http://www.bar.com/path"));
1461     actual.push_back(
1462         URLPattern(URLPattern::SCHEME_HTTP, "http://www.baz.com/path"));
1463     CompareLists(expected,
1464                  Extension::GetDistinctHostsForDisplay(actual));
1465   }
1466 
1467   {
1468     SCOPED_TRACE("two dupes");
1469 
1470     // Add some dupes.
1471     actual.push_back(
1472         URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path"));
1473     actual.push_back(
1474         URLPattern(URLPattern::SCHEME_HTTP, "http://www.baz.com/path"));
1475     CompareLists(expected,
1476                  Extension::GetDistinctHostsForDisplay(actual));
1477   }
1478 
1479   {
1480     SCOPED_TRACE("schemes differ");
1481 
1482     // Add a pattern that differs only by scheme. This should be filtered out.
1483     actual.push_back(
1484         URLPattern(URLPattern::SCHEME_HTTPS, "https://www.bar.com/path"));
1485     CompareLists(expected,
1486                  Extension::GetDistinctHostsForDisplay(actual));
1487   }
1488 
1489   {
1490     SCOPED_TRACE("paths differ");
1491 
1492     // Add some dupes by path.
1493     actual.push_back(
1494         URLPattern(URLPattern::SCHEME_HTTP, "http://www.bar.com/pathypath"));
1495     CompareLists(expected,
1496                  Extension::GetDistinctHostsForDisplay(actual));
1497   }
1498 
1499   {
1500     SCOPED_TRACE("subdomains differ");
1501 
1502     // We don't do anything special for subdomains.
1503     actual.push_back(
1504         URLPattern(URLPattern::SCHEME_HTTP, "http://monkey.www.bar.com/path"));
1505     actual.push_back(
1506         URLPattern(URLPattern::SCHEME_HTTP, "http://bar.com/path"));
1507 
1508     expected.push_back("monkey.www.bar.com");
1509     expected.push_back("bar.com");
1510 
1511     CompareLists(expected,
1512                  Extension::GetDistinctHostsForDisplay(actual));
1513   }
1514 
1515   {
1516     SCOPED_TRACE("RCDs differ");
1517 
1518     // Now test for RCD uniquing.
1519     actual.push_back(
1520         URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path"));
1521     actual.push_back(
1522         URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path"));
1523     actual.push_back(
1524         URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.de/path"));
1525     actual.push_back(
1526         URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca.us/path"));
1527     actual.push_back(
1528         URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.net/path"));
1529     actual.push_back(
1530         URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com.my/path"));
1531 
1532     // This is an unknown RCD, which shouldn't be uniqued out.
1533     actual.push_back(
1534         URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.xyzzy/path"));
1535     // But it should only occur once.
1536     actual.push_back(
1537         URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.xyzzy/path"));
1538 
1539     expected.push_back("www.foo.xyzzy");
1540 
1541     CompareLists(expected,
1542                  Extension::GetDistinctHostsForDisplay(actual));
1543   }
1544 
1545   {
1546     SCOPED_TRACE("wildcards");
1547 
1548     actual.push_back(
1549         URLPattern(URLPattern::SCHEME_HTTP, "http://*.google.com/*"));
1550 
1551     expected.push_back("*.google.com");
1552 
1553     CompareLists(expected,
1554                  Extension::GetDistinctHostsForDisplay(actual));
1555   }
1556 }
1557 
TEST(ExtensionTest,GetDistinctHostsForDisplay_ComIsBestRcd)1558 TEST(ExtensionTest, GetDistinctHostsForDisplay_ComIsBestRcd) {
1559   URLPatternList actual;
1560   actual.push_back(
1561       URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path"));
1562   actual.push_back(
1563       URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.org/path"));
1564   actual.push_back(
1565       URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path"));
1566   actual.push_back(
1567       URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.net/path"));
1568   actual.push_back(
1569       URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path"));
1570   actual.push_back(
1571       URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path"));
1572 
1573   std::vector<std::string> expected;
1574   expected.push_back("www.foo.com");
1575 
1576   CompareLists(expected,
1577                  Extension::GetDistinctHostsForDisplay(actual));
1578 }
1579 
TEST(ExtensionTest,GetDistinctHostsForDisplay_NetIs2ndBestRcd)1580 TEST(ExtensionTest, GetDistinctHostsForDisplay_NetIs2ndBestRcd) {
1581   URLPatternList actual;
1582   actual.push_back(
1583       URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path"));
1584   actual.push_back(
1585       URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.org/path"));
1586   actual.push_back(
1587       URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path"));
1588   actual.push_back(
1589       URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.net/path"));
1590   actual.push_back(
1591       URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path"));
1592   // No http://www.foo.com/path
1593 
1594   std::vector<std::string> expected;
1595   expected.push_back("www.foo.net");
1596 
1597   CompareLists(expected,
1598                  Extension::GetDistinctHostsForDisplay(actual));
1599 }
1600 
TEST(ExtensionTest,GetDistinctHostsForDisplay_OrgIs3rdBestRcd)1601 TEST(ExtensionTest, GetDistinctHostsForDisplay_OrgIs3rdBestRcd) {
1602   URLPatternList actual;
1603   actual.push_back(
1604       URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path"));
1605   actual.push_back(
1606       URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.org/path"));
1607   actual.push_back(
1608       URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path"));
1609   // No http://www.foo.net/path
1610   actual.push_back(
1611       URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path"));
1612   // No http://www.foo.com/path
1613 
1614   std::vector<std::string> expected;
1615   expected.push_back("www.foo.org");
1616 
1617   CompareLists(expected,
1618                  Extension::GetDistinctHostsForDisplay(actual));
1619 }
1620 
TEST(ExtensionTest,GetDistinctHostsForDisplay_FirstInListIs4thBestRcd)1621 TEST(ExtensionTest, GetDistinctHostsForDisplay_FirstInListIs4thBestRcd) {
1622   URLPatternList actual;
1623   actual.push_back(
1624       URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path"));
1625   // No http://www.foo.org/path
1626   actual.push_back(
1627       URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path"));
1628   // No http://www.foo.net/path
1629   actual.push_back(
1630       URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path"));
1631   // No http://www.foo.com/path
1632 
1633   std::vector<std::string> expected;
1634   expected.push_back("www.foo.ca");
1635 
1636   CompareLists(expected,
1637                  Extension::GetDistinctHostsForDisplay(actual));
1638 }
1639 
TEST(ExtensionTest,IsElevatedHostList)1640 TEST(ExtensionTest, IsElevatedHostList) {
1641   URLPatternList list1;
1642   URLPatternList list2;
1643 
1644   list1.push_back(
1645       URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/path"));
1646   list1.push_back(
1647       URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/path"));
1648 
1649   // Test that the host order does not matter.
1650   list2.push_back(
1651       URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/path"));
1652   list2.push_back(
1653       URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/path"));
1654 
1655   EXPECT_FALSE(Extension::IsElevatedHostList(list1, list2));
1656   EXPECT_FALSE(Extension::IsElevatedHostList(list2, list1));
1657 
1658   // Test that paths are ignored.
1659   list2.clear();
1660   list2.push_back(
1661       URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/*"));
1662   EXPECT_FALSE(Extension::IsElevatedHostList(list1, list2));
1663   EXPECT_FALSE(Extension::IsElevatedHostList(list2, list1));
1664 
1665   // Test that RCDs are ignored.
1666   list2.clear();
1667   list2.push_back(
1668       URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/*"));
1669   EXPECT_FALSE(Extension::IsElevatedHostList(list1, list2));
1670   EXPECT_FALSE(Extension::IsElevatedHostList(list2, list1));
1671 
1672   // Test that subdomain wildcards are handled properly.
1673   list2.clear();
1674   list2.push_back(
1675       URLPattern(URLPattern::SCHEME_HTTP, "http://*.google.com.hk/*"));
1676   EXPECT_TRUE(Extension::IsElevatedHostList(list1, list2));
1677   //TODO(jstritar): Does not match subdomains properly. http://crbug.com/65337
1678   //EXPECT_FALSE(Extension::IsElevatedHostList(list2, list1));
1679 
1680   // Test that different domains count as different hosts.
1681   list2.clear();
1682   list2.push_back(
1683       URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/path"));
1684   list2.push_back(
1685       URLPattern(URLPattern::SCHEME_HTTP, "http://www.example.org/path"));
1686   EXPECT_TRUE(Extension::IsElevatedHostList(list1, list2));
1687   EXPECT_FALSE(Extension::IsElevatedHostList(list2, list1));
1688 
1689   // Test that different subdomains count as different hosts.
1690   list2.clear();
1691   list2.push_back(
1692       URLPattern(URLPattern::SCHEME_HTTP, "http://mail.google.com/*"));
1693   EXPECT_TRUE(Extension::IsElevatedHostList(list1, list2));
1694   EXPECT_TRUE(Extension::IsElevatedHostList(list2, list1));
1695 }
1696 
TEST(ExtensionTest,GenerateId)1697 TEST(ExtensionTest, GenerateId) {
1698   std::string result;
1699   EXPECT_TRUE(Extension::GenerateId("", &result));
1700 
1701   EXPECT_TRUE(Extension::GenerateId("test", &result));
1702   EXPECT_EQ(result, "jpignaibiiemhngfjkcpokkamffknabf");
1703 
1704   EXPECT_TRUE(Extension::GenerateId("_", &result));
1705   EXPECT_EQ(result, "ncocknphbhhlhkikpnnlmbcnbgdempcd");
1706 
1707   EXPECT_TRUE(Extension::GenerateId(
1708       "this_string_is_longer_than_a_single_sha256_hash_digest", &result));
1709   EXPECT_EQ(result, "jimneklojkjdibfkgiiophfhjhbdgcfi");
1710 }
1711