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
36namespace keys = extension_manifest_keys;
37namespace values = extension_manifest_values;
38namespace errors = extension_manifest_errors;
39
40namespace {
41
42void 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
51static void AddPattern(ExtensionExtent* extent, const std::string& pattern) {
52  int schemes = URLPattern::SCHEME_ALL;
53  extent->AddPattern(URLPattern(schemes, pattern));
54}
55
56}
57
58class 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.
63TEST(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
74TEST(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
118TEST(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
381TEST(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
444TEST(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
493TEST(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
515TEST(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
697TEST(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
709TEST(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
736TEST(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.
796TEST(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
818static 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
843static 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
848TEST(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
912TEST(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
1000TEST(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|.
1053static SkBitmap ResizedCopy(const SkBitmap& source, int size) {
1054  return skia::ImageOperations::Resize(source,
1055                                       skia::ImageOperations::RESIZE_LANCZOS3,
1056                                       size,
1057                                       size);
1058}
1059
1060static bool SizeEquals(const SkBitmap& bitmap, const gfx::Size& size) {
1061  return bitmap.width() == size.width() && bitmap.height() == size.height();
1062}
1063
1064TEST(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").
1139TEST(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).
1169TEST(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
1209TEST(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
1218TEST(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
1233TEST(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.
1300class ExtensionScriptAndCaptureVisibleTest : public testing::Test {
1301 public:
1302  ExtensionScriptAndCaptureVisibleTest() {
1303    PathService::Get(chrome::DIR_TEST_DATA, &dirpath_);
1304  }
1305
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
1340  bool Allowed(const Extension* extension, const GURL& url) {
1341    return (extension->CanExecuteScriptOnPage(url, NULL, NULL) &&
1342            extension->CanCaptureVisiblePage(url, NULL));
1343  }
1344
1345  bool CaptureOnly(const Extension* extension, const GURL& url) {
1346    return !extension->CanExecuteScriptOnPage(url, NULL, NULL) &&
1347        extension->CanCaptureVisiblePage(url, NULL);
1348  }
1349
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
1359TEST_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
1446TEST(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
1558TEST(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
1580TEST(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
1601TEST(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
1621TEST(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
1640TEST(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
1697TEST(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