extension_unittest.cc revision dc0f95d653279beabeb9817299e2902918ba123e
1// Copyright (c) 2010 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/json_value_serializer.h"
25#include "chrome/common/url_constants.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}
71
72
73// Please don't put any more manifest tests here!!
74// Move them to extension_manifest_unittest.cc instead and make them use the
75// more data-driven style there instead.
76// Bug: http://crbug.com/38462
77
78
79TEST(ExtensionTest, InitFromValueInvalid) {
80#if defined(OS_WIN)
81  FilePath path(FILE_PATH_LITERAL("c:\\foo"));
82#elif defined(OS_POSIX)
83  FilePath path(FILE_PATH_LITERAL("/foo"));
84#endif
85  scoped_refptr<Extension> extension_ptr(new Extension(path,
86                                                       Extension::INVALID));
87  Extension& extension = *extension_ptr;
88  int error_code = 0;
89  std::string error;
90
91  // Start with a valid extension manifest
92  FilePath extensions_path;
93  ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &extensions_path));
94  extensions_path = extensions_path.AppendASCII("extensions")
95      .AppendASCII("good")
96      .AppendASCII("Extensions")
97      .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
98      .AppendASCII("1.0.0.0")
99      .Append(Extension::kManifestFilename);
100
101  JSONFileValueSerializer serializer(extensions_path);
102  scoped_ptr<DictionaryValue> valid_value(
103      static_cast<DictionaryValue*>(serializer.Deserialize(&error_code,
104                                                           &error)));
105  EXPECT_EQ("", error);
106  EXPECT_EQ(0, error_code);
107  ASSERT_TRUE(valid_value.get());
108  ASSERT_TRUE(extension.InitFromValue(*valid_value, true, false, &error));
109  ASSERT_EQ("", error);
110  EXPECT_EQ("en_US", extension.default_locale());
111
112  scoped_ptr<DictionaryValue> input_value;
113
114  // Test missing and invalid versions
115  input_value.reset(valid_value->DeepCopy());
116  input_value->Remove(keys::kVersion, NULL);
117  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
118  EXPECT_EQ(errors::kInvalidVersion, error);
119
120  input_value->SetInteger(keys::kVersion, 42);
121  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
122  EXPECT_EQ(errors::kInvalidVersion, error);
123
124  // Test missing and invalid names.
125  input_value.reset(valid_value->DeepCopy());
126  input_value->Remove(keys::kName, NULL);
127  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
128  EXPECT_EQ(errors::kInvalidName, error);
129
130  input_value->SetInteger(keys::kName, 42);
131  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
132  EXPECT_EQ(errors::kInvalidName, error);
133
134  // Test invalid description
135  input_value.reset(valid_value->DeepCopy());
136  input_value->SetInteger(keys::kDescription, 42);
137  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
138  EXPECT_EQ(errors::kInvalidDescription, error);
139
140  // Test invalid icons
141  input_value.reset(valid_value->DeepCopy());
142  input_value->SetInteger(keys::kIcons, 42);
143  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
144  EXPECT_EQ(errors::kInvalidIcons, error);
145
146  // Test invalid icon paths
147  input_value.reset(valid_value->DeepCopy());
148  DictionaryValue* icons = NULL;
149  input_value->GetDictionary(keys::kIcons, &icons);
150  ASSERT_FALSE(NULL == icons);
151  icons->SetInteger(base::IntToString(128), 42);
152  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
153  EXPECT_TRUE(MatchPattern(error, errors::kInvalidIconPath));
154
155  // Test invalid user scripts list
156  input_value.reset(valid_value->DeepCopy());
157  input_value->SetInteger(keys::kContentScripts, 42);
158  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
159  EXPECT_EQ(errors::kInvalidContentScriptsList, error);
160
161  // Test invalid user script item
162  input_value.reset(valid_value->DeepCopy());
163  ListValue* content_scripts = NULL;
164  input_value->GetList(keys::kContentScripts, &content_scripts);
165  ASSERT_FALSE(NULL == content_scripts);
166  content_scripts->Set(0, Value::CreateIntegerValue(42));
167  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
168  EXPECT_TRUE(MatchPattern(error, errors::kInvalidContentScript));
169
170  // Test missing and invalid matches array
171  input_value.reset(valid_value->DeepCopy());
172  input_value->GetList(keys::kContentScripts, &content_scripts);
173  DictionaryValue* user_script = NULL;
174  content_scripts->GetDictionary(0, &user_script);
175  user_script->Remove(keys::kMatches, NULL);
176  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
177  EXPECT_TRUE(MatchPattern(error, errors::kInvalidMatches));
178
179  user_script->Set(keys::kMatches, Value::CreateIntegerValue(42));
180  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
181  EXPECT_TRUE(MatchPattern(error, errors::kInvalidMatches));
182
183  ListValue* matches = new ListValue;
184  user_script->Set(keys::kMatches, matches);
185  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
186  EXPECT_TRUE(MatchPattern(error, errors::kInvalidMatchCount));
187
188  // Test invalid match element
189  matches->Set(0, Value::CreateIntegerValue(42));
190  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
191  EXPECT_TRUE(MatchPattern(error, errors::kInvalidMatch));
192
193  matches->Set(0, Value::CreateStringValue("chrome://*/*"));
194  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
195  EXPECT_TRUE(MatchPattern(error, errors::kInvalidMatch));
196
197  // Test missing and invalid files array
198  input_value.reset(valid_value->DeepCopy());
199  input_value->GetList(keys::kContentScripts, &content_scripts);
200  content_scripts->GetDictionary(0, &user_script);
201  user_script->Remove(keys::kJs, NULL);
202  user_script->Remove(keys::kCss, NULL);
203  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
204  EXPECT_TRUE(MatchPattern(error, errors::kMissingFile));
205
206  user_script->Set(keys::kJs, Value::CreateIntegerValue(42));
207  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
208  EXPECT_TRUE(MatchPattern(error, errors::kInvalidJsList));
209
210  user_script->Set(keys::kCss, new ListValue);
211  user_script->Set(keys::kJs, new ListValue);
212  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
213  EXPECT_TRUE(MatchPattern(error, errors::kMissingFile));
214  user_script->Remove(keys::kCss, NULL);
215
216  ListValue* files = new ListValue;
217  user_script->Set(keys::kJs, files);
218  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
219  EXPECT_TRUE(MatchPattern(error, errors::kMissingFile));
220
221  // Test invalid file element
222  files->Set(0, Value::CreateIntegerValue(42));
223  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
224  EXPECT_TRUE(MatchPattern(error, errors::kInvalidJs));
225
226  user_script->Remove(keys::kJs, NULL);
227  // Test the css element
228  user_script->Set(keys::kCss, Value::CreateIntegerValue(42));
229  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
230  EXPECT_TRUE(MatchPattern(error, errors::kInvalidCssList));
231
232  // Test invalid file element
233  ListValue* css_files = new ListValue;
234  user_script->Set(keys::kCss, css_files);
235  css_files->Set(0, Value::CreateIntegerValue(42));
236  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
237  EXPECT_TRUE(MatchPattern(error, errors::kInvalidCss));
238
239  // Test missing and invalid permissions array
240  input_value.reset(valid_value->DeepCopy());
241  EXPECT_TRUE(extension.InitFromValue(*input_value, true, false, &error));
242
243  ListValue* permissions = NULL;
244  input_value->GetList(keys::kPermissions, &permissions);
245  ASSERT_FALSE(NULL == permissions);
246
247  permissions = new ListValue;
248  input_value->Set(keys::kPermissions, permissions);
249  EXPECT_TRUE(extension.InitFromValue(*input_value, true, false, &error));
250
251  input_value->Set(keys::kPermissions, Value::CreateIntegerValue(9));
252  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
253  EXPECT_TRUE(MatchPattern(error, errors::kInvalidPermissions));
254
255  input_value.reset(valid_value->DeepCopy());
256  input_value->GetList(keys::kPermissions, &permissions);
257  permissions->Set(0, Value::CreateIntegerValue(24));
258  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
259  EXPECT_TRUE(MatchPattern(error, errors::kInvalidPermission));
260
261  // We allow unknown API permissions, so this will be valid until we better
262  // distinguish between API and host permissions.
263  permissions->Set(0, Value::CreateStringValue("www.google.com"));
264  EXPECT_TRUE(extension.InitFromValue(*input_value, true, false, &error));
265
266  // Multiple page actions are not allowed.
267  input_value.reset(valid_value->DeepCopy());
268  DictionaryValue* action = new DictionaryValue;
269  action->SetString(keys::kPageActionId, "MyExtensionActionId");
270  action->SetString(keys::kName, "MyExtensionActionName");
271  ListValue* action_list = new ListValue;
272  action_list->Append(action->DeepCopy());
273  action_list->Append(action);
274  input_value->Set(keys::kPageActions, action_list);
275  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
276  EXPECT_STREQ(errors::kInvalidPageActionsListSize, error.c_str());
277
278  // Test invalid options page url.
279  input_value.reset(valid_value->DeepCopy());
280  input_value->Set(keys::kOptionsPage, Value::CreateNullValue());
281  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
282  EXPECT_TRUE(MatchPattern(error, errors::kInvalidOptionsPage));
283
284  // Test invalid/empty default locale.
285  input_value.reset(valid_value->DeepCopy());
286  input_value->Set(keys::kDefaultLocale, Value::CreateIntegerValue(5));
287  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
288  EXPECT_TRUE(MatchPattern(error, errors::kInvalidDefaultLocale));
289
290  input_value->Set(keys::kDefaultLocale, Value::CreateStringValue(""));
291  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
292  EXPECT_TRUE(MatchPattern(error, errors::kInvalidDefaultLocale));
293
294  // Test invalid minimum_chrome_version.
295  input_value.reset(valid_value->DeepCopy());
296  input_value->Set(keys::kMinimumChromeVersion, Value::CreateIntegerValue(42));
297  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
298  EXPECT_TRUE(MatchPattern(error, errors::kInvalidMinimumChromeVersion));
299
300#if !defined(OS_MACOSX)
301  // TODO(aa): The version isn't stamped into the unit test binary on mac.
302  input_value->Set(keys::kMinimumChromeVersion,
303                   Value::CreateStringValue("88.8"));
304  EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error));
305  EXPECT_TRUE(MatchPattern(error, errors::kChromeVersionTooLow));
306#endif
307}
308
309TEST(ExtensionTest, InitFromValueValid) {
310#if defined(OS_WIN)
311  FilePath path(FILE_PATH_LITERAL("C:\\foo"));
312#elif defined(OS_POSIX)
313  FilePath path(FILE_PATH_LITERAL("/foo"));
314#endif
315  scoped_refptr<Extension> extension_ptr(new Extension(path,
316                                                       Extension::INVALID));
317  Extension& extension = *extension_ptr;
318  std::string error;
319  DictionaryValue input_value;
320
321  // Test minimal extension
322  input_value.SetString(keys::kVersion, "1.0.0.0");
323  input_value.SetString(keys::kName, "my extension");
324
325  EXPECT_TRUE(extension.InitFromValue(input_value, false, false, &error));
326  EXPECT_EQ("", error);
327  EXPECT_TRUE(Extension::IdIsValid(extension.id()));
328  EXPECT_EQ("1.0.0.0", extension.VersionString());
329  EXPECT_EQ("my extension", extension.name());
330  EXPECT_EQ(extension.id(), extension.url().host());
331  EXPECT_EQ(path.value(), extension.path().value());
332
333  // Test permissions scheme.
334  ListValue* permissions = new ListValue;
335  permissions->Set(0, Value::CreateStringValue("file:///C:/foo.txt"));
336  input_value.Set(keys::kPermissions, permissions);
337
338  // We allow unknown API permissions, so this will be valid until we better
339  // distinguish between API and host permissions.
340  EXPECT_TRUE(extension.InitFromValue(input_value, false, false, &error));
341  input_value.Remove(keys::kPermissions, NULL);
342
343  // Test with an options page.
344  input_value.SetString(keys::kOptionsPage, "options.html");
345  EXPECT_TRUE(extension.InitFromValue(input_value, false, false, &error));
346  EXPECT_EQ("", error);
347  EXPECT_EQ("chrome-extension", extension.options_url().scheme());
348  EXPECT_EQ("/options.html", extension.options_url().path());
349
350  // Test that an empty list of page actions does not stop a browser action
351  // from being loaded.
352  ListValue* empty_list = new ListValue;
353  input_value.Set(keys::kPageActions, empty_list);
354  EXPECT_TRUE(extension.InitFromValue(input_value, false, false, &error));
355  EXPECT_EQ("", error);
356
357#if !defined(OS_MACOSX)
358  // TODO(aa): The version isn't stamped into the unit test binary on mac.
359  // Test with a minimum_chrome_version.
360  input_value.SetString(keys::kMinimumChromeVersion, "1.0");
361  EXPECT_TRUE(extension.InitFromValue(input_value, false, false, &error));
362  EXPECT_EQ("", error);
363  // The minimum chrome version is not stored in the Extension object.
364#endif
365}
366
367TEST(ExtensionTest, InitFromValueValidNameInRTL) {
368#if defined(TOOLKIT_GTK)
369  GtkTextDirection gtk_dir = gtk_widget_get_default_direction();
370  gtk_widget_set_default_direction(GTK_TEXT_DIR_RTL);
371#else
372  std::string locale = l10n_util::GetApplicationLocale("");
373  base::i18n::SetICUDefaultLocale("he");
374#endif
375
376#if defined(OS_WIN)
377  FilePath path(FILE_PATH_LITERAL("C:\\foo"));
378#elif defined(OS_POSIX)
379  FilePath path(FILE_PATH_LITERAL("/foo"));
380#endif
381  scoped_refptr<Extension> extension_ptr(new Extension(path,
382                                                       Extension::INVALID));
383  Extension& extension = *extension_ptr;
384  std::string error;
385  DictionaryValue input_value;
386
387  input_value.SetString(keys::kVersion, "1.0.0.0");
388  // No strong RTL characters in name.
389  std::wstring name(L"Dictionary (by Google)");
390  input_value.SetString(keys::kName, WideToUTF16Hack(name));
391  EXPECT_TRUE(extension.InitFromValue(input_value, false, false, &error));
392  EXPECT_EQ("", error);
393  std::wstring localized_name(name);
394  base::i18n::AdjustStringForLocaleDirection(&localized_name);
395  EXPECT_EQ(localized_name, UTF8ToWide(extension.name()));
396
397  // Strong RTL characters in name.
398  name = L"Dictionary (\x05D1\x05D2"L" Google)";
399  input_value.SetString(keys::kName, WideToUTF16Hack(name));
400  EXPECT_TRUE(extension.InitFromValue(input_value, false, false, &error));
401  EXPECT_EQ("", error);
402  localized_name = name;
403  base::i18n::AdjustStringForLocaleDirection(&localized_name);
404  EXPECT_EQ(localized_name, UTF8ToWide(extension.name()));
405
406  // Reset locale.
407#if defined(TOOLKIT_GTK)
408  gtk_widget_set_default_direction(gtk_dir);
409#else
410  base::i18n::SetICUDefaultLocale(locale);
411#endif
412}
413
414TEST(ExtensionTest, GetResourceURLAndPath) {
415#if defined(OS_WIN)
416  FilePath path(FILE_PATH_LITERAL("C:\\foo"));
417#elif defined(OS_POSIX)
418  FilePath path(FILE_PATH_LITERAL("/foo"));
419#endif
420  DictionaryValue input_value;
421  input_value.SetString(keys::kVersion, "1.0.0.0");
422  input_value.SetString(keys::kName, "my extension");
423  scoped_refptr<Extension> extension(Extension::Create(
424      path, Extension::INVALID, input_value, false, true, NULL));
425  EXPECT_TRUE(extension.get());
426
427  EXPECT_EQ(extension->url().spec() + "bar/baz.js",
428            Extension::GetResourceURL(extension->url(), "bar/baz.js").spec());
429  EXPECT_EQ(extension->url().spec() + "baz.js",
430            Extension::GetResourceURL(extension->url(),
431                                      "bar/../baz.js").spec());
432  EXPECT_EQ(extension->url().spec() + "baz.js",
433            Extension::GetResourceURL(extension->url(), "../baz.js").spec());
434}
435
436TEST(ExtensionTest, LoadPageActionHelper) {
437#if defined(OS_WIN)
438    FilePath path(base::StringPrintf(L"c:\\extension"));
439#else
440    FilePath path(base::StringPrintf("/extension"));
441#endif
442  scoped_refptr<Extension> extension_ptr(new Extension(path,
443                                                       Extension::INVALID));
444  Extension& extension = *extension_ptr;
445  std::string error_msg;
446  scoped_ptr<ExtensionAction> action;
447  DictionaryValue input;
448
449  // First try with an empty dictionary.
450  action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
451  ASSERT_TRUE(action != NULL);
452  ASSERT_TRUE(error_msg.empty());
453
454  // Now setup some values to use in the action.
455  const std::string id("MyExtensionActionId");
456  const std::string name("MyExtensionActionName");
457  std::string img1("image1.png");
458  std::string img2("image2.png");
459
460  // Add the dictionary for the contextual action.
461  input.SetString(keys::kPageActionId, id);
462  input.SetString(keys::kName, name);
463  ListValue* icons = new ListValue;
464  icons->Set(0, Value::CreateStringValue(img1));
465  icons->Set(1, Value::CreateStringValue(img2));
466  input.Set(keys::kPageActionIcons, icons);
467
468  // Parse and read back the values from the object.
469  action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
470  ASSERT_TRUE(NULL != action.get());
471  ASSERT_TRUE(error_msg.empty());
472  ASSERT_EQ(id, action->id());
473  // No title, so fall back to name.
474  ASSERT_EQ(name, action->GetTitle(1));
475  ASSERT_EQ(2u, action->icon_paths()->size());
476  ASSERT_EQ(img1, (*action->icon_paths())[0]);
477  ASSERT_EQ(img2, (*action->icon_paths())[1]);
478
479  // Explicitly set the same type and parse again.
480  input.SetString(keys::kType, values::kPageActionTypeTab);
481  action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
482  ASSERT_TRUE(NULL != action.get());
483  ASSERT_TRUE(error_msg.empty());
484
485  // Make a deep copy of the input and remove one key at a time and see if we
486  // get the right error.
487  scoped_ptr<DictionaryValue> copy;
488
489  // First remove id key.
490  copy.reset(input.DeepCopy());
491  copy->Remove(keys::kPageActionId, NULL);
492  action.reset(extension.LoadExtensionActionHelper(copy.get(), &error_msg));
493  ASSERT_TRUE(NULL != action.get());
494
495  // Then remove the name key. It's optional, so no error.
496  copy.reset(input.DeepCopy());
497  copy->Remove(keys::kName, NULL);
498  action.reset(extension.LoadExtensionActionHelper(copy.get(), &error_msg));
499  ASSERT_TRUE(NULL != action.get());
500  ASSERT_TRUE(action->GetTitle(1).empty());
501  ASSERT_TRUE(error_msg.empty());
502
503  // Then remove the icon paths key.
504  copy.reset(input.DeepCopy());
505  copy->Remove(keys::kPageActionIcons, NULL);
506  action.reset(extension.LoadExtensionActionHelper(copy.get(), &error_msg));
507  ASSERT_TRUE(NULL != action.get());
508  error_msg = "";
509
510  // Now test that we can parse the new format for page actions.
511
512  // Now setup some values to use in the page action.
513  const std::string kTitle("MyExtensionActionTitle");
514  const std::string kIcon("image1.png");
515  const std::string kPopupHtmlFile("a_popup.html");
516
517  // Add the dictionary for the contextual action.
518  input.Clear();
519  input.SetString(keys::kPageActionDefaultTitle, kTitle);
520  input.SetString(keys::kPageActionDefaultIcon, kIcon);
521
522  // Parse and read back the values from the object.
523  action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
524  ASSERT_TRUE(action.get());
525  ASSERT_TRUE(error_msg.empty());
526  ASSERT_EQ(kTitle, action->GetTitle(1));
527  ASSERT_EQ(0u, action->icon_paths()->size());
528
529  // Invalid title should give an error even with a valid name.
530  input.Clear();
531  input.SetInteger(keys::kPageActionDefaultTitle, 42);
532  input.SetString(keys::kName, name);
533  action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
534  ASSERT_TRUE(NULL == action.get());
535  ASSERT_STREQ(errors::kInvalidPageActionDefaultTitle, error_msg.c_str());
536  error_msg = "";
537
538  // Invalid name should give an error only with no title.
539  input.SetString(keys::kPageActionDefaultTitle, kTitle);
540  input.SetInteger(keys::kName, 123);
541  action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
542  ASSERT_TRUE(NULL != action.get());
543  ASSERT_EQ(kTitle, action->GetTitle(1));
544  ASSERT_TRUE(error_msg.empty());
545
546  input.Remove(keys::kPageActionDefaultTitle, NULL);
547  action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
548  ASSERT_TRUE(NULL == action.get());
549  ASSERT_STREQ(errors::kInvalidPageActionName, error_msg.c_str());
550  error_msg = "";
551
552  // Test that keys "popup" and "default_popup" both work, but can not
553  // be used at the same time.
554  input.Clear();
555  input.SetString(keys::kPageActionDefaultTitle, kTitle);
556  input.SetString(keys::kPageActionDefaultIcon, kIcon);
557
558  // LoadExtensionActionHelper expects the extension member |extension_url|
559  // to be set.
560  extension.extension_url_ =
561      GURL(std::string(chrome::kExtensionScheme) +
562           chrome::kStandardSchemeSeparator +
563           "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/");
564
565  // Add key "popup", expect success.
566  input.SetString(keys::kPageActionPopup, kPopupHtmlFile);
567  action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
568  ASSERT_TRUE(NULL != action.get());
569  ASSERT_TRUE(error_msg.empty());
570  ASSERT_STREQ(
571      extension.url().Resolve(kPopupHtmlFile).spec().c_str(),
572      action->GetPopupUrl(ExtensionAction::kDefaultTabId).spec().c_str());
573
574  // Add key "default_popup", expect failure.
575  input.SetString(keys::kPageActionDefaultPopup, kPopupHtmlFile);
576  action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
577  ASSERT_TRUE(NULL == action.get());
578  ASSERT_STREQ(
579      ExtensionErrorUtils::FormatErrorMessage(
580          errors::kInvalidPageActionOldAndNewKeys,
581          keys::kPageActionDefaultPopup,
582          keys::kPageActionPopup).c_str(),
583      error_msg.c_str());
584  error_msg = "";
585
586  // Remove key "popup", expect success.
587  input.Remove(keys::kPageActionPopup, NULL);
588  action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
589  ASSERT_TRUE(NULL != action.get());
590  ASSERT_TRUE(error_msg.empty());
591  ASSERT_STREQ(
592      extension.url().Resolve(kPopupHtmlFile).spec().c_str(),
593      action->GetPopupUrl(ExtensionAction::kDefaultTabId).spec().c_str());
594
595  // Setting default_popup to "" is the same as having no popup.
596  input.Remove(keys::kPageActionDefaultPopup, NULL);
597  input.SetString(keys::kPageActionDefaultPopup, "");
598  action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
599  ASSERT_TRUE(NULL != action.get());
600  ASSERT_TRUE(error_msg.empty());
601  EXPECT_FALSE(action->HasPopup(ExtensionAction::kDefaultTabId));
602  ASSERT_STREQ(
603      "",
604      action->GetPopupUrl(ExtensionAction::kDefaultTabId).spec().c_str());
605
606  // Setting popup to "" is the same as having no popup.
607  input.Remove(keys::kPageActionDefaultPopup, NULL);
608  input.SetString(keys::kPageActionPopup, "");
609  action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
610  ASSERT_TRUE(NULL != action.get());
611  ASSERT_TRUE(error_msg.empty());
612  EXPECT_FALSE(action->HasPopup(ExtensionAction::kDefaultTabId));
613  ASSERT_STREQ(
614      "",
615      action->GetPopupUrl(ExtensionAction::kDefaultTabId).spec().c_str());
616}
617
618TEST(ExtensionTest, IdIsValid) {
619  EXPECT_TRUE(Extension::IdIsValid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
620  EXPECT_TRUE(Extension::IdIsValid("pppppppppppppppppppppppppppppppp"));
621  EXPECT_TRUE(Extension::IdIsValid("abcdefghijklmnopabcdefghijklmnop"));
622  EXPECT_TRUE(Extension::IdIsValid("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"));
623  EXPECT_FALSE(Extension::IdIsValid("abcdefghijklmnopabcdefghijklmno"));
624  EXPECT_FALSE(Extension::IdIsValid("abcdefghijklmnopabcdefghijklmnopa"));
625  EXPECT_FALSE(Extension::IdIsValid("0123456789abcdef0123456789abcdef"));
626  EXPECT_FALSE(Extension::IdIsValid("abcdefghijklmnopabcdefghijklmnoq"));
627  EXPECT_FALSE(Extension::IdIsValid("abcdefghijklmnopabcdefghijklmno0"));
628}
629
630TEST(ExtensionTest, GenerateID) {
631  const uint8 public_key_info[] = {
632    0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
633    0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81,
634    0x89, 0x02, 0x81, 0x81, 0x00, 0xb8, 0x7f, 0x2b, 0x20, 0xdc, 0x7c, 0x9b,
635    0x0c, 0xdc, 0x51, 0x61, 0x99, 0x0d, 0x36, 0x0f, 0xd4, 0x66, 0x88, 0x08,
636    0x55, 0x84, 0xd5, 0x3a, 0xbf, 0x2b, 0xa4, 0x64, 0x85, 0x7b, 0x0c, 0x04,
637    0x13, 0x3f, 0x8d, 0xf4, 0xbc, 0x38, 0x0d, 0x49, 0xfe, 0x6b, 0xc4, 0x5a,
638    0xb0, 0x40, 0x53, 0x3a, 0xd7, 0x66, 0x09, 0x0f, 0x9e, 0x36, 0x74, 0x30,
639    0xda, 0x8a, 0x31, 0x4f, 0x1f, 0x14, 0x50, 0xd7, 0xc7, 0x20, 0x94, 0x17,
640    0xde, 0x4e, 0xb9, 0x57, 0x5e, 0x7e, 0x0a, 0xe5, 0xb2, 0x65, 0x7a, 0x89,
641    0x4e, 0xb6, 0x47, 0xff, 0x1c, 0xbd, 0xb7, 0x38, 0x13, 0xaf, 0x47, 0x85,
642    0x84, 0x32, 0x33, 0xf3, 0x17, 0x49, 0xbf, 0xe9, 0x96, 0xd0, 0xd6, 0x14,
643    0x6f, 0x13, 0x8d, 0xc5, 0xfc, 0x2c, 0x72, 0xba, 0xac, 0xea, 0x7e, 0x18,
644    0x53, 0x56, 0xa6, 0x83, 0xa2, 0xce, 0x93, 0x93, 0xe7, 0x1f, 0x0f, 0xe6,
645    0x0f, 0x02, 0x03, 0x01, 0x00, 0x01
646  };
647
648  std::string extension_id;
649  EXPECT_TRUE(
650      Extension::GenerateId(
651          std::string(reinterpret_cast<const char*>(&public_key_info[0]),
652                      arraysize(public_key_info)),
653          &extension_id));
654  EXPECT_EQ("melddjfinppjdikinhbgehiennejpfhp", extension_id);
655}
656
657TEST(ExtensionTest, UpdateUrls) {
658  // Test several valid update urls
659  std::vector<std::string> valid;
660  valid.push_back("http://test.com");
661  valid.push_back("http://test.com/");
662  valid.push_back("http://test.com/update");
663  valid.push_back("http://test.com/update?check=true");
664  for (size_t i = 0; i < valid.size(); i++) {
665    GURL url(valid[i]);
666    EXPECT_TRUE(url.is_valid());
667
668    DictionaryValue input_value;
669#if defined(OS_WIN)
670    // (Why %Iu below?  This is the single file in the whole code base that
671    // might make use of a WidePRIuS; let's not encourage any more.)
672    FilePath path(base::StringPrintf(L"c:\\extension%Iu", i));
673#else
674    FilePath path(base::StringPrintf("/extension%" PRIuS, i));
675#endif
676    std::string error;
677
678    input_value.SetString(keys::kVersion, "1.0");
679    input_value.SetString(keys::kName, "Test");
680    input_value.SetString(keys::kUpdateURL, url.spec());
681
682    scoped_refptr<Extension> extension(Extension::Create(
683        path, Extension::INVALID, input_value, false, true, &error));
684    EXPECT_TRUE(extension.get()) << error;
685  }
686
687  // Test some invalid update urls
688  std::vector<std::string> invalid;
689  invalid.push_back("");
690  invalid.push_back("test.com");
691  valid.push_back("http://test.com/update#whatever");
692  for (size_t i = 0; i < invalid.size(); i++) {
693    DictionaryValue input_value;
694#if defined(OS_WIN)
695    // (Why %Iu below?  This is the single file in the whole code base that
696    // might make use of a WidePRIuS; let's not encourage any more.)
697    FilePath path(base::StringPrintf(L"c:\\extension%Iu", i));
698#else
699    FilePath path(base::StringPrintf("/extension%" PRIuS, i));
700#endif
701    std::string error;
702    input_value.SetString(keys::kVersion, "1.0");
703    input_value.SetString(keys::kName, "Test");
704    input_value.SetString(keys::kUpdateURL, invalid[i]);
705
706    scoped_refptr<Extension> extension(Extension::Create(
707        path, Extension::INVALID, input_value, false, true, &error));
708    EXPECT_FALSE(extension.get());
709    EXPECT_TRUE(MatchPattern(error, errors::kInvalidUpdateURL));
710  }
711}
712
713// This test ensures that the mimetype sniffing code stays in sync with the
714// actual crx files that we test other parts of the system with.
715TEST(ExtensionTest, MimeTypeSniffing) {
716  FilePath path;
717  ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &path));
718  path = path.AppendASCII("extensions").AppendASCII("good.crx");
719
720  std::string data;
721  ASSERT_TRUE(file_util::ReadFileToString(path, &data));
722
723  std::string result;
724  EXPECT_TRUE(net::SniffMimeType(data.c_str(), data.size(),
725              GURL("http://www.example.com/foo.crx"), "", &result));
726  EXPECT_EQ(std::string(Extension::kMimeType), result);
727
728  data.clear();
729  result.clear();
730  path = path.DirName().AppendASCII("bad_magic.crx");
731  ASSERT_TRUE(file_util::ReadFileToString(path, &data));
732  EXPECT_TRUE(net::SniffMimeType(data.c_str(), data.size(),
733              GURL("http://www.example.com/foo.crx"), "", &result));
734  EXPECT_EQ("application/octet-stream", result);
735}
736
737static scoped_refptr<Extension> LoadManifest(const std::string& dir,
738                                             const std::string& test_file) {
739  FilePath path;
740  PathService::Get(chrome::DIR_TEST_DATA, &path);
741  path = path.AppendASCII("extensions")
742             .AppendASCII(dir)
743             .AppendASCII(test_file);
744
745  JSONFileValueSerializer serializer(path);
746  std::string error;
747  scoped_ptr<Value> result(serializer.Deserialize(NULL, &error));
748  if (!result.get()) {
749    EXPECT_EQ("", error);
750    return NULL;
751  }
752
753  scoped_refptr<Extension> extension = Extension::Create(
754      path.DirName(), Extension::INVALID,
755      *static_cast<DictionaryValue*>(result.get()), false, true, &error);
756  EXPECT_TRUE(extension) << error;
757  return extension;
758}
759
760TEST(ExtensionTest, EffectiveHostPermissions) {
761  scoped_refptr<Extension> extension;
762  ExtensionExtent hosts;
763
764  extension = LoadManifest("effective_host_permissions", "empty.json");
765  EXPECT_EQ(0u, extension->GetEffectiveHostPermissions().patterns().size());
766  EXPECT_FALSE(hosts.ContainsURL(GURL("http://www.google.com")));
767  EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts());
768
769  extension = LoadManifest("effective_host_permissions", "one_host.json");
770  hosts = extension->GetEffectiveHostPermissions();
771  EXPECT_TRUE(hosts.ContainsURL(GURL("http://www.google.com")));
772  EXPECT_FALSE(hosts.ContainsURL(GURL("https://www.google.com")));
773  EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts());
774
775  extension = LoadManifest("effective_host_permissions",
776                           "one_host_wildcard.json");
777  hosts = extension->GetEffectiveHostPermissions();
778  EXPECT_TRUE(hosts.ContainsURL(GURL("http://google.com")));
779  EXPECT_TRUE(hosts.ContainsURL(GURL("http://foo.google.com")));
780  EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts());
781
782  extension = LoadManifest("effective_host_permissions", "two_hosts.json");
783  hosts = extension->GetEffectiveHostPermissions();
784  EXPECT_TRUE(hosts.ContainsURL(GURL("http://www.google.com")));
785  EXPECT_TRUE(hosts.ContainsURL(GURL("http://www.reddit.com")));
786  EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts());
787
788  extension = LoadManifest("effective_host_permissions",
789                           "https_not_considered.json");
790  hosts = extension->GetEffectiveHostPermissions();
791  EXPECT_TRUE(hosts.ContainsURL(GURL("http://google.com")));
792  EXPECT_TRUE(hosts.ContainsURL(GURL("https://google.com")));
793  EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts());
794
795  extension = LoadManifest("effective_host_permissions",
796                           "two_content_scripts.json");
797  hosts = extension->GetEffectiveHostPermissions();
798  EXPECT_TRUE(hosts.ContainsURL(GURL("http://google.com")));
799  EXPECT_TRUE(hosts.ContainsURL(GURL("http://www.reddit.com")));
800  EXPECT_TRUE(hosts.ContainsURL(GURL("http://news.ycombinator.com")));
801  EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts());
802
803  extension = LoadManifest("effective_host_permissions", "all_hosts.json");
804  hosts = extension->GetEffectiveHostPermissions();
805  EXPECT_TRUE(hosts.ContainsURL(GURL("http://test/")));
806  EXPECT_FALSE(hosts.ContainsURL(GURL("https://test/")));
807  EXPECT_TRUE(hosts.ContainsURL(GURL("http://www.google.com")));
808  EXPECT_TRUE(extension->HasEffectiveAccessToAllHosts());
809
810  extension = LoadManifest("effective_host_permissions", "all_hosts2.json");
811  hosts = extension->GetEffectiveHostPermissions();
812  EXPECT_TRUE(hosts.ContainsURL(GURL("http://test/")));
813  EXPECT_TRUE(hosts.ContainsURL(GURL("http://www.google.com")));
814  EXPECT_TRUE(extension->HasEffectiveAccessToAllHosts());
815
816  extension = LoadManifest("effective_host_permissions", "all_hosts3.json");
817  hosts = extension->GetEffectiveHostPermissions();
818  EXPECT_FALSE(hosts.ContainsURL(GURL("http://test/")));
819  EXPECT_TRUE(hosts.ContainsURL(GURL("https://test/")));
820  EXPECT_TRUE(hosts.ContainsURL(GURL("http://www.google.com")));
821  EXPECT_TRUE(extension->HasEffectiveAccessToAllHosts());
822}
823
824TEST(ExtensionTest, IsPrivilegeIncrease) {
825  const struct {
826    const char* base_name;
827    // Increase these sizes if you have more than 10.
828    const char* granted_apis[10];
829    const char* granted_hosts[10];
830    bool full_access;
831    bool expect_increase;
832  } kTests[] = {
833    { "allhosts1", {NULL}, {"http://*/", NULL}, false,
834      false },  // all -> all
835    { "allhosts2", {NULL}, {"http://*/", NULL}, false,
836      false },  // all -> one
837    { "allhosts3", {NULL}, {NULL}, false, true },  // one -> all
838    { "hosts1", {NULL},
839      {"http://www.google.com/", "http://www.reddit.com/", NULL}, false,
840      false },  // http://a,http://b -> http://a,http://b
841    { "hosts2", {NULL},
842      {"http://www.google.com/", "http://www.reddit.com/", NULL}, false,
843      true },  // http://a,http://b -> https://a,http://*.b
844    { "hosts3", {NULL},
845      {"http://www.google.com/", "http://www.reddit.com/", NULL}, false,
846      false },  // http://a,http://b -> http://a
847    { "hosts4", {NULL},
848      {"http://www.google.com/", NULL}, false,
849      true },  // http://a -> http://a,http://b
850    { "hosts5", {"tabs", "notifications", NULL},
851      {"http://*.example.com/", "http://*.example.com/*",
852       "http://*.example.co.uk/*", "http://*.example.com.au/*",
853       NULL}, false,
854      false },  // http://a,b,c -> http://a,b,c + https://a,b,c
855    { "hosts6", {"tabs", "notifications", NULL},
856      {"http://*.example.com/", "http://*.example.com/*", NULL}, false,
857      false },  // http://a.com -> http://a.com + http://a.co.uk
858    { "permissions1", {"tabs", NULL},
859      {NULL}, false, false },  // tabs -> tabs
860    { "permissions2", {"tabs", NULL},
861      {NULL}, false, true },  // tabs -> tabs,bookmarks
862    { "permissions3", {NULL},
863      {"http://*/*", NULL},
864      false, true },  // http://a -> http://a,tabs
865    { "permissions5", {"bookmarks", NULL},
866      {NULL}, false, true },  // bookmarks -> bookmarks,history
867#if !defined(OS_CHROMEOS)  // plugins aren't allowed in ChromeOS
868    { "permissions4", {NULL},
869      {NULL}, true, false },  // plugin -> plugin,tabs
870    { "plugin1", {NULL},
871      {NULL}, true, false },  // plugin -> plugin
872    { "plugin2", {NULL},
873      {NULL}, true, false },  // plugin -> none
874    { "plugin3", {NULL},
875      {NULL}, false, true },  // none -> plugin
876#endif
877    { "storage", {NULL},
878      {NULL}, false, false },  // none -> storage
879    { "notifications", {NULL},
880      {NULL}, false, false }  // none -> notifications
881  };
882
883  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) {
884    scoped_refptr<Extension> old_extension(
885        LoadManifest("allow_silent_upgrade",
886                     std::string(kTests[i].base_name) + "_old.json"));
887    scoped_refptr<Extension> new_extension(
888        LoadManifest("allow_silent_upgrade",
889                     std::string(kTests[i].base_name) + "_new.json"));
890
891    std::set<std::string> granted_apis;
892    for (size_t j = 0; kTests[i].granted_apis[j] != NULL; ++j)
893      granted_apis.insert(kTests[i].granted_apis[j]);
894
895    ExtensionExtent granted_hosts;
896    for (size_t j = 0; kTests[i].granted_hosts[j] != NULL; ++j)
897      AddPattern(&granted_hosts, kTests[i].granted_hosts[j]);
898
899    EXPECT_TRUE(new_extension.get()) << kTests[i].base_name << "_new.json";
900    if (!new_extension.get())
901      continue;
902
903    EXPECT_EQ(kTests[i].expect_increase,
904              Extension::IsPrivilegeIncrease(kTests[i].full_access,
905                                             granted_apis,
906                                             granted_hosts,
907                                             new_extension.get()))
908        << kTests[i].base_name;
909  }
910}
911
912TEST(ExtensionTest, PermissionMessages) {
913  // Ensure that all permissions that needs to show install UI actually have
914  // strings associated with them.
915
916  std::set<std::string> skip;
917
918  // These are considered "nuisance" or "trivial" permissions that don't need
919  // a prompt.
920  skip.insert(Extension::kContextMenusPermission);
921  skip.insert(Extension::kIdlePermission);
922  skip.insert(Extension::kNotificationPermission);
923  skip.insert(Extension::kUnlimitedStoragePermission);
924  skip.insert(Extension::kContentSettingsPermission);
925
926  // TODO(erikkay) add a string for this permission.
927  skip.insert(Extension::kBackgroundPermission);
928
929  // The cookie permission does nothing unless you have associated host
930  // permissions.
931  skip.insert(Extension::kCookiePermission);
932
933  // The proxy permission is warned as part of host permission checks.
934  skip.insert(Extension::kProxyPermission);
935
936  // If you've turned on the experimental command-line flag, we don't need
937  // to warn you further.
938  skip.insert(Extension::kExperimentalPermission);
939
940  // This is only usable by component extensions.
941  skip.insert(Extension::kWebstorePrivatePermission);
942
943  for (size_t i = 0; i < Extension::kNumPermissions; ++i) {
944    int message_id = Extension::kPermissions[i].message_id;
945    std::string name = Extension::kPermissions[i].name;
946    if (skip.count(name))
947      EXPECT_EQ(0, message_id) << "unexpected message_id for " << name;
948    else
949      EXPECT_NE(0, message_id) << "missing message_id for " << name;
950  }
951}
952
953// Returns a copy of |source| resized to |size| x |size|.
954static SkBitmap ResizedCopy(const SkBitmap& source, int size) {
955  return skia::ImageOperations::Resize(source,
956                                       skia::ImageOperations::RESIZE_LANCZOS3,
957                                       size,
958                                       size);
959}
960
961static bool SizeEquals(const SkBitmap& bitmap, const gfx::Size& size) {
962  return bitmap.width() == size.width() && bitmap.height() == size.height();
963}
964
965TEST(ExtensionTest, ImageCaching) {
966  FilePath path;
967  ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &path));
968  path = path.AppendASCII("extensions");
969
970  // Initialize the Extension.
971  std::string errors;
972  DictionaryValue values;
973  values.SetString(keys::kName, "test");
974  values.SetString(keys::kVersion, "0.1");
975  scoped_refptr<Extension> extension(Extension::Create(
976      path, Extension::INVALID, values, false, true, &errors));
977  ASSERT_TRUE(extension.get());
978
979  // Create an ExtensionResource pointing at an icon.
980  FilePath icon_relative_path(FILE_PATH_LITERAL("icon3.png"));
981  ExtensionResource resource(extension->id(),
982                             extension->path(),
983                             icon_relative_path);
984
985  // Read in the icon file.
986  FilePath icon_absolute_path = extension->path().Append(icon_relative_path);
987  std::string raw_png;
988  ASSERT_TRUE(file_util::ReadFileToString(icon_absolute_path, &raw_png));
989  SkBitmap image;
990  ASSERT_TRUE(gfx::PNGCodec::Decode(
991      reinterpret_cast<const unsigned char*>(raw_png.data()),
992      raw_png.length(),
993      &image));
994
995  // Make sure the icon file is the size we expect.
996  gfx::Size original_size(66, 66);
997  ASSERT_EQ(image.width(), original_size.width());
998  ASSERT_EQ(image.height(), original_size.height());
999
1000  // Create two resized versions at size 16x16 and 24x24.
1001  SkBitmap image16 = ResizedCopy(image, 16);
1002  SkBitmap image24 = ResizedCopy(image, 24);
1003
1004  gfx::Size size16(16, 16);
1005  gfx::Size size24(24, 24);
1006
1007  // Cache the 16x16 copy.
1008  EXPECT_FALSE(extension->HasCachedImage(resource, size16));
1009  extension->SetCachedImage(resource, image16, original_size);
1010  EXPECT_TRUE(extension->HasCachedImage(resource, size16));
1011  EXPECT_TRUE(SizeEquals(extension->GetCachedImage(resource, size16), size16));
1012  EXPECT_FALSE(extension->HasCachedImage(resource, size24));
1013  EXPECT_FALSE(extension->HasCachedImage(resource, original_size));
1014
1015  // Cache the 24x24 copy.
1016  extension->SetCachedImage(resource, image24, original_size);
1017  EXPECT_TRUE(extension->HasCachedImage(resource, size24));
1018  EXPECT_TRUE(SizeEquals(extension->GetCachedImage(resource, size24), size24));
1019  EXPECT_FALSE(extension->HasCachedImage(resource, original_size));
1020
1021  // Cache the original, and verify that it gets returned when we ask for a
1022  // max_size that is larger than the original.
1023  gfx::Size size128(128, 128);
1024  EXPECT_TRUE(image.width() < size128.width() &&
1025              image.height() < size128.height());
1026  extension->SetCachedImage(resource, image, original_size);
1027  EXPECT_TRUE(extension->HasCachedImage(resource, original_size));
1028  EXPECT_TRUE(extension->HasCachedImage(resource, size128));
1029  EXPECT_TRUE(SizeEquals(extension->GetCachedImage(resource, original_size),
1030                         original_size));
1031  EXPECT_TRUE(SizeEquals(extension->GetCachedImage(resource, size128),
1032                         original_size));
1033  EXPECT_EQ(extension->GetCachedImage(resource, original_size).getPixels(),
1034            extension->GetCachedImage(resource, size128).getPixels());
1035}
1036
1037// Tests that the old permission name "unlimited_storage" still works for
1038// backwards compatibility (we renamed it to "unlimitedStorage").
1039TEST(ExtensionTest, OldUnlimitedStoragePermission) {
1040  ScopedTempDir directory;
1041  ASSERT_TRUE(directory.CreateUniqueTempDir());
1042  FilePath extension_path = directory.path();
1043  DictionaryValue dictionary;
1044
1045  // The two required keys.
1046  dictionary.SetString(extension_manifest_keys::kName, "test");
1047  dictionary.SetString(extension_manifest_keys::kVersion, "0.1");
1048
1049  // Create a permissions list containing "unlimited_storage" and add it.
1050  ListValue* permissions = new ListValue();
1051  const char* old_unlimited = "unlimited_storage";
1052  EXPECT_STREQ(old_unlimited, Extension::kOldUnlimitedStoragePermission);
1053  permissions->Append(Value::CreateStringValue(old_unlimited));
1054  dictionary.Set(extension_manifest_keys::kPermissions, permissions);
1055
1056  // Initialize the extension and make sure the permission for unlimited storage
1057  // is present.
1058  std::string errors;
1059  scoped_refptr<Extension> extension(Extension::Create(
1060      extension_path, Extension::INVALID, dictionary, false, true, &errors));
1061  EXPECT_TRUE(extension.get());
1062  EXPECT_TRUE(extension->HasApiPermission(
1063      Extension::kUnlimitedStoragePermission));
1064}
1065
1066// This tests the API permissions with an empty manifest (one that just
1067// specifies a name and a version and nothing else).
1068TEST(ExtensionTest, ApiPermissions) {
1069  const struct {
1070    const char* permission_name;
1071    bool expect_success;
1072  } kTests[] = {
1073    // Negative test.
1074    { "non_existing_permission", false },
1075    // Test default module/package permission.
1076    { "browserAction",  true },
1077    { "browserActions", true },
1078    { "devtools",       true },
1079    { "extension",      true },
1080    { "i18n",           true },
1081    { "pageAction",     true },
1082    { "pageActions",    true },
1083    { "test",           true },
1084    // Some negative tests.
1085    { "bookmarks",      false },
1086    { "cookies",        false },
1087    { "history",        false },
1088    { "tabs.onUpdated", false },
1089    // Make sure we find the module name after stripping '.' and '/'.
1090    { "browserAction/abcd/onClick",  true },
1091    { "browserAction.abcd.onClick",  true },
1092    // Test Tabs functions.
1093    { "tabs.create",      true},
1094    { "tabs.update",      true},
1095    { "tabs.getSelected", false},
1096  };
1097
1098  scoped_refptr<Extension> extension;
1099  extension = LoadManifest("empty_manifest", "empty.json");
1100
1101  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) {
1102    EXPECT_EQ(kTests[i].expect_success,
1103              extension->HasApiPermission(kTests[i].permission_name))
1104                  << "Permission being tested: " << kTests[i].permission_name;
1105  }
1106}
1107
1108TEST(ExtensionTest, GetDistinctHostsForDisplay) {
1109  std::vector<std::string> expected;
1110  expected.push_back("www.foo.com");
1111  expected.push_back("www.bar.com");
1112  expected.push_back("www.baz.com");
1113  URLPatternList actual;
1114
1115  {
1116    SCOPED_TRACE("no dupes");
1117
1118    // Simple list with no dupes.
1119    actual.push_back(
1120        URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path"));
1121    actual.push_back(
1122        URLPattern(URLPattern::SCHEME_HTTP, "http://www.bar.com/path"));
1123    actual.push_back(
1124        URLPattern(URLPattern::SCHEME_HTTP, "http://www.baz.com/path"));
1125    CompareLists(expected,
1126                 Extension::GetDistinctHostsForDisplay(actual));
1127  }
1128
1129  {
1130    SCOPED_TRACE("two dupes");
1131
1132    // Add some dupes.
1133    actual.push_back(
1134        URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path"));
1135    actual.push_back(
1136        URLPattern(URLPattern::SCHEME_HTTP, "http://www.baz.com/path"));
1137    CompareLists(expected,
1138                 Extension::GetDistinctHostsForDisplay(actual));
1139  }
1140
1141  {
1142    SCOPED_TRACE("schemes differ");
1143
1144    // Add a pattern that differs only by scheme. This should be filtered out.
1145    actual.push_back(
1146        URLPattern(URLPattern::SCHEME_HTTPS, "https://www.bar.com/path"));
1147    CompareLists(expected,
1148                 Extension::GetDistinctHostsForDisplay(actual));
1149  }
1150
1151  {
1152    SCOPED_TRACE("paths differ");
1153
1154    // Add some dupes by path.
1155    actual.push_back(
1156        URLPattern(URLPattern::SCHEME_HTTP, "http://www.bar.com/pathypath"));
1157    CompareLists(expected,
1158                 Extension::GetDistinctHostsForDisplay(actual));
1159  }
1160
1161  {
1162    SCOPED_TRACE("subdomains differ");
1163
1164    // We don't do anything special for subdomains.
1165    actual.push_back(
1166        URLPattern(URLPattern::SCHEME_HTTP, "http://monkey.www.bar.com/path"));
1167    actual.push_back(
1168        URLPattern(URLPattern::SCHEME_HTTP, "http://bar.com/path"));
1169
1170    expected.push_back("monkey.www.bar.com");
1171    expected.push_back("bar.com");
1172
1173    CompareLists(expected,
1174                 Extension::GetDistinctHostsForDisplay(actual));
1175  }
1176
1177  {
1178    SCOPED_TRACE("RCDs differ");
1179
1180    // Now test for RCD uniquing.
1181    actual.push_back(
1182        URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path"));
1183    actual.push_back(
1184        URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path"));
1185    actual.push_back(
1186        URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.de/path"));
1187    actual.push_back(
1188        URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca.us/path"));
1189    actual.push_back(
1190        URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.net/path"));
1191    actual.push_back(
1192        URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com.my/path"));
1193
1194    // This is an unknown RCD, which shouldn't be uniqued out.
1195    actual.push_back(
1196        URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.xyzzy/path"));
1197
1198    expected.push_back("www.foo.xyzzy");
1199
1200    CompareLists(expected,
1201                 Extension::GetDistinctHostsForDisplay(actual));
1202  }
1203
1204  {
1205    SCOPED_TRACE("wildcards");
1206
1207    actual.push_back(
1208        URLPattern(URLPattern::SCHEME_HTTP, "http://*.google.com/*"));
1209
1210    expected.push_back("*.google.com");
1211
1212    CompareLists(expected,
1213                 Extension::GetDistinctHostsForDisplay(actual));
1214  }
1215}
1216
1217TEST(ExtensionTest, IsElevatedHostList) {
1218  URLPatternList list1;
1219  URLPatternList list2;
1220
1221  list1.push_back(
1222      URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/path"));
1223  list1.push_back(
1224      URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/path"));
1225
1226  // Test that the host order does not matter.
1227  list2.push_back(
1228      URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/path"));
1229  list2.push_back(
1230      URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/path"));
1231
1232  EXPECT_FALSE(Extension::IsElevatedHostList(list1, list2));
1233  EXPECT_FALSE(Extension::IsElevatedHostList(list2, list1));
1234
1235  // Test that paths are ignored.
1236  list2.clear();
1237  list2.push_back(
1238      URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/*"));
1239  EXPECT_FALSE(Extension::IsElevatedHostList(list1, list2));
1240  EXPECT_FALSE(Extension::IsElevatedHostList(list2, list1));
1241
1242  // Test that RCDs are ignored.
1243  list2.clear();
1244  list2.push_back(
1245      URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/*"));
1246  EXPECT_FALSE(Extension::IsElevatedHostList(list1, list2));
1247  EXPECT_FALSE(Extension::IsElevatedHostList(list2, list1));
1248
1249  // Test that subdomain wildcards are handled properly.
1250  list2.clear();
1251  list2.push_back(
1252      URLPattern(URLPattern::SCHEME_HTTP, "http://*.google.com.hk/*"));
1253  EXPECT_TRUE(Extension::IsElevatedHostList(list1, list2));
1254  //TODO(jstritar): Does not match subdomains properly. http://crbug.com/65337
1255  //EXPECT_FALSE(Extension::IsElevatedHostList(list2, list1));
1256
1257  // Test that different domains count as different hosts.
1258  list2.clear();
1259  list2.push_back(
1260      URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/path"));
1261  list2.push_back(
1262      URLPattern(URLPattern::SCHEME_HTTP, "http://www.example.org/path"));
1263  EXPECT_TRUE(Extension::IsElevatedHostList(list1, list2));
1264  EXPECT_FALSE(Extension::IsElevatedHostList(list2, list1));
1265
1266  // Test that different subdomains count as different hosts.
1267  list2.clear();
1268  list2.push_back(
1269      URLPattern(URLPattern::SCHEME_HTTP, "http://mail.google.com/*"));
1270  EXPECT_TRUE(Extension::IsElevatedHostList(list1, list2));
1271  EXPECT_TRUE(Extension::IsElevatedHostList(list2, list1));
1272}
1273
1274TEST(ExtensionTest, GenerateId) {
1275  std::string result;
1276  EXPECT_FALSE(Extension::GenerateId("", &result));
1277
1278  EXPECT_TRUE(Extension::GenerateId("test", &result));
1279  EXPECT_EQ(result, "jpignaibiiemhngfjkcpokkamffknabf");
1280
1281  EXPECT_TRUE(Extension::GenerateId("_", &result));
1282  EXPECT_EQ(result, "ncocknphbhhlhkikpnnlmbcnbgdempcd");
1283
1284  EXPECT_TRUE(Extension::GenerateId(
1285      "this_string_is_longer_than_a_single_sha256_hash_digest", &result));
1286  EXPECT_EQ(result, "jimneklojkjdibfkgiiophfhjhbdgcfi");
1287}
1288