Logo Search packages:      
Sourcecode: chromium-browser version File versions  Download package

extension_unittest.cc

// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/common/extensions/extension.h"

#include "base/format_macros.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/string_util.h"
#include "base/path_service.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/extensions/extension_error_reporter.h"
#include "chrome/common/extensions/extension_error_utils.h"
#include "chrome/common/json_value_serializer.h"
#include "chrome/common/url_constants.h"
#include "net/base/mime_sniffer.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace keys = extension_manifest_keys;
namespace values = extension_manifest_values;
namespace errors = extension_manifest_errors;

class ExtensionTest : public testing::Test {
};

// We persist location values in the preferences, so this is a sanity test that
// someone doesn't accidentally change them.
TEST(ExtensionTest, LocationValuesTest) {
  ASSERT_EQ(0, Extension::INVALID);
  ASSERT_EQ(1, Extension::INTERNAL);
  ASSERT_EQ(2, Extension::EXTERNAL_PREF);
  ASSERT_EQ(3, Extension::EXTERNAL_REGISTRY);
  ASSERT_EQ(4, Extension::LOAD);
  ASSERT_EQ(5, Extension::COMPONENT);
}


// Please don't put any more manifest tests here!!
// Move them to extension_manifest_unittest.cc instead and make them use the
// more data-driven style there instead.
// Bug: http://crbug.com/38462


TEST(ExtensionTest, InitFromValueInvalid) {
#if defined(OS_WIN)
  FilePath path(FILE_PATH_LITERAL("c:\\foo"));
#elif defined(OS_POSIX)
  FilePath path(FILE_PATH_LITERAL("/foo"));
#endif
  Extension extension(path);
  int error_code = 0;
  std::string error;
  ExtensionErrorReporter::Init(false);

  // Start with a valid extension manifest
  FilePath extensions_path;
  ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &extensions_path));
  extensions_path = extensions_path.AppendASCII("extensions")
      .AppendASCII("good")
      .AppendASCII("Extensions")
      .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
      .AppendASCII("1.0.0.0")
      .Append(Extension::kManifestFilename);

  JSONFileValueSerializer serializer(extensions_path);
  scoped_ptr<DictionaryValue> valid_value(
      static_cast<DictionaryValue*>(serializer.Deserialize(&error_code,
                                                           &error)));
  EXPECT_EQ("", error);
  EXPECT_EQ(0, error_code);
  ASSERT_TRUE(valid_value.get());
  ASSERT_TRUE(extension.InitFromValue(*valid_value, true, &error));
  ASSERT_EQ("", error);
  EXPECT_EQ("en_US", extension.default_locale());

  scoped_ptr<DictionaryValue> input_value;

  // Test missing and invalid versions
  input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy()));
  input_value->Remove(keys::kVersion, NULL);
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_EQ(errors::kInvalidVersion, error);

  input_value->SetInteger(keys::kVersion, 42);
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_EQ(errors::kInvalidVersion, error);

  // Test missing and invalid names
  input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy()));
  input_value->Remove(keys::kName, NULL);
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_EQ(errors::kInvalidName, error);

  input_value->SetInteger(keys::kName, 42);
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_EQ(errors::kInvalidName, error);

  // Test invalid description
  input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy()));
  input_value->SetInteger(keys::kDescription, 42);
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_EQ(errors::kInvalidDescription, error);

  // Test invalid icons
  input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy()));
  input_value->SetInteger(keys::kIcons, 42);
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_EQ(errors::kInvalidIcons, error);

  // Test invalid icon paths
  input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy()));
  DictionaryValue* icons = NULL;
  input_value->GetDictionary(keys::kIcons, &icons);
  ASSERT_FALSE(NULL == icons);
  icons->SetInteger(ASCIIToWide(IntToString(128)), 42);
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_TRUE(MatchPatternASCII(error, errors::kInvalidIconPath));

  // Test invalid user scripts list
  input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy()));
  input_value->SetInteger(keys::kContentScripts, 42);
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_EQ(errors::kInvalidContentScriptsList, error);

  // Test invalid user script item
  input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy()));
  ListValue* content_scripts = NULL;
  input_value->GetList(keys::kContentScripts, &content_scripts);
  ASSERT_FALSE(NULL == content_scripts);
  content_scripts->Set(0, Value::CreateIntegerValue(42));
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_TRUE(MatchPatternASCII(error, errors::kInvalidContentScript));

  // Test missing and invalid matches array
  input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy()));
  input_value->GetList(keys::kContentScripts, &content_scripts);
  DictionaryValue* user_script = NULL;
  content_scripts->GetDictionary(0, &user_script);
  user_script->Remove(keys::kMatches, NULL);
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_TRUE(MatchPatternASCII(error, errors::kInvalidMatches));

  user_script->Set(keys::kMatches, Value::CreateIntegerValue(42));
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_TRUE(MatchPatternASCII(error, errors::kInvalidMatches));

  ListValue* matches = new ListValue;
  user_script->Set(keys::kMatches, matches);
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_TRUE(MatchPatternASCII(error, errors::kInvalidMatchCount));

  // Test invalid match element
  matches->Set(0, Value::CreateIntegerValue(42));
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_TRUE(MatchPatternASCII(error, errors::kInvalidMatch));

  // Test missing and invalid files array
  input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy()));
  input_value->GetList(keys::kContentScripts, &content_scripts);
  content_scripts->GetDictionary(0, &user_script);
  user_script->Remove(keys::kJs, NULL);
  user_script->Remove(keys::kCss, NULL);
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_TRUE(MatchPatternASCII(error, errors::kMissingFile));

  user_script->Set(keys::kJs, Value::CreateIntegerValue(42));
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_TRUE(MatchPatternASCII(error, errors::kInvalidJsList));

  user_script->Set(keys::kCss, new ListValue);
  user_script->Set(keys::kJs, new ListValue);
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_TRUE(MatchPatternASCII(error, errors::kMissingFile));
  user_script->Remove(keys::kCss, NULL);

  ListValue* files = new ListValue;
  user_script->Set(keys::kJs, files);
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_TRUE(MatchPatternASCII(error, errors::kMissingFile));

  // Test invalid file element
  files->Set(0, Value::CreateIntegerValue(42));
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_TRUE(MatchPatternASCII(error, errors::kInvalidJs));

  user_script->Remove(keys::kJs, NULL);
  // Test the css element
  user_script->Set(keys::kCss, Value::CreateIntegerValue(42));
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_TRUE(MatchPatternASCII(error, errors::kInvalidCssList));

  // Test invalid file element
  ListValue* css_files = new ListValue;
  user_script->Set(keys::kCss, css_files);
  css_files->Set(0, Value::CreateIntegerValue(42));
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_TRUE(MatchPatternASCII(error, errors::kInvalidCss));

  // Test missing and invalid permissions array
  input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy()));
  EXPECT_TRUE(extension.InitFromValue(*input_value, true, &error));
  ListValue* permissions = NULL;
  input_value->GetList(keys::kPermissions, &permissions);
  ASSERT_FALSE(NULL == permissions);

  permissions = new ListValue;
  input_value->Set(keys::kPermissions, permissions);
  EXPECT_TRUE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_EQ(0u, ExtensionErrorReporter::GetInstance()->GetErrors()->size());

  input_value->Set(keys::kPermissions, Value::CreateIntegerValue(9));
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_TRUE(MatchPatternASCII(error, errors::kInvalidPermissions));

  input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy()));
  input_value->GetList(keys::kPermissions, &permissions);
  permissions->Set(0, Value::CreateIntegerValue(24));
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_TRUE(MatchPatternASCII(error, errors::kInvalidPermission));

  permissions->Set(0, Value::CreateStringValue("www.google.com"));
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_TRUE(MatchPatternASCII(error, errors::kInvalidPermission));

  // Test permissions scheme.
  input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy()));
  input_value->GetList(keys::kPermissions, &permissions);
  permissions->Set(0, Value::CreateStringValue("file:///C:/foo.txt"));
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_TRUE(MatchPatternASCII(error, errors::kInvalidPermissionScheme));

  // Multiple page actions are not allowed.
  input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy()));
  DictionaryValue* action = new DictionaryValue;
  action->SetString(keys::kPageActionId, "MyExtensionActionId");
  action->SetString(keys::kName, "MyExtensionActionName");
  ListValue* action_list = new ListValue;
  action_list->Append(action->DeepCopy());
  action_list->Append(action);
  input_value->Set(keys::kPageActions, action_list);
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_STREQ(errors::kInvalidPageActionsListSize, error.c_str());

  // Test invalid UI surface count (both page action and browser action).
  input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy()));
  action = new DictionaryValue;
  action->SetString(keys::kPageActionId, "MyExtensionActionId");
  action->SetString(keys::kName, "MyExtensionActionName");
  action_list = new ListValue;
  action_list->Append(action->DeepCopy());
  input_value->Set(keys::kPageActions, action_list);
  input_value->Set(keys::kBrowserAction, action);
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_STREQ(errors::kOneUISurfaceOnly, error.c_str());

  // Test invalid options page url.
  input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy()));
  input_value->Set(keys::kOptionsPage, Value::CreateNullValue());
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_TRUE(MatchPatternASCII(error, errors::kInvalidOptionsPage));

  // Test invalid/empty default locale.
  input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy()));
  input_value->Set(keys::kDefaultLocale, Value::CreateIntegerValue(5));
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_TRUE(MatchPatternASCII(error, errors::kInvalidDefaultLocale));

  input_value->Set(keys::kDefaultLocale, Value::CreateStringValue(""));
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_TRUE(MatchPatternASCII(error, errors::kInvalidDefaultLocale));

  // Test invalid minimum_chrome_version.
  input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy()));
  input_value->Set(keys::kMinimumChromeVersion, Value::CreateIntegerValue(42));
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_TRUE(MatchPatternASCII(error, errors::kInvalidMinimumChromeVersion));

#if !defined(OS_MACOSX)
  // TODO(aa): The version isn't stamped into the unit test binary on mac.
  input_value->Set(keys::kMinimumChromeVersion,
                   Value::CreateStringValue("88.8"));
  EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
  EXPECT_TRUE(MatchPatternASCII(error, errors::kChromeVersionTooLow));
#endif
}

TEST(ExtensionTest, InitFromValueValid) {
#if defined(OS_WIN)
  FilePath path(FILE_PATH_LITERAL("C:\\foo"));
#elif defined(OS_POSIX)
  FilePath path(FILE_PATH_LITERAL("/foo"));
#endif
  Extension extension(path);
  std::string error;
  DictionaryValue input_value;

  // Test minimal extension
  input_value.SetString(keys::kVersion, "1.0.0.0");
  input_value.SetString(keys::kName, "my extension");

  EXPECT_TRUE(extension.InitFromValue(input_value, false, &error));
  EXPECT_EQ("", error);
  EXPECT_TRUE(Extension::IdIsValid(extension.id()));
  EXPECT_EQ("1.0.0.0", extension.VersionString());
  EXPECT_EQ("my extension", extension.name());
  EXPECT_EQ(extension.id(), extension.url().host());
  EXPECT_EQ(path.value(), extension.path().value());

  // Test with an options page.
  input_value.SetString(keys::kOptionsPage, "options.html");
  EXPECT_TRUE(extension.InitFromValue(input_value, false, &error));
  EXPECT_EQ("", error);
  EXPECT_EQ("chrome-extension", extension.options_url().scheme());
  EXPECT_EQ("/options.html", extension.options_url().path());

  // Test that an empty list of page actions does not stop a browser action
  // from being loaded.
  ListValue* empty_list = new ListValue;
  input_value.Set(keys::kPageActions, empty_list);
  EXPECT_TRUE(extension.InitFromValue(input_value, false, &error));
  EXPECT_EQ("", error);

#if !defined(OS_MACOSX)
  // TODO(aa): The version isn't stamped into the unit test binary on mac.
  // Test with a minimum_chrome_version.
  input_value.SetString(keys::kMinimumChromeVersion, "1.0");
  EXPECT_TRUE(extension.InitFromValue(input_value, false, &error));
  EXPECT_EQ("", error);
  // The minimum chrome version is not stored in the Extension object.
#endif
}

TEST(ExtensionTest, GetResourceURLAndPath) {
#if defined(OS_WIN)
  FilePath path(FILE_PATH_LITERAL("C:\\foo"));
#elif defined(OS_POSIX)
  FilePath path(FILE_PATH_LITERAL("/foo"));
#endif
  Extension extension(path);
  DictionaryValue input_value;
  input_value.SetString(keys::kVersion, "1.0.0.0");
  input_value.SetString(keys::kName, "my extension");
  EXPECT_TRUE(extension.InitFromValue(input_value, false, NULL));

  EXPECT_EQ(extension.url().spec() + "bar/baz.js",
            Extension::GetResourceURL(extension.url(), "bar/baz.js").spec());
  EXPECT_EQ(extension.url().spec() + "baz.js",
            Extension::GetResourceURL(extension.url(), "bar/../baz.js").spec());
  EXPECT_EQ(extension.url().spec() + "baz.js",
            Extension::GetResourceURL(extension.url(), "../baz.js").spec());
}

TEST(ExtensionTest, LoadPageActionHelper) {
#if defined(OS_WIN)
    FilePath path(StringPrintf(L"c:\\extension"));
#else
    FilePath path(StringPrintf("/extension"));
#endif
  Extension extension(path);
  std::string error_msg;
  scoped_ptr<ExtensionAction> action;
  DictionaryValue input;

  // First try with an empty dictionary.
  action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
  ASSERT_TRUE(action != NULL);
  ASSERT_TRUE(error_msg.empty());

  // Now setup some values to use in the action.
  const std::string id("MyExtensionActionId");
  const std::string name("MyExtensionActionName");
  std::string img1("image1.png");
  std::string img2("image2.png");

  // Add the dictionary for the contextual action.
  input.SetString(keys::kPageActionId, id);
  input.SetString(keys::kName, name);
  ListValue* icons = new ListValue;
  icons->Set(0, Value::CreateStringValue(img1));
  icons->Set(1, Value::CreateStringValue(img2));
  input.Set(keys::kPageActionIcons, icons);

  // Parse and read back the values from the object.
  action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
  ASSERT_TRUE(NULL != action.get());
  ASSERT_TRUE(error_msg.empty());
  ASSERT_EQ(id, action->id());
  // No title, so fall back to name.
  ASSERT_EQ(name, action->GetTitle(1));
  ASSERT_EQ(2u, action->icon_paths()->size());
  ASSERT_EQ(img1, (*action->icon_paths())[0]);
  ASSERT_EQ(img2, (*action->icon_paths())[1]);

  // Explicitly set the same type and parse again.
  input.SetString(keys::kType, values::kPageActionTypeTab);
  action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
  ASSERT_TRUE(NULL != action.get());
  ASSERT_TRUE(error_msg.empty());

  // Make a deep copy of the input and remove one key at a time and see if we
  // get the right error.
  scoped_ptr<DictionaryValue> copy;

  // First remove id key.
  copy.reset(static_cast<DictionaryValue*>(input.DeepCopy()));
  copy->Remove(keys::kPageActionId, NULL);
  action.reset(extension.LoadExtensionActionHelper(copy.get(), &error_msg));
  ASSERT_TRUE(NULL != action.get());

  // Then remove the name key. It's optional, so no error.
  copy.reset(static_cast<DictionaryValue*>(input.DeepCopy()));
  copy->Remove(keys::kName, NULL);
  action.reset(extension.LoadExtensionActionHelper(copy.get(), &error_msg));
  ASSERT_TRUE(NULL != action.get());
  ASSERT_TRUE(action->GetTitle(1).empty());
  ASSERT_TRUE(error_msg.empty());

  // Then remove the icon paths key.
  copy.reset(static_cast<DictionaryValue*>(input.DeepCopy()));
  copy->Remove(keys::kPageActionIcons, NULL);
  action.reset(extension.LoadExtensionActionHelper(copy.get(), &error_msg));
  ASSERT_TRUE(NULL != action.get());
  error_msg = "";

  // Now test that we can parse the new format for page actions.

  // Now setup some values to use in the page action.
  const std::string kTitle("MyExtensionActionTitle");
  const std::string kIcon("image1.png");
  const std::string kPopupHtmlFile("a_popup.html");

  // Add the dictionary for the contextual action.
  input.Clear();
  input.SetString(keys::kPageActionDefaultTitle, kTitle);
  input.SetString(keys::kPageActionDefaultIcon, kIcon);

  // Parse and read back the values from the object.
  action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
  ASSERT_TRUE(action.get());
  ASSERT_TRUE(error_msg.empty());
  ASSERT_EQ(kTitle, action->GetTitle(1));
  ASSERT_EQ(0u, action->icon_paths()->size());

  // Invalid title should give an error even with a valid name.
  input.Clear();
  input.SetInteger(keys::kPageActionDefaultTitle, 42);
  input.SetString(keys::kName, name);
  action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
  ASSERT_TRUE(NULL == action.get());
  ASSERT_STREQ(errors::kInvalidPageActionDefaultTitle, error_msg.c_str());
  error_msg = "";

  // Invalid name should give an error only with no title.
  input.SetString(keys::kPageActionDefaultTitle, kTitle);
  input.SetInteger(keys::kName, 123);
  action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
  ASSERT_TRUE(NULL != action.get());
  ASSERT_EQ(kTitle, action->GetTitle(1));
  ASSERT_TRUE(error_msg.empty());

  input.Remove(keys::kPageActionDefaultTitle, NULL);
  action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
  ASSERT_TRUE(NULL == action.get());
  ASSERT_STREQ(errors::kInvalidPageActionName, error_msg.c_str());
  error_msg = "";

  // Test that keys "popup" and "default_popup" both work, but can not
  // be used at the same time.
  input.Clear();
  input.SetString(keys::kPageActionDefaultTitle, kTitle);
  input.SetString(keys::kPageActionDefaultIcon, kIcon);

  // LoadExtensionActionHelper expects the extension member |extension_url_|
  // to be set.
  extension.extension_url_ = GURL(std::string(chrome::kExtensionScheme) +
                                  chrome::kStandardSchemeSeparator +
                                  "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/");

  // Add key "popup", expect success.
  input.SetString(keys::kPageActionPopup, kPopupHtmlFile);
  action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
  ASSERT_TRUE(NULL != action.get());
  ASSERT_TRUE(error_msg.empty());
  ASSERT_STREQ(
      extension.extension_url_.Resolve(kPopupHtmlFile).spec().c_str(),
      action->GetPopupUrl(ExtensionAction::kDefaultTabId).spec().c_str());

  // Add key "default_popup", expect failure.
  input.SetString(keys::kPageActionDefaultPopup, kPopupHtmlFile);
  action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
  ASSERT_TRUE(NULL == action.get());
  ASSERT_STREQ(
      ExtensionErrorUtils::FormatErrorMessage(
          errors::kInvalidPageActionOldAndNewKeys,
          WideToASCII(keys::kPageActionDefaultPopup),
          WideToASCII(keys::kPageActionPopup)).c_str(),
      error_msg.c_str());
  error_msg = "";

  // Remove key "popup", expect success.
  input.Remove(keys::kPageActionPopup, NULL);
  action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
  ASSERT_TRUE(NULL != action.get());
  ASSERT_TRUE(error_msg.empty());
  ASSERT_STREQ(
      extension.extension_url_.Resolve(kPopupHtmlFile).spec().c_str(),
      action->GetPopupUrl(ExtensionAction::kDefaultTabId).spec().c_str());

  // Setting default_popup to "" is the same as having no popup.
  input.Remove(keys::kPageActionDefaultPopup, NULL);
  input.SetString(keys::kPageActionDefaultPopup, "");
  action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
  ASSERT_TRUE(NULL != action.get());
  ASSERT_TRUE(error_msg.empty());
  EXPECT_FALSE(action->HasPopup(ExtensionAction::kDefaultTabId));
  ASSERT_STREQ(
      "",
      action->GetPopupUrl(ExtensionAction::kDefaultTabId).spec().c_str());

  // Setting popup to "" is the same as having no popup.
  input.Remove(keys::kPageActionDefaultPopup, NULL);
  input.SetString(keys::kPageActionPopup, "");
  action.reset(extension.LoadExtensionActionHelper(&input, &error_msg));
  ASSERT_TRUE(NULL != action.get());
  ASSERT_TRUE(error_msg.empty());
  EXPECT_FALSE(action->HasPopup(ExtensionAction::kDefaultTabId));
  ASSERT_STREQ(
      "",
      action->GetPopupUrl(ExtensionAction::kDefaultTabId).spec().c_str());
}

TEST(ExtensionTest, IdIsValid) {
  EXPECT_TRUE(Extension::IdIsValid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
  EXPECT_TRUE(Extension::IdIsValid("pppppppppppppppppppppppppppppppp"));
  EXPECT_TRUE(Extension::IdIsValid("abcdefghijklmnopabcdefghijklmnop"));
  EXPECT_TRUE(Extension::IdIsValid("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"));
  EXPECT_FALSE(Extension::IdIsValid("abcdefghijklmnopabcdefghijklmno"));
  EXPECT_FALSE(Extension::IdIsValid("abcdefghijklmnopabcdefghijklmnopa"));
  EXPECT_FALSE(Extension::IdIsValid("0123456789abcdef0123456789abcdef"));
  EXPECT_FALSE(Extension::IdIsValid("abcdefghijklmnopabcdefghijklmnoq"));
  EXPECT_FALSE(Extension::IdIsValid("abcdefghijklmnopabcdefghijklmno0"));
}

TEST(ExtensionTest, GenerateID) {
  const uint8 public_key_info[] = {
    0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
    0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81,
    0x89, 0x02, 0x81, 0x81, 0x00, 0xb8, 0x7f, 0x2b, 0x20, 0xdc, 0x7c, 0x9b,
    0x0c, 0xdc, 0x51, 0x61, 0x99, 0x0d, 0x36, 0x0f, 0xd4, 0x66, 0x88, 0x08,
    0x55, 0x84, 0xd5, 0x3a, 0xbf, 0x2b, 0xa4, 0x64, 0x85, 0x7b, 0x0c, 0x04,
    0x13, 0x3f, 0x8d, 0xf4, 0xbc, 0x38, 0x0d, 0x49, 0xfe, 0x6b, 0xc4, 0x5a,
    0xb0, 0x40, 0x53, 0x3a, 0xd7, 0x66, 0x09, 0x0f, 0x9e, 0x36, 0x74, 0x30,
    0xda, 0x8a, 0x31, 0x4f, 0x1f, 0x14, 0x50, 0xd7, 0xc7, 0x20, 0x94, 0x17,
    0xde, 0x4e, 0xb9, 0x57, 0x5e, 0x7e, 0x0a, 0xe5, 0xb2, 0x65, 0x7a, 0x89,
    0x4e, 0xb6, 0x47, 0xff, 0x1c, 0xbd, 0xb7, 0x38, 0x13, 0xaf, 0x47, 0x85,
    0x84, 0x32, 0x33, 0xf3, 0x17, 0x49, 0xbf, 0xe9, 0x96, 0xd0, 0xd6, 0x14,
    0x6f, 0x13, 0x8d, 0xc5, 0xfc, 0x2c, 0x72, 0xba, 0xac, 0xea, 0x7e, 0x18,
    0x53, 0x56, 0xa6, 0x83, 0xa2, 0xce, 0x93, 0x93, 0xe7, 0x1f, 0x0f, 0xe6,
    0x0f, 0x02, 0x03, 0x01, 0x00, 0x01
  };

  std::string extension_id;
  EXPECT_TRUE(
      Extension::GenerateId(
          std::string(reinterpret_cast<const char*>(&public_key_info[0]),
                      arraysize(public_key_info)),
          &extension_id));
  EXPECT_EQ("melddjfinppjdikinhbgehiennejpfhp", extension_id);
}

TEST(ExtensionTest, UpdateUrls) {
  // Test several valid update urls
  std::vector<std::string> valid;
  valid.push_back("http://test.com");
  valid.push_back("http://test.com/");
  valid.push_back("http://test.com/update");
  valid.push_back("http://test.com/update?check=true");
  for (size_t i = 0; i < valid.size(); i++) {
    GURL url(valid[i]);
    EXPECT_TRUE(url.is_valid());

    DictionaryValue input_value;
#if defined(OS_WIN)
    // (Why %Iu below?  This is the single file in the whole code base that
    // might make use of a WidePRIuS; let's not encourage any more.)
    FilePath path(StringPrintf(L"c:\\extension%Iu", i));
#else
    FilePath path(StringPrintf("/extension%" PRIuS, i));
#endif
    Extension extension(path);
    std::string error;

    input_value.SetString(keys::kVersion, "1.0");
    input_value.SetString(keys::kName, "Test");
    input_value.SetString(keys::kUpdateURL, url.spec());

    EXPECT_TRUE(extension.InitFromValue(input_value, false, &error));
  }

  // Test some invalid update urls
  std::vector<std::string> invalid;
  invalid.push_back("");
  invalid.push_back("test.com");
  valid.push_back("http://test.com/update#whatever");
  for (size_t i = 0; i < invalid.size(); i++) {
    DictionaryValue input_value;
#if defined(OS_WIN)
    // (Why %Iu below?  This is the single file in the whole code base that
    // might make use of a WidePRIuS; let's not encourage any more.)
    FilePath path(StringPrintf(L"c:\\extension%Iu", i));
#else
    FilePath path(StringPrintf("/extension%" PRIuS, i));
#endif
    Extension extension(path);
    std::string error;
    input_value.SetString(keys::kVersion, "1.0");
    input_value.SetString(keys::kName, "Test");
    input_value.SetString(keys::kUpdateURL, invalid[i]);

    EXPECT_FALSE(extension.InitFromValue(input_value, false, &error));
    EXPECT_TRUE(MatchPatternASCII(error, errors::kInvalidUpdateURL));
  }
}

// This test ensures that the mimetype sniffing code stays in sync with the
// actual crx files that we test other parts of the system with.
TEST(ExtensionTest, MimeTypeSniffing) {
  FilePath path;
  ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &path));
  path = path.AppendASCII("extensions").AppendASCII("good.crx");

  std::string data;
  ASSERT_TRUE(file_util::ReadFileToString(path, &data));

  std::string result;
  EXPECT_TRUE(net::SniffMimeType(data.c_str(), data.size(),
              GURL("http://www.example.com/foo.crx"), "", &result));
  EXPECT_EQ(std::string(Extension::kMimeType), result);

  data.clear();
  result.clear();
  path = path.DirName().AppendASCII("bad_magic.crx");
  ASSERT_TRUE(file_util::ReadFileToString(path, &data));
  EXPECT_TRUE(net::SniffMimeType(data.c_str(), data.size(),
              GURL("http://www.example.com/foo.crx"), "", &result));
  EXPECT_EQ("application/octet-stream", result);
}

static Extension* LoadManifest(const std::string& dir,
                               const std::string& test_file) {
  FilePath path;
  PathService::Get(chrome::DIR_TEST_DATA, &path);
  path = path.AppendASCII("extensions")
             .AppendASCII(dir)
             .AppendASCII(test_file);

  JSONFileValueSerializer serializer(path);
  scoped_ptr<Value> result(serializer.Deserialize(NULL, NULL));
  if (!result.get())
    return NULL;

  std::string error;
  scoped_ptr<Extension> extension(new Extension(path.DirName()));
  extension->InitFromValue(*static_cast<DictionaryValue*>(result.get()),
                           false, &error);

  return extension.release();
}

TEST(ExtensionTest, EffectiveHostPermissions) {
  scoped_ptr<Extension> extension;
  std::set<std::string> hosts;

  extension.reset(LoadManifest("effective_host_permissions", "empty.json"));
  EXPECT_EQ(0u, extension->GetEffectiveHostPermissions().size());
  EXPECT_FALSE(extension->HasAccessToAllHosts());

  extension.reset(LoadManifest("effective_host_permissions", "one_host.json"));
  hosts = extension->GetEffectiveHostPermissions();
  EXPECT_EQ(1u, hosts.size());
  EXPECT_TRUE(hosts.find("www.google.com") != hosts.end());
  EXPECT_FALSE(extension->HasAccessToAllHosts());

  extension.reset(LoadManifest("effective_host_permissions",
                               "one_host_wildcard.json"));
  hosts = extension->GetEffectiveHostPermissions();
  EXPECT_EQ(1u, hosts.size());
  EXPECT_TRUE(hosts.find("google.com") != hosts.end());
  EXPECT_FALSE(extension->HasAccessToAllHosts());

  extension.reset(LoadManifest("effective_host_permissions",
                               "two_hosts.json"));
  hosts = extension->GetEffectiveHostPermissions();
  EXPECT_EQ(2u, hosts.size());
  EXPECT_TRUE(hosts.find("www.google.com") != hosts.end());
  EXPECT_TRUE(hosts.find("www.reddit.com") != hosts.end());
  EXPECT_FALSE(extension->HasAccessToAllHosts());

  extension.reset(LoadManifest("effective_host_permissions",
                               "duplicate_host.json"));
  hosts = extension->GetEffectiveHostPermissions();
  EXPECT_EQ(1u, hosts.size());
  EXPECT_TRUE(hosts.find("google.com") != hosts.end());
  EXPECT_FALSE(extension->HasAccessToAllHosts());

  extension.reset(LoadManifest("effective_host_permissions",
                               "https_not_considered.json"));
  hosts = extension->GetEffectiveHostPermissions();
  EXPECT_EQ(1u, hosts.size());
  EXPECT_TRUE(hosts.find("google.com") != hosts.end());
  EXPECT_FALSE(extension->HasAccessToAllHosts());

  extension.reset(LoadManifest("effective_host_permissions",
                               "two_content_scripts.json"));
  hosts = extension->GetEffectiveHostPermissions();
  EXPECT_EQ(3u, hosts.size());
  EXPECT_TRUE(hosts.find("google.com") != hosts.end());
  EXPECT_TRUE(hosts.find("www.reddit.com") != hosts.end());
  EXPECT_TRUE(hosts.find("news.ycombinator.com") != hosts.end());
  EXPECT_FALSE(extension->HasAccessToAllHosts());

  extension.reset(LoadManifest("effective_host_permissions",
                               "duplicate_content_script.json"));
  hosts = extension->GetEffectiveHostPermissions();
  EXPECT_EQ(2u, hosts.size());
  EXPECT_TRUE(hosts.find("google.com") != hosts.end());
  EXPECT_TRUE(hosts.find("www.reddit.com") != hosts.end());
  EXPECT_FALSE(extension->HasAccessToAllHosts());

  extension.reset(LoadManifest("effective_host_permissions",
                               "all_hosts.json"));
  hosts = extension->GetEffectiveHostPermissions();
  EXPECT_EQ(1u, hosts.size());
  EXPECT_TRUE(hosts.find("") != hosts.end());
  EXPECT_TRUE(extension->HasAccessToAllHosts());

  extension.reset(LoadManifest("effective_host_permissions",
                               "all_hosts2.json"));
  hosts = extension->GetEffectiveHostPermissions();
  EXPECT_EQ(2u, hosts.size());
  EXPECT_TRUE(hosts.find("") != hosts.end());
  EXPECT_TRUE(hosts.find("www.google.com") != hosts.end());
  EXPECT_TRUE(extension->HasAccessToAllHosts());

  extension.reset(LoadManifest("effective_host_permissions",
                               "all_hosts3.json"));
  hosts = extension->GetEffectiveHostPermissions();
  EXPECT_EQ(2u, hosts.size());
  EXPECT_TRUE(hosts.find("") != hosts.end());
  EXPECT_TRUE(hosts.find("www.google.com") != hosts.end());
  EXPECT_TRUE(extension->HasAccessToAllHosts());
}

TEST(ExtensionTest, IsPrivilegeIncrease) {
  const struct {
    const char* base_name;
    bool expect_success;
  } kTests[] = {
    { "allhosts1", false },  // all -> all
    { "allhosts2", false },  // all -> one
    { "allhosts3", true },  // one -> all
    { "hosts1", false },  // http://a,http://b -> http://a,http://b
    { "hosts2", false },  // http://a,http://b -> https://a,http://*.b
    { "hosts3", false },  // http://a,http://b -> http://a
    { "hosts4", true },  // http://a -> http://a,http://b
    { "permissions1", false },  // tabs -> tabs
    { "permissions2", false },  // tabs -> tabs,bookmarks
    { "permissions3", true },  // http://a -> http://a,tabs
    { "permissions4", false },  // plugin -> plugin,tabs
    { "plugin1", false },  // plugin -> plugin
    { "plugin2", false },  // plugin -> none
    { "plugin3", true },  // none -> plugin
    { "storage", false },  // none -> storage
    { "notifications", false } // none -> notifications
  };

  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) {
    scoped_ptr<Extension> old_extension(
        LoadManifest("allow_silent_upgrade",
                     std::string(kTests[i].base_name) + "_old.json"));
    scoped_ptr<Extension> new_extension(
        LoadManifest("allow_silent_upgrade",
                     std::string(kTests[i].base_name) + "_new.json"));

    EXPECT_EQ(kTests[i].expect_success,
              Extension::IsPrivilegeIncrease(old_extension.get(),
                                             new_extension.get()))
        << kTests[i].base_name;
  }
}

Generated by  Doxygen 1.6.0   Back to index