diff options
author | rdevlin.cronin@chromium.org <rdevlin.cronin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-24 01:36:16 +0000 |
---|---|---|
committer | rdevlin.cronin@chromium.org <rdevlin.cronin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-24 01:36:16 +0000 |
commit | 334ec0a0edaae51da3a9b267a1c5af441fe50f7c (patch) | |
tree | 1287c852f3f785918e397080556dceba5cc2e591 | |
parent | 62049567333baa9069fbc1e251334a97505bd0d7 (diff) | |
download | chromium_src-334ec0a0edaae51da3a9b267a1c5af441fe50f7c.zip chromium_src-334ec0a0edaae51da3a9b267a1c5af441fe50f7c.tar.gz chromium_src-334ec0a0edaae51da3a9b267a1c5af441fe50f7c.tar.bz2 |
Move ContentScripts out of Extension
TBR=ben@chromium.org (gypis)
BUG=159265
Review URL: https://chromiumcodereview.appspot.com/11724002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@190141 0039d316-1c4b-4281-b951-d872f2087c98
24 files changed, 730 insertions, 435 deletions
diff --git a/chrome/browser/chromeos/accessibility/accessibility_util.cc b/chrome/browser/chromeos/accessibility/accessibility_util.cc index f8f71aa..2b3aee2 100644 --- a/chrome/browser/chromeos/accessibility/accessibility_util.cc +++ b/chrome/browser/chromeos/accessibility/accessibility_util.cc @@ -28,6 +28,7 @@ #include "chrome/browser/ui/singleton_tabs.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_messages.h" +#include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h" #include "chrome/common/extensions/user_script.h" #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" @@ -197,8 +198,10 @@ void EnableSpokenFeedback(bool enabled, extension->id(), render_view_host->GetProcess()->GetID(), render_view_host->GetRoutingID()); - for (size_t i = 0; i < extension->content_scripts().size(); i++) { - const extensions::UserScript& script = extension->content_scripts()[i]; + const extensions::UserScriptList& content_scripts = + extensions::ContentScriptsInfo::GetContentScripts(extension); + for (size_t i = 0; i < content_scripts.size(); i++) { + const extensions::UserScript& script = content_scripts[i]; for (size_t j = 0; j < script.js_scripts().size(); ++j) { const extensions::UserScript::File &file = script.js_scripts()[j]; extensions::ExtensionResource resource = extension->GetResource( diff --git a/chrome/browser/extensions/content_scripts_parser.cc b/chrome/browser/extensions/content_scripts_parser.cc new file mode 100644 index 0000000..4229195 --- /dev/null +++ b/chrome/browser/extensions/content_scripts_parser.cc @@ -0,0 +1,28 @@ +// Copyright (c) 2013 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/browser/extensions/content_scripts_parser.h" + +#include "base/lazy_instance.h" +#include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h" + +namespace extensions { + +static base::LazyInstance<ProfileKeyedAPIFactory<ContentScriptsParser> > + g_factory = LAZY_INSTANCE_INITIALIZER; + +// static +ProfileKeyedAPIFactory<ContentScriptsParser>* +ContentScriptsParser::GetFactoryInstance() { + return &g_factory.Get(); +} + +ContentScriptsParser::ContentScriptsParser(Profile* profile) { + (new ContentScriptsHandler)->Register(); +} + +ContentScriptsParser::~ContentScriptsParser() { +} + +} // namespace extensions diff --git a/chrome/browser/extensions/content_scripts_parser.h b/chrome/browser/extensions/content_scripts_parser.h new file mode 100644 index 0000000..e6f20d4 --- /dev/null +++ b/chrome/browser/extensions/content_scripts_parser.h @@ -0,0 +1,37 @@ +// Copyright (c) 2013 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. + +#ifndef CHROME_BROWSER_EXTENSIONS_CONTENT_SCRIPTS_PARSER_H_ +#define CHROME_BROWSER_EXTENSIONS_CONTENT_SCRIPTS_PARSER_H_ + +#include "chrome/browser/extensions/api/profile_keyed_api_factory.h" +#include "chrome/browser/profiles/profile_keyed_service.h" + +class Profile; + +namespace extensions { + +// The profile-keyed service that manages the content scripts API. +class ContentScriptsParser : public ProfileKeyedAPI { + public: + explicit ContentScriptsParser(Profile* profile); + virtual ~ContentScriptsParser(); + + // ProfileKeyedAPI implementation. + static ProfileKeyedAPIFactory<ContentScriptsParser>* GetFactoryInstance(); + + private: + friend class ProfileKeyedAPIFactory<ContentScriptsParser>; + + // ProfileKeyedAPI implementation. + static const char* service_name() { + return "ContentScriptsParser"; + } + + DISALLOW_COPY_AND_ASSIGN(ContentScriptsParser); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_CONTENT_SCRIPTS_PARSER_H_ diff --git a/chrome/browser/extensions/convert_user_script_unittest.cc b/chrome/browser/extensions/convert_user_script_unittest.cc index 9985909..bce2d17 100644 --- a/chrome/browser/extensions/convert_user_script_unittest.cc +++ b/chrome/browser/extensions/convert_user_script_unittest.cc @@ -14,6 +14,8 @@ #include "chrome/browser/extensions/convert_user_script.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/manifest_handler.h" +#include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h" #include "extensions/common/constants.h" #include "testing/gtest/include/gtest/gtest.h" @@ -28,7 +30,20 @@ static void AddPattern(URLPatternSet* extent, const std::string& pattern) { } -TEST(ExtensionFromUserScript, Basic) { +class ExtensionFromUserScript : public testing::Test { + public: + virtual void SetUp() OVERRIDE { + testing::Test::SetUp(); + (new ContentScriptsHandler)->Register(); + } + + virtual void TearDown() OVERRIDE { + testing::Test::TearDown(); + ManifestHandler::ClearRegistryForTesting(); + } +}; + +TEST_F(ExtensionFromUserScript, Basic) { base::ScopedTempDir extensions_dir; ASSERT_TRUE(extensions_dir.CreateUniqueTempDir()); @@ -56,8 +71,9 @@ TEST(ExtensionFromUserScript, Basic) { EXPECT_EQ("IhCFCg9PMQTAcJdc9ytUP99WME+4yh6aMnM1uupkovo=", extension->public_key()); - ASSERT_EQ(1u, extension->content_scripts().size()); - const UserScript& script = extension->content_scripts()[0]; + ASSERT_EQ(1u, ContentScriptsInfo::GetContentScripts(extension).size()); + const UserScript& script = + ContentScriptsInfo::GetContentScripts(extension)[0]; EXPECT_EQ(UserScript::DOCUMENT_IDLE, script.run_location()); ASSERT_EQ(2u, script.globs().size()); EXPECT_EQ("http://www.google.com/*", script.globs().at(0)); @@ -78,7 +94,7 @@ TEST(ExtensionFromUserScript, Basic) { extension->path().Append(kManifestFilename))); } -TEST(ExtensionFromUserScript, NoMetdata) { +TEST_F(ExtensionFromUserScript, NoMetdata) { base::ScopedTempDir extensions_dir; ASSERT_TRUE(extensions_dir.CreateUniqueTempDir()); @@ -106,8 +122,9 @@ TEST(ExtensionFromUserScript, NoMetdata) { EXPECT_EQ("k1WxKx54hX6tfl5gQaXD/m4d9QUMwRdXWM4RW+QkWcY=", extension->public_key()); - ASSERT_EQ(1u, extension->content_scripts().size()); - const UserScript& script = extension->content_scripts()[0]; + ASSERT_EQ(1u, ContentScriptsInfo::GetContentScripts(extension).size()); + const UserScript& script = + ContentScriptsInfo::GetContentScripts(extension)[0]; ASSERT_EQ(1u, script.globs().size()); EXPECT_EQ("*", script.globs()[0]); EXPECT_EQ(0u, script.exclude_globs().size()); @@ -124,7 +141,7 @@ TEST(ExtensionFromUserScript, NoMetdata) { extension->path().Append(kManifestFilename))); } -TEST(ExtensionFromUserScript, NotUTF8) { +TEST_F(ExtensionFromUserScript, NotUTF8) { base::ScopedTempDir extensions_dir; ASSERT_TRUE(extensions_dir.CreateUniqueTempDir()); @@ -142,7 +159,7 @@ TEST(ExtensionFromUserScript, NotUTF8) { EXPECT_EQ(ASCIIToUTF16("User script must be UTF8 encoded."), error); } -TEST(ExtensionFromUserScript, RunAtDocumentStart) { +TEST_F(ExtensionFromUserScript, RunAtDocumentStart) { base::ScopedTempDir extensions_dir; ASSERT_TRUE(extensions_dir.CreateUniqueTempDir()); @@ -170,12 +187,13 @@ TEST(ExtensionFromUserScript, RunAtDocumentStart) { extension->public_key()); // Validate run location. - ASSERT_EQ(1u, extension->content_scripts().size()); - const UserScript& script = extension->content_scripts()[0]; + ASSERT_EQ(1u, ContentScriptsInfo::GetContentScripts(extension).size()); + const UserScript& script = + ContentScriptsInfo::GetContentScripts(extension)[0]; EXPECT_EQ(UserScript::DOCUMENT_START, script.run_location()); } -TEST(ExtensionFromUserScript, RunAtDocumentEnd) { +TEST_F(ExtensionFromUserScript, RunAtDocumentEnd) { base::ScopedTempDir extensions_dir; ASSERT_TRUE(extensions_dir.CreateUniqueTempDir()); @@ -203,12 +221,13 @@ TEST(ExtensionFromUserScript, RunAtDocumentEnd) { extension->public_key()); // Validate run location. - ASSERT_EQ(1u, extension->content_scripts().size()); - const UserScript& script = extension->content_scripts()[0]; + ASSERT_EQ(1u, ContentScriptsInfo::GetContentScripts(extension).size()); + const UserScript& script = + ContentScriptsInfo::GetContentScripts(extension)[0]; EXPECT_EQ(UserScript::DOCUMENT_END, script.run_location()); } -TEST(ExtensionFromUserScript, RunAtDocumentIdle) { +TEST_F(ExtensionFromUserScript, RunAtDocumentIdle) { base::ScopedTempDir extensions_dir; ASSERT_TRUE(extensions_dir.CreateUniqueTempDir()); @@ -237,8 +256,9 @@ TEST(ExtensionFromUserScript, RunAtDocumentIdle) { extension->public_key()); // Validate run location. - ASSERT_EQ(1u, extension->content_scripts().size()); - const UserScript& script = extension->content_scripts()[0]; + ASSERT_EQ(1u, ContentScriptsInfo::GetContentScripts(extension).size()); + const UserScript& script = + ContentScriptsInfo::GetContentScripts(extension)[0]; EXPECT_EQ(UserScript::DOCUMENT_IDLE, script.run_location()); } diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc index ad0134d..76e1100 100644 --- a/chrome/browser/extensions/extension_service_unittest.cc +++ b/chrome/browser/extensions/extension_service_unittest.cc @@ -69,6 +69,7 @@ #include "chrome/common/extensions/extension_l10n_util.h" #include "chrome/common/extensions/extension_manifest_constants.h" #include "chrome/common/extensions/manifest_handler.h" +#include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h" #include "chrome/common/extensions/manifest_url_handler.h" #include "chrome/common/extensions/permissions/permission_set.h" #include "chrome/common/pref_names.h" @@ -549,6 +550,7 @@ void ExtensionServiceTestBase::SetUp() { testing::Test::SetUp(); ExtensionErrorReporter::GetInstance()->ClearErrors(); (new extensions::BackgroundManifestHandler)->Register(); + (new extensions::ContentScriptsHandler)->Register(); (new extensions::DefaultLocaleHandler)->Register(); (new extensions::PluginsHandler)->Register(); } @@ -1156,7 +1158,8 @@ TEST_F(ExtensionServiceTest, LoadAllExtensionsFromDirectorySuccess) { AddPattern(&expected_patterns, "http://*.google.com/*"); AddPattern(&expected_patterns, "https://*.google.com/*"); const Extension* extension = loaded_[0]; - const extensions::UserScriptList& scripts = extension->content_scripts(); + const extensions::UserScriptList& scripts = + extensions::ContentScriptsInfo::GetContentScripts(extension); ASSERT_EQ(2u, scripts.size()); EXPECT_EQ(expected_patterns, scripts[0].url_patterns()); EXPECT_EQ(2u, scripts[0].js_scripts().size()); @@ -1195,7 +1198,8 @@ TEST_F(ExtensionServiceTest, LoadAllExtensionsFromDirectorySuccess) { EXPECT_EQ(std::string(""), loaded_[1]->description()); EXPECT_EQ(loaded_[1]->GetResourceURL("background.html"), extensions::BackgroundInfo::GetBackgroundURL(loaded_[1])); - EXPECT_EQ(0u, loaded_[1]->content_scripts().size()); + EXPECT_EQ( + 0u, extensions::ContentScriptsInfo::GetContentScripts(loaded_[1]).size()); // We don't parse the plugins section on Chrome OS. #if defined(OS_CHROMEOS) @@ -1220,7 +1224,9 @@ TEST_F(ExtensionServiceTest, LoadAllExtensionsFromDirectorySuccess) { EXPECT_EQ(std::string(good2), loaded_[index]->id()); EXPECT_EQ(std::string("My extension 3"), loaded_[index]->name()); EXPECT_EQ(std::string(""), loaded_[index]->description()); - EXPECT_EQ(0u, loaded_[index]->content_scripts().size()); + EXPECT_EQ( + 0u, + extensions::ContentScriptsInfo::GetContentScripts(loaded_[index]).size()); EXPECT_EQ(Manifest::INTERNAL, loaded_[index]->location()); }; @@ -2107,7 +2113,9 @@ TEST_F(ExtensionServiceTest, InstallTheme) { ValidatePrefKeyCount(++pref_count); ASSERT_TRUE(extension); EXPECT_TRUE(extension->is_theme()); - EXPECT_EQ(0u, extension->content_scripts().size()); + EXPECT_EQ( + 0u, + extensions::ContentScriptsInfo::GetContentScripts(extension).size()); } // A theme with image resources missing (misspelt path). diff --git a/chrome/browser/extensions/extension_ui_unittest.cc b/chrome/browser/extensions/extension_ui_unittest.cc index 5623429..a623506 100644 --- a/chrome/browser/extensions/extension_ui_unittest.cc +++ b/chrome/browser/extensions/extension_ui_unittest.cc @@ -13,6 +13,8 @@ #include "chrome/browser/ui/webui/extensions/extension_settings_handler.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/manifest_handler.h" +#include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h" #include "chrome/test/base/testing_profile.h" #include "content/public/test/test_browser_thread.h" #include "extensions/common/constants.h" @@ -29,6 +31,8 @@ class ExtensionUITest : public testing::Test { protected: virtual void SetUp() OVERRIDE { + testing::Test::SetUp(); + // Create an ExtensionService and ManagementPolicy to inject into the // ExtensionSettingsHandler. profile_.reset(new TestingProfile()); @@ -41,6 +45,8 @@ class ExtensionUITest : public testing::Test { handler_.reset(new ExtensionSettingsHandler(extension_service_, management_policy_)); + + (new extensions::ContentScriptsHandler)->Register(); } virtual void TearDown() OVERRIDE { @@ -48,6 +54,8 @@ class ExtensionUITest : public testing::Test { profile_.reset(); // Execute any pending deletion tasks. message_loop_.RunUntilIdle(); + extensions::ManifestHandler::ClearRegistryForTesting(); + testing::Test::TearDown(); } static DictionaryValue* DeserializeJSONTestData(const base::FilePath& path, diff --git a/chrome/browser/extensions/user_script_listener.cc b/chrome/browser/extensions/user_script_listener.cc index ec93cf7..de663b4 100644 --- a/chrome/browser/extensions/user_script_listener.cc +++ b/chrome/browser/extensions/user_script_listener.cc @@ -9,6 +9,7 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/resource_controller.h" @@ -185,7 +186,8 @@ void UserScriptListener::CollectURLPatterns(const Extension* extension, URLPatterns* patterns) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - const UserScriptList& scripts = extension->content_scripts(); + const UserScriptList& scripts = + ContentScriptsInfo::GetContentScripts(extension); for (UserScriptList::const_iterator iter = scripts.begin(); iter != scripts.end(); ++iter) { patterns->insert(patterns->end(), @@ -204,7 +206,7 @@ void UserScriptListener::Observe(int type, Profile* profile = content::Source<Profile>(source).ptr(); const Extension* extension = content::Details<const Extension>(details).ptr(); - if (extension->content_scripts().empty()) + if (ContentScriptsInfo::GetContentScripts(extension).empty()) return; // no new patterns from this extension. URLPatterns new_patterns; @@ -221,7 +223,7 @@ void UserScriptListener::Observe(int type, Profile* profile = content::Source<Profile>(source).ptr(); const Extension* unloaded_extension = content::Details<UnloadedExtensionInfo>(details)->extension; - if (unloaded_extension->content_scripts().empty()) + if (ContentScriptsInfo::GetContentScripts(unloaded_extension).empty()) return; // no patterns to delete for this extension. // Clear all our patterns and reregister all the still-loaded extensions. diff --git a/chrome/browser/extensions/user_script_master.cc b/chrome/browser/extensions/user_script_master.cc index dcd5804..999c0f7 100644 --- a/chrome/browser/extensions/user_script_master.cc +++ b/chrome/browser/extensions/user_script_master.cc @@ -24,6 +24,7 @@ #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_file_util.h" #include "chrome/common/extensions/extension_set.h" +#include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h" #include "chrome/common/extensions/message_bundle.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/render_process_host.h" @@ -363,7 +364,8 @@ void UserScriptMaster::Observe(int type, extension->path(), LocaleInfo::GetDefaultLocale(extension)); bool incognito_enabled = extensions::ExtensionSystem::Get(profile_)-> extension_service()->IsIncognitoEnabled(extension->id()); - const UserScriptList& scripts = extension->content_scripts(); + const UserScriptList& scripts = + ContentScriptsInfo::GetContentScripts(extension); for (UserScriptList::const_iterator iter = scripts.begin(); iter != scripts.end(); ++iter) { user_scripts_.push_back(*iter); diff --git a/chrome/browser/profiles/profile_dependency_manager.cc b/chrome/browser/profiles/profile_dependency_manager.cc index 3bfe9ab..e1add09 100644 --- a/chrome/browser/profiles/profile_dependency_manager.cc +++ b/chrome/browser/profiles/profile_dependency_manager.cc @@ -48,6 +48,7 @@ #include "chrome/browser/extensions/api/tab_capture/tab_capture_registry_factory.h" #include "chrome/browser/extensions/api/tabs/tabs_windows_api.h" #include "chrome/browser/extensions/api/web_navigation/web_navigation_api.h" +#include "chrome/browser/extensions/content_scripts_parser.h" #include "chrome/browser/extensions/csp_parser.h" #include "chrome/browser/extensions/extension_system_factory.h" #include "chrome/browser/extensions/install_tracker_factory.h" @@ -273,6 +274,7 @@ void ProfileDependencyManager::AssertFactoriesBuilt() { extensions::BookmarksAPI::GetFactoryInstance(); extensions::BluetoothAPIFactory::GetInstance(); extensions::CommandService::GetFactoryInstance(); + extensions::ContentScriptsParser::GetFactoryInstance(); extensions::CookiesAPI::GetFactoryInstance(); extensions::CSPParser::GetFactoryInstance(); extensions::DialAPIFactory::GetInstance(); diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi index 7a6af0e..537d0ec 100644 --- a/chrome/chrome_browser_extensions.gypi +++ b/chrome/chrome_browser_extensions.gypi @@ -496,6 +496,8 @@ 'browser/extensions/crx_installer.cc', 'browser/extensions/crx_installer.h', 'browser/extensions/crx_installer_error.h', + 'browser/extensions/content_scripts_parser.cc', + 'browser/extensions/content_scripts_parser.h', 'browser/extensions/csp_parser.cc', 'browser/extensions/csp_parser.h', 'browser/extensions/data_deleter.cc', diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi index 9e56307..73aeddb4 100644 --- a/chrome/chrome_common.gypi +++ b/chrome/chrome_common.gypi @@ -240,6 +240,8 @@ 'common/extensions/manifest_handler.h', 'common/extensions/manifest_handler_helpers.cc', 'common/extensions/manifest_handler_helpers.h', + 'common/extensions/manifest_handlers/content_scripts_handler.cc', + 'common/extensions/manifest_handlers/content_scripts_handler.h', 'common/extensions/manifest_url_handler.cc', 'common/extensions/manifest_url_handler.h', 'common/extensions/message_bundle.cc', @@ -410,6 +412,8 @@ ['include', 'common/extensions/api/extension_action/action_info.h'], ['include', 'common/extensions/api/extension_action/browser_action_handler.cc'], ['include', 'common/extensions/api/extension_action/browser_action_handler.h'], + ['include', 'common/extensions/api/content_scripts/content_scripts_handler.cc'], + ['include', 'common/extensions/api/content_scripts/content_scripts_handler.h'], ['include', 'common/extensions/api/extension_action/page_action_handler.cc'], ['include', 'common/extensions/api/extension_action/page_action_handler.h'], ['include', 'common/extensions/api/icons/icons_handler.cc'], diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index 5394b2e..a6eb3f1 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -1520,14 +1520,14 @@ 'common/extensions/features/base_feature_provider_unittest.cc', 'common/extensions/features/complex_feature_unittest.cc', 'common/extensions/features/simple_feature_unittest.cc', + 'common/extensions/manifest_handlers/content_scripts_manifest_unittest.cc', + 'common/extensions/manifest_handlers/exclude_matches_manifest_unittest.cc', 'common/extensions/manifest_tests/extension_manifest_test.cc', 'common/extensions/manifest_tests/extension_manifests_background_unittest.cc', 'common/extensions/manifest_tests/extension_manifests_chromepermission_unittest.cc', - 'common/extensions/manifest_tests/extension_manifests_contentscript_unittest.cc', 'common/extensions/manifest_tests/extension_manifests_contentsecuritypolicy_unittest.cc', 'common/extensions/manifest_tests/extension_manifests_default_extent_path_unittest.cc', 'common/extensions/manifest_tests/extension_manifests_devtools_unittest.cc', - 'common/extensions/manifest_tests/extension_manifests_excludematches_unittest.cc', 'common/extensions/manifest_tests/extension_manifests_experimental_unittest.cc', 'common/extensions/manifest_tests/extension_manifests_homepage_unittest.cc', 'common/extensions/manifest_tests/extension_manifests_initvalue_unittest.cc', diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc index fe25d22..a83392e 100644 --- a/chrome/common/extensions/extension.cc +++ b/chrome/common/extensions/extension.cc @@ -979,15 +979,6 @@ bool Extension::ShouldDisplayInExtensionSettings() const { return true; } -bool Extension::HasContentScriptAtURL(const GURL& url) const { - for (UserScriptList::const_iterator it = content_scripts_.begin(); - it != content_scripts_.end(); ++it) { - if (it->MatchesURL(url)) - return true; - } - return false; -} - scoped_refptr<const PermissionSet> Extension::GetTabSpecificPermissions( int tab_id) const { base::AutoLock auto_lock(runtime_data_lock_); @@ -1927,46 +1918,13 @@ bool Extension::LoadExtensionFeatures(string16* error) { manifest_->GetBoolean(keys::kConvertedFromUserScript, &converted_from_user_script_); - if (!LoadContentScripts(error) || - !LoadSystemIndicator(error) || + if (!LoadSystemIndicator(error) || !LoadIncognitoMode(error)) return false; return true; } -bool Extension::LoadContentScripts(string16* error) { - if (!manifest_->HasKey(keys::kContentScripts)) - return true; - - const ListValue* list_value; - if (!manifest_->GetList(keys::kContentScripts, &list_value)) { - *error = ASCIIToUTF16(errors::kInvalidContentScriptsList); - return false; - } - - for (size_t i = 0; i < list_value->GetSize(); ++i) { - const DictionaryValue* content_script = NULL; - if (!list_value->GetDictionary(i, &content_script)) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidContentScript, base::IntToString(i)); - return false; - } - - UserScript script; - if (!LoadUserScriptHelper(content_script, i, error, &script)) - return false; // Failed to parse script context definition. - - script.set_extension_id(id()); - if (converted_from_user_script_) { - script.set_emulate_greasemonkey(true); - script.set_match_all_frames(true); // Greasemonkey matches all frames. - } - content_scripts_.push_back(script); - } - return true; -} - bool Extension::LoadSystemIndicator(string16* error) { if (!manifest_->HasKey(keys::kSystemIndicator)) { // There was no manifest entry for the system indicator. @@ -2015,261 +1973,6 @@ bool Extension::LoadIncognitoMode(string16* error) { return true; } -// Helper method that loads a UserScript object from a dictionary in the -// content_script list of the manifest. -bool Extension::LoadUserScriptHelper(const DictionaryValue* content_script, - int definition_index, - string16* error, - UserScript* result) { - // run_at - if (content_script->HasKey(keys::kRunAt)) { - std::string run_location; - if (!content_script->GetString(keys::kRunAt, &run_location)) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidRunAt, - base::IntToString(definition_index)); - return false; - } - - if (run_location == values::kRunAtDocumentStart) { - result->set_run_location(UserScript::DOCUMENT_START); - } else if (run_location == values::kRunAtDocumentEnd) { - result->set_run_location(UserScript::DOCUMENT_END); - } else if (run_location == values::kRunAtDocumentIdle) { - result->set_run_location(UserScript::DOCUMENT_IDLE); - } else { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidRunAt, - base::IntToString(definition_index)); - return false; - } - } - - // all frames - if (content_script->HasKey(keys::kAllFrames)) { - bool all_frames = false; - if (!content_script->GetBoolean(keys::kAllFrames, &all_frames)) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidAllFrames, base::IntToString(definition_index)); - return false; - } - result->set_match_all_frames(all_frames); - } - - // matches (required) - const ListValue* matches = NULL; - if (!content_script->GetList(keys::kMatches, &matches)) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidMatches, - base::IntToString(definition_index)); - return false; - } - - if (matches->GetSize() == 0) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidMatchCount, - base::IntToString(definition_index)); - return false; - } - for (size_t j = 0; j < matches->GetSize(); ++j) { - std::string match_str; - if (!matches->GetString(j, &match_str)) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidMatch, - base::IntToString(definition_index), - base::IntToString(j), - errors::kExpectString); - return false; - } - - URLPattern pattern( - UserScript::ValidUserScriptSchemes(CanExecuteScriptEverywhere())); - - URLPattern::ParseResult parse_result = pattern.Parse(match_str); - if (parse_result != URLPattern::PARSE_SUCCESS) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidMatch, - base::IntToString(definition_index), - base::IntToString(j), - URLPattern::GetParseResultString(parse_result)); - return false; - } - - // TODO(aboxhall): check for webstore - if (!CanExecuteScriptEverywhere() && - pattern.scheme() != chrome::kChromeUIScheme) { - // Exclude SCHEME_CHROMEUI unless it's been explicitly requested. - // If the --extensions-on-chrome-urls flag has not been passed, requesting - // a chrome:// url will cause a parse failure above, so there's no need to - // check the flag here. - pattern.SetValidSchemes( - pattern.valid_schemes() & ~URLPattern::SCHEME_CHROMEUI); - } - - if (pattern.MatchesScheme(chrome::kFileScheme) && - !CanExecuteScriptEverywhere()) { - wants_file_access_ = true; - if (!(creation_flags_ & ALLOW_FILE_ACCESS)) { - pattern.SetValidSchemes( - pattern.valid_schemes() & ~URLPattern::SCHEME_FILE); - } - } - - result->add_url_pattern(pattern); - } - - // exclude_matches - if (content_script->HasKey(keys::kExcludeMatches)) { // optional - const ListValue* exclude_matches = NULL; - if (!content_script->GetList(keys::kExcludeMatches, &exclude_matches)) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidExcludeMatches, - base::IntToString(definition_index)); - return false; - } - - for (size_t j = 0; j < exclude_matches->GetSize(); ++j) { - std::string match_str; - if (!exclude_matches->GetString(j, &match_str)) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidExcludeMatch, - base::IntToString(definition_index), - base::IntToString(j), - errors::kExpectString); - return false; - } - - int valid_schemes = - UserScript::ValidUserScriptSchemes(CanExecuteScriptEverywhere()); - URLPattern pattern(valid_schemes); - URLPattern::ParseResult parse_result = pattern.Parse(match_str); - if (parse_result != URLPattern::PARSE_SUCCESS) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidExcludeMatch, - base::IntToString(definition_index), base::IntToString(j), - URLPattern::GetParseResultString(parse_result)); - return false; - } - - result->add_exclude_url_pattern(pattern); - } - } - - // include/exclude globs (mostly for Greasemonkey compatibility) - if (!LoadGlobsHelper(content_script, definition_index, keys::kIncludeGlobs, - error, &UserScript::add_glob, result)) { - return false; - } - - if (!LoadGlobsHelper(content_script, definition_index, keys::kExcludeGlobs, - error, &UserScript::add_exclude_glob, result)) { - return false; - } - - // js and css keys - const ListValue* js = NULL; - if (content_script->HasKey(keys::kJs) && - !content_script->GetList(keys::kJs, &js)) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidJsList, - base::IntToString(definition_index)); - return false; - } - - const ListValue* css = NULL; - if (content_script->HasKey(keys::kCss) && - !content_script->GetList(keys::kCss, &css)) { - *error = ErrorUtils:: - FormatErrorMessageUTF16(errors::kInvalidCssList, - base::IntToString(definition_index)); - return false; - } - - // The manifest needs to have at least one js or css user script definition. - if (((js ? js->GetSize() : 0) + (css ? css->GetSize() : 0)) == 0) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kMissingFile, - base::IntToString(definition_index)); - return false; - } - - if (js) { - for (size_t script_index = 0; script_index < js->GetSize(); - ++script_index) { - const Value* value; - std::string relative; - if (!js->Get(script_index, &value) || !value->GetAsString(&relative)) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidJs, - base::IntToString(definition_index), - base::IntToString(script_index)); - return false; - } - GURL url = GetResourceURL(relative); - ExtensionResource resource = GetResource(relative); - result->js_scripts().push_back(UserScript::File( - resource.extension_root(), resource.relative_path(), url)); - } - } - - if (css) { - for (size_t script_index = 0; script_index < css->GetSize(); - ++script_index) { - const Value* value; - std::string relative; - if (!css->Get(script_index, &value) || !value->GetAsString(&relative)) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidCss, - base::IntToString(definition_index), - base::IntToString(script_index)); - return false; - } - GURL url = GetResourceURL(relative); - ExtensionResource resource = GetResource(relative); - result->css_scripts().push_back(UserScript::File( - resource.extension_root(), resource.relative_path(), url)); - } - } - - return true; -} - -bool Extension::LoadGlobsHelper( - const DictionaryValue* content_script, - int content_script_index, - const char* globs_property_name, - string16* error, - void(UserScript::*add_method)(const std::string& glob), - UserScript* instance) { - if (!content_script->HasKey(globs_property_name)) - return true; // they are optional - - const ListValue* list = NULL; - if (!content_script->GetList(globs_property_name, &list)) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidGlobList, - base::IntToString(content_script_index), - globs_property_name); - return false; - } - - for (size_t i = 0; i < list->GetSize(); ++i) { - std::string glob; - if (!list->GetString(i, &glob)) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidGlob, - base::IntToString(content_script_index), - globs_property_name, - base::IntToString(i)); - return false; - } - - (instance->*add_method)(glob); - } - - return true; -} - bool Extension::HasMultipleUISurfaces() const { int num_surfaces = 0; diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h index 53e3a8f..86f7f96 100644 --- a/chrome/common/extensions/extension.h +++ b/chrome/common/extensions/extension.h @@ -386,9 +386,6 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // settings page (i.e. chrome://extensions). bool ShouldDisplayInExtensionSettings() const; - // Returns true if the extension has a content script declared at |url|. - bool HasContentScriptAtURL(const GURL& url) const; - // Gets the tab-specific host permissions of |tab_id|, or NULL if there // aren't any. scoped_refptr<const PermissionSet> GetTabSpecificPermissions(int tab_id) @@ -432,7 +429,6 @@ class Extension : public base::RefCountedThreadSafe<Extension> { bool converted_from_user_script() const { return converted_from_user_script_; } - const UserScriptList& content_scripts() const { return content_scripts_; } const ActionInfo* system_indicator_info() const { return system_indicator_info_.get(); } @@ -466,6 +462,12 @@ class Extension : public base::RefCountedThreadSafe<Extension> { bool kiosk_enabled() const { return kiosk_enabled_; } bool offline_enabled() const { return offline_enabled_; } bool wants_file_access() const { return wants_file_access_; } + // TODO(rdevlin.cronin): This is needed for ContentScriptsHandler, and should + // be moved out as part of crbug.com/159265. This should not be used anywhere + // else. + void set_wants_file_access(bool wants_file_access) { + wants_file_access_ = wants_file_access; + } int creation_flags() const { return creation_flags_; } bool from_webstore() const { return (creation_flags_ & FROM_WEBSTORE) != 0; } bool from_bookmark() const { return (creation_flags_ & FROM_BOOKMARK) != 0; } @@ -578,7 +580,6 @@ class Extension : public base::RefCountedThreadSafe<Extension> { bool LoadKioskEnabled(string16* error); bool LoadOfflineEnabled(string16* error); bool LoadExtensionFeatures(string16* error); - bool LoadContentScripts(string16* error); bool LoadBrowserAction(string16* error); bool LoadSystemIndicator(string16* error); bool LoadTextToSpeechVoices(string16* error); @@ -591,22 +592,6 @@ class Extension : public base::RefCountedThreadSafe<Extension> { const base::DictionaryValue* content_pack_value, string16* error); - // Helper method that loads a UserScript object from a - // dictionary in the content_script list of the manifest. - bool LoadUserScriptHelper(const base::DictionaryValue* content_script, - int definition_index, - string16* error, - UserScript* result); - - // Helper method that loads either the include_globs or exclude_globs list - // from an entry in the content_script lists of the manifest. - bool LoadGlobsHelper(const base::DictionaryValue* content_script, - int content_script_index, - const char* globs_property_name, - string16* error, - void(UserScript::*add_method)(const std::string& glob), - UserScript* instance); - // Returns true if the extension has more than one "UI surface". For example, // an extension that has a browser action and a page action. bool HasMultipleUISurfaces() const; @@ -699,9 +684,6 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // different UI if so). bool converted_from_user_script_; - // Paths to the content scripts the extension contains. - UserScriptList content_scripts_; - // The extension's system indicator, if any. scoped_ptr<ActionInfo> system_indicator_info_; diff --git a/chrome/common/extensions/extension_file_util.cc b/chrome/common/extensions/extension_file_util.cc index 1ed7356..4c2fbb0 100644 --- a/chrome/common/extensions/extension_file_util.cc +++ b/chrome/common/extensions/extension_file_util.cc @@ -49,12 +49,6 @@ const base::FilePath::CharType kTempDirectoryName[] = FILE_PATH_LITERAL("Temp"); namespace extension_file_util { -// Returns false and sets the error if script file can't be loaded, -// or if it's not UTF-8 encoded. -static bool IsScriptValid(const base::FilePath& path, - const base::FilePath& relative_path, - int message_id, std::string* error); - base::FilePath InstallExtension(const base::FilePath& unpacked_source_dir, const std::string& id, const std::string& version, @@ -254,41 +248,6 @@ bool ValidateExtension(const Extension* extension, extension, error, warnings)) return false; - // TODO(yoz): Move this to content scripts manifest handler. - // Validate that claimed script resources actually exist, - // and are UTF-8 encoded. - ExtensionResource::SymlinkPolicy symlink_policy; - if ((extension->creation_flags() & - Extension::FOLLOW_SYMLINKS_ANYWHERE) != 0) { - symlink_policy = ExtensionResource::FOLLOW_SYMLINKS_ANYWHERE; - } else { - symlink_policy = ExtensionResource::SYMLINKS_MUST_RESOLVE_WITHIN_ROOT; - } - - for (size_t i = 0; i < extension->content_scripts().size(); ++i) { - const extensions::UserScript& script = extension->content_scripts()[i]; - - for (size_t j = 0; j < script.js_scripts().size(); j++) { - const extensions::UserScript::File& js_script = script.js_scripts()[j]; - const base::FilePath& path = ExtensionResource::GetFilePath( - js_script.extension_root(), js_script.relative_path(), - symlink_policy); - if (!IsScriptValid(path, js_script.relative_path(), - IDS_EXTENSION_LOAD_JAVASCRIPT_FAILED, error)) - return false; - } - - for (size_t j = 0; j < script.css_scripts().size(); j++) { - const extensions::UserScript::File& css_script = script.css_scripts()[j]; - const base::FilePath& path = ExtensionResource::GetFilePath( - css_script.extension_root(), css_script.relative_path(), - symlink_policy); - if (!IsScriptValid(path, css_script.relative_path(), - IDS_EXTENSION_LOAD_CSS_FAILED, error)) - return false; - } - } - // Check children of extension root to see if any of them start with _ and is // not on the reserved list. if (!CheckForIllegalFilenames(extension->path(), error)) { @@ -452,29 +411,6 @@ SubstitutionMap* LoadMessageBundleSubstitutionMap( return returnValue; } -static bool IsScriptValid(const base::FilePath& path, - const base::FilePath& relative_path, - int message_id, - std::string* error) { - std::string content; - if (!file_util::PathExists(path) || - !file_util::ReadFileToString(path, &content)) { - *error = l10n_util::GetStringFUTF8( - message_id, - relative_path.LossyDisplayName()); - return false; - } - - if (!IsStringUTF8(content)) { - *error = l10n_util::GetStringFUTF8( - IDS_EXTENSION_BAD_FILE_ENCODING, - relative_path.LossyDisplayName()); - return false; - } - - return true; -} - bool CheckForIllegalFilenames(const base::FilePath& extension_path, std::string* error) { // Reserved underscore names. diff --git a/chrome/common/extensions/extension_file_util_unittest.cc b/chrome/common/extensions/extension_file_util_unittest.cc index db0e38b..cb9260f 100644 --- a/chrome/common/extensions/extension_file_util_unittest.cc +++ b/chrome/common/extensions/extension_file_util_unittest.cc @@ -20,6 +20,7 @@ #include "chrome/common/extensions/extension_manifest_constants.h" #include "chrome/common/extensions/manifest.h" #include "chrome/common/extensions/manifest_handler.h" +#include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h" #include "extensions/common/constants.h" #include "grit/generated_resources.h" #include "testing/gmock/include/gmock/gmock.h" @@ -40,6 +41,7 @@ class ExtensionFileUtilTest : public testing::Test { (new extensions::DefaultLocaleHandler)->Register(); (new extensions::IconsHandler)->Register(); (new extensions::PageActionHandler)->Register(); + (new extensions::ContentScriptsHandler)->Register(); } virtual void TearDown() OVERRIDE { diff --git a/chrome/common/extensions/extension_unittest.cc b/chrome/common/extensions/extension_unittest.cc index 8f01536..696de8b 100644 --- a/chrome/common/extensions/extension_unittest.cc +++ b/chrome/common/extensions/extension_unittest.cc @@ -24,6 +24,7 @@ #include "chrome/common/extensions/features/feature.h" #include "chrome/common/extensions/manifest.h" #include "chrome/common/extensions/manifest_handler.h" +#include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h" #include "chrome/common/extensions/permissions/api_permission.h" #include "chrome/common/extensions/permissions/permission_set.h" #include "chrome/common/extensions/permissions/socket_permission.h" @@ -109,6 +110,7 @@ class ExtensionTest : public testing::Test { testing::Test::SetUp(); (new BackgroundManifestHandler)->Register(); (new CommandsHandler)->Register(); + (new ContentScriptsHandler)->Register(); (new PluginsHandler)->Register(); } @@ -479,34 +481,58 @@ TEST_F(ExtensionTest, WantsFileAccess) { extension = LoadManifest("permissions", "content_script_all_urls.json"); EXPECT_TRUE(extension->wants_file_access()); EXPECT_FALSE(extension->CanExecuteScriptOnPage( - file_url, file_url, -1, &extension->content_scripts()[0], NULL)); + file_url, + file_url, + -1, + &ContentScriptsInfo::GetContentScripts(extension)[0], + NULL)); extension = LoadManifest("permissions", "content_script_all_urls.json", Extension::ALLOW_FILE_ACCESS); EXPECT_TRUE(extension->wants_file_access()); EXPECT_TRUE(extension->CanExecuteScriptOnPage( - file_url, file_url, -1, &extension->content_scripts()[0], NULL)); + file_url, + file_url, + -1, + &ContentScriptsInfo::GetContentScripts(extension)[0], + NULL)); // file:///* content script match extension = LoadManifest("permissions", "content_script_file_scheme.json"); EXPECT_TRUE(extension->wants_file_access()); EXPECT_FALSE(extension->CanExecuteScriptOnPage( - file_url, file_url, -1, &extension->content_scripts()[0], NULL)); + file_url, + file_url, + -1, + &ContentScriptsInfo::GetContentScripts(extension)[0], + NULL)); extension = LoadManifest("permissions", "content_script_file_scheme.json", Extension::ALLOW_FILE_ACCESS); EXPECT_TRUE(extension->wants_file_access()); EXPECT_TRUE(extension->CanExecuteScriptOnPage( - file_url, file_url, -1, &extension->content_scripts()[0], NULL)); + file_url, + file_url, + -1, + &ContentScriptsInfo::GetContentScripts(extension)[0], + NULL)); // http://* content script match extension = LoadManifest("permissions", "content_script_http_scheme.json"); EXPECT_FALSE(extension->wants_file_access()); EXPECT_FALSE(extension->CanExecuteScriptOnPage( - file_url, file_url, -1, &extension->content_scripts()[0], NULL)); + file_url, + file_url, + -1, + &ContentScriptsInfo::GetContentScripts(extension)[0], + NULL)); extension = LoadManifest("permissions", "content_script_http_scheme.json", Extension::ALLOW_FILE_ACCESS); EXPECT_FALSE(extension->wants_file_access()); EXPECT_FALSE(extension->CanExecuteScriptOnPage( - file_url, file_url, -1, &extension->content_scripts()[0], NULL)); + file_url, + file_url, + -1, + &ContentScriptsInfo::GetContentScripts(extension)[0], + NULL)); } TEST_F(ExtensionTest, ExtraFlags) { diff --git a/chrome/common/extensions/manifest_handlers/content_scripts_handler.cc b/chrome/common/extensions/manifest_handlers/content_scripts_handler.cc new file mode 100644 index 0000000..a2edb9a --- /dev/null +++ b/chrome/common/extensions/manifest_handlers/content_scripts_handler.cc @@ -0,0 +1,446 @@ +// Copyright (c) 2013 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/manifest_handlers/content_scripts_handler.h" + +#include "base/file_util.h" +#include "base/lazy_instance.h" +#include "base/memory/scoped_ptr.h" +#include "base/string_number_conversions.h" +#include "base/string_util.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_manifest_constants.h" +#include "content/public/common/url_constants.h" +#include "extensions/common/error_utils.h" +#include "extensions/common/extension_resource.h" +#include "extensions/common/url_pattern.h" +#include "googleurl/src/gurl.h" +#include "grit/generated_resources.h" +#include "ui/base/l10n/l10n_util.h" + +namespace extensions { + +namespace keys = extension_manifest_keys; +namespace values = extension_manifest_values; +namespace errors = extension_manifest_errors; + +namespace { + +// Helper method that loads either the include_globs or exclude_globs list +// from an entry in the content_script lists of the manifest. +bool LoadGlobsHelper(const DictionaryValue* content_script, + int content_script_index, + const char* globs_property_name, + string16* error, + void(UserScript::*add_method)(const std::string& glob), + UserScript* instance) { + if (!content_script->HasKey(globs_property_name)) + return true; // they are optional + + const ListValue* list = NULL; + if (!content_script->GetList(globs_property_name, &list)) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidGlobList, + base::IntToString(content_script_index), + globs_property_name); + return false; + } + + for (size_t i = 0; i < list->GetSize(); ++i) { + std::string glob; + if (!list->GetString(i, &glob)) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidGlob, + base::IntToString(content_script_index), + globs_property_name, + base::IntToString(i)); + return false; + } + + (instance->*add_method)(glob); + } + + return true; +} + +// Helper method that loads a UserScript object from a dictionary in the +// content_script list of the manifest. +bool LoadUserScriptFromDictionary(const DictionaryValue* content_script, + int definition_index, + Extension* extension, + string16* error, + UserScript* result) { + // run_at + if (content_script->HasKey(keys::kRunAt)) { + std::string run_location; + if (!content_script->GetString(keys::kRunAt, &run_location)) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidRunAt, + base::IntToString(definition_index)); + return false; + } + + if (run_location == values::kRunAtDocumentStart) { + result->set_run_location(UserScript::DOCUMENT_START); + } else if (run_location == values::kRunAtDocumentEnd) { + result->set_run_location(UserScript::DOCUMENT_END); + } else if (run_location == values::kRunAtDocumentIdle) { + result->set_run_location(UserScript::DOCUMENT_IDLE); + } else { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidRunAt, + base::IntToString(definition_index)); + return false; + } + } + + // all frames + if (content_script->HasKey(keys::kAllFrames)) { + bool all_frames = false; + if (!content_script->GetBoolean(keys::kAllFrames, &all_frames)) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidAllFrames, base::IntToString(definition_index)); + return false; + } + result->set_match_all_frames(all_frames); + } + + // matches (required) + const ListValue* matches = NULL; + if (!content_script->GetList(keys::kMatches, &matches)) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidMatches, + base::IntToString(definition_index)); + return false; + } + + if (matches->GetSize() == 0) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidMatchCount, + base::IntToString(definition_index)); + return false; + } + for (size_t j = 0; j < matches->GetSize(); ++j) { + std::string match_str; + if (!matches->GetString(j, &match_str)) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidMatch, + base::IntToString(definition_index), + base::IntToString(j), + errors::kExpectString); + return false; + } + + URLPattern pattern(UserScript::ValidUserScriptSchemes( + extension->CanExecuteScriptEverywhere())); + + URLPattern::ParseResult parse_result = pattern.Parse(match_str); + if (parse_result != URLPattern::PARSE_SUCCESS) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidMatch, + base::IntToString(definition_index), + base::IntToString(j), + URLPattern::GetParseResultString(parse_result)); + return false; + } + + // TODO(aboxhall): check for webstore + if (!extension->CanExecuteScriptEverywhere() && + pattern.scheme() != chrome::kChromeUIScheme) { + // Exclude SCHEME_CHROMEUI unless it's been explicitly requested. + // If the --extensions-on-chrome-urls flag has not been passed, requesting + // a chrome:// url will cause a parse failure above, so there's no need to + // check the flag here. + pattern.SetValidSchemes( + pattern.valid_schemes() & ~URLPattern::SCHEME_CHROMEUI); + } + + if (pattern.MatchesScheme(chrome::kFileScheme) && + !extension->CanExecuteScriptEverywhere()) { + extension->set_wants_file_access(true); + if (!(extension->creation_flags() & Extension::ALLOW_FILE_ACCESS)) { + pattern.SetValidSchemes( + pattern.valid_schemes() & ~URLPattern::SCHEME_FILE); + } + } + + result->add_url_pattern(pattern); + } + + // exclude_matches + if (content_script->HasKey(keys::kExcludeMatches)) { // optional + const ListValue* exclude_matches = NULL; + if (!content_script->GetList(keys::kExcludeMatches, &exclude_matches)) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidExcludeMatches, + base::IntToString(definition_index)); + return false; + } + + for (size_t j = 0; j < exclude_matches->GetSize(); ++j) { + std::string match_str; + if (!exclude_matches->GetString(j, &match_str)) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidExcludeMatch, + base::IntToString(definition_index), + base::IntToString(j), + errors::kExpectString); + return false; + } + + int valid_schemes = UserScript::ValidUserScriptSchemes( + extension->CanExecuteScriptEverywhere()); + URLPattern pattern(valid_schemes); + + URLPattern::ParseResult parse_result = pattern.Parse(match_str); + if (parse_result != URLPattern::PARSE_SUCCESS) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidExcludeMatch, + base::IntToString(definition_index), base::IntToString(j), + URLPattern::GetParseResultString(parse_result)); + return false; + } + + result->add_exclude_url_pattern(pattern); + } + } + + // include/exclude globs (mostly for Greasemonkey compatibility) + if (!LoadGlobsHelper(content_script, definition_index, keys::kIncludeGlobs, + error, &UserScript::add_glob, result)) { + return false; + } + + if (!LoadGlobsHelper(content_script, definition_index, keys::kExcludeGlobs, + error, &UserScript::add_exclude_glob, result)) { + return false; + } + + // js and css keys + const ListValue* js = NULL; + if (content_script->HasKey(keys::kJs) && + !content_script->GetList(keys::kJs, &js)) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidJsList, + base::IntToString(definition_index)); + return false; + } + + const ListValue* css = NULL; + if (content_script->HasKey(keys::kCss) && + !content_script->GetList(keys::kCss, &css)) { + *error = ErrorUtils:: + FormatErrorMessageUTF16(errors::kInvalidCssList, + base::IntToString(definition_index)); + return false; + } + + // The manifest needs to have at least one js or css user script definition. + if (((js ? js->GetSize() : 0) + (css ? css->GetSize() : 0)) == 0) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kMissingFile, + base::IntToString(definition_index)); + return false; + } + + if (js) { + for (size_t script_index = 0; script_index < js->GetSize(); + ++script_index) { + const Value* value; + std::string relative; + if (!js->Get(script_index, &value) || !value->GetAsString(&relative)) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidJs, + base::IntToString(definition_index), + base::IntToString(script_index)); + return false; + } + GURL url = extension->GetResourceURL(relative); + ExtensionResource resource = extension->GetResource(relative); + result->js_scripts().push_back(UserScript::File( + resource.extension_root(), resource.relative_path(), url)); + } + } + + if (css) { + for (size_t script_index = 0; script_index < css->GetSize(); + ++script_index) { + const Value* value; + std::string relative; + if (!css->Get(script_index, &value) || !value->GetAsString(&relative)) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidCss, + base::IntToString(definition_index), + base::IntToString(script_index)); + return false; + } + GURL url = extension->GetResourceURL(relative); + ExtensionResource resource = extension->GetResource(relative); + result->css_scripts().push_back(UserScript::File( + resource.extension_root(), resource.relative_path(), url)); + } + } + + return true; +} + +// Returns false and sets the error if script file can't be loaded, +// or if it's not UTF-8 encoded. +static bool IsScriptValid(const base::FilePath& path, + const base::FilePath& relative_path, + int message_id, + std::string* error) { + std::string content; + if (!file_util::PathExists(path) || + !file_util::ReadFileToString(path, &content)) { + *error = l10n_util::GetStringFUTF8( + message_id, + relative_path.LossyDisplayName()); + return false; + } + + if (!IsStringUTF8(content)) { + *error = l10n_util::GetStringFUTF8( + IDS_EXTENSION_BAD_FILE_ENCODING, + relative_path.LossyDisplayName()); + return false; + } + + return true; +} + +struct EmptyUserScriptList { + UserScriptList user_script_list; +}; + +static base::LazyInstance<EmptyUserScriptList> g_empty_script_list = + LAZY_INSTANCE_INITIALIZER; + +} // namespace + +ContentScriptsInfo::ContentScriptsInfo() { +} + +ContentScriptsInfo::~ContentScriptsInfo() { +} + +// static +const UserScriptList& ContentScriptsInfo::GetContentScripts( + const Extension* extension) { + ContentScriptsInfo* info = static_cast<ContentScriptsInfo*>( + extension->GetManifestData(keys::kContentScripts)); + return info ? info->content_scripts + : g_empty_script_list.Get().user_script_list; +} + +// static +bool ContentScriptsInfo::ExtensionHasScriptAtURL(const Extension* extension, + const GURL& url) { + const UserScriptList& content_scripts = GetContentScripts(extension); + for (UserScriptList::const_iterator iter = content_scripts.begin(); + iter != content_scripts.end(); ++iter) { + if (iter->MatchesURL(url)) + return true; + } + return false; +} + +ContentScriptsHandler::ContentScriptsHandler() { +} + +ContentScriptsHandler::~ContentScriptsHandler() { +} + +const std::vector<std::string> ContentScriptsHandler::Keys() const { + static const char* keys[] = { + keys::kContentScripts + }; + return std::vector<std::string>(keys, keys + arraysize(keys)); +} + +bool ContentScriptsHandler::Parse(Extension* extension, string16* error) { + scoped_ptr<ContentScriptsInfo> content_scripts_info(new ContentScriptsInfo); + const ListValue* scripts_list = NULL; + if (!extension->manifest()->GetList(keys::kContentScripts, &scripts_list)) { + *error = ASCIIToUTF16(errors::kInvalidContentScriptsList); + return false; + } + + for (size_t i = 0; i < scripts_list->GetSize(); ++i) { + const DictionaryValue* script_dict = NULL; + if (!scripts_list->GetDictionary(i, &script_dict)) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidContentScript, + base::IntToString(i)); + return false; + } + + UserScript user_script; + if (!LoadUserScriptFromDictionary(script_dict, + i, + extension, + error, + &user_script)) { + return false; // Failed to parse script context definition. + } + + user_script.set_extension_id(extension->id()); + if (extension->converted_from_user_script()) { + user_script.set_emulate_greasemonkey(true); + // Greasemonkey matches all frames. + user_script.set_match_all_frames(true); + } + content_scripts_info->content_scripts.push_back(user_script); + } + extension->SetManifestData(keys::kContentScripts, + content_scripts_info.release()); + return true; +} + +bool ContentScriptsHandler::Validate( + const Extension* extension, + std::string* error, + std::vector<InstallWarning>* warnings) const { + // Validate that claimed script resources actually exist, + // and are UTF-8 encoded. + ExtensionResource::SymlinkPolicy symlink_policy; + if ((extension->creation_flags() & + Extension::FOLLOW_SYMLINKS_ANYWHERE) != 0) { + symlink_policy = ExtensionResource::FOLLOW_SYMLINKS_ANYWHERE; + } else { + symlink_policy = ExtensionResource::SYMLINKS_MUST_RESOLVE_WITHIN_ROOT; + } + + const extensions::UserScriptList& content_scripts = + extensions::ContentScriptsInfo::GetContentScripts(extension); + for (size_t i = 0; i < content_scripts.size(); ++i) { + const extensions::UserScript& script = content_scripts[i]; + + for (size_t j = 0; j < script.js_scripts().size(); j++) { + const extensions::UserScript::File& js_script = script.js_scripts()[j]; + const base::FilePath& path = ExtensionResource::GetFilePath( + js_script.extension_root(), js_script.relative_path(), + symlink_policy); + if (!IsScriptValid(path, js_script.relative_path(), + IDS_EXTENSION_LOAD_JAVASCRIPT_FAILED, error)) + return false; + } + + for (size_t j = 0; j < script.css_scripts().size(); j++) { + const extensions::UserScript::File& css_script = script.css_scripts()[j]; + const base::FilePath& path = ExtensionResource::GetFilePath( + css_script.extension_root(), css_script.relative_path(), + symlink_policy); + if (!IsScriptValid(path, css_script.relative_path(), + IDS_EXTENSION_LOAD_CSS_FAILED, error)) + return false; + } + } + + return true; +} + +} // namespace extensions diff --git a/chrome/common/extensions/manifest_handlers/content_scripts_handler.h b/chrome/common/extensions/manifest_handlers/content_scripts_handler.h new file mode 100644 index 0000000..c3b8d06 --- /dev/null +++ b/chrome/common/extensions/manifest_handlers/content_scripts_handler.h @@ -0,0 +1,51 @@ +// Copyright (c) 2013 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. + +#ifndef CHROME_COMMON_EXTENSIONS_MANIFEST_HANDLERS_CONTENT_SCRIPTS_HANDLER_H_ +#define CHROME_COMMON_EXTENSIONS_MANIFEST_HANDLERS_CONTENT_SCRIPTS_HANDLER_H_ + +#include <string> + +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/manifest_handler.h" +#include "chrome/common/extensions/user_script.h" + +namespace extensions { + +struct ContentScriptsInfo : public Extension::ManifestData { + ContentScriptsInfo(); + virtual ~ContentScriptsInfo(); + + // Paths to the content scripts the extension contains (possibly empty). + UserScriptList content_scripts; + + // Returns the content scripts for the extension (if the extension has + // no content scripts, this returns an empty list). + static const UserScriptList& GetContentScripts(const Extension* extension); + + // Returns true if the extension has a content script declared at |url|. + static bool ExtensionHasScriptAtURL(const Extension* extension, + const GURL& url); +}; + +// Parses the "content_scripts" manifest key. +class ContentScriptsHandler : public ManifestHandler { + public: + ContentScriptsHandler(); + virtual ~ContentScriptsHandler(); + + virtual bool Parse(Extension* extension, string16* error) OVERRIDE; + virtual bool Validate(const Extension* extension, + std::string* error, + std::vector<InstallWarning>* warnings) const OVERRIDE; + + private: + virtual const std::vector<std::string> Keys() const OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(ContentScriptsHandler); +}; + +} // namespace extensions + +#endif // CHROME_COMMON_EXTENSIONS_MANIFEST_HANDLERS_CONTENT_SCRIPTS_HANDLER_H_ diff --git a/chrome/common/extensions/manifest_tests/extension_manifests_contentscript_unittest.cc b/chrome/common/extensions/manifest_handlers/content_scripts_manifest_unittest.cc index 8470893..679357c0 100644 --- a/chrome/common/extensions/manifest_tests/extension_manifests_contentscript_unittest.cc +++ b/chrome/common/extensions/manifest_handlers/content_scripts_manifest_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 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. @@ -7,17 +7,28 @@ #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_manifest_constants.h" +#include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h" #include "chrome/common/extensions/manifest_tests/extension_manifest_test.h" #include "extensions/common/error_utils.h" #include "testing/gtest/include/gtest/gtest.h" namespace errors = extension_manifest_errors; -TEST_F(ExtensionManifestTest, ContentScriptMatchPattern) { +namespace extensions { + +class ContentScriptsManifestTest : public ExtensionManifestTest { + protected: + virtual void SetUp() OVERRIDE { + ExtensionManifestTest::SetUp(); + (new ContentScriptsHandler)->Register(); + } +}; + +TEST_F(ContentScriptsManifestTest, ContentScriptMatchPattern) { Testcase testcases[] = { // chrome:// urls are not allowed. Testcase("content_script_chrome_url_invalid.json", - extensions::ErrorUtils::FormatErrorMessage( + ErrorUtils::FormatErrorMessage( errors::kInvalidMatch, base::IntToString(0), base::IntToString(0), @@ -26,10 +37,10 @@ TEST_F(ExtensionManifestTest, ContentScriptMatchPattern) { // Match paterns must be strings. Testcase("content_script_match_pattern_not_string.json", - extensions::ErrorUtils::FormatErrorMessage(errors::kInvalidMatch, - base::IntToString(0), - base::IntToString(0), - errors::kExpectString)) + ErrorUtils::FormatErrorMessage(errors::kInvalidMatch, + base::IntToString(0), + base::IntToString(0), + errors::kExpectString)) }; RunTestcases(testcases, arraysize(testcases), EXPECT_TYPE_ERROR); @@ -37,7 +48,7 @@ TEST_F(ExtensionManifestTest, ContentScriptMatchPattern) { LoadAndExpectSuccess("ports_in_content_scripts.json"); } -TEST_F(ExtensionManifestTest, ContentScriptsOnChromeUrlsWithFlag) { +TEST_F(ContentScriptsManifestTest, ContentScriptsOnChromeUrlsWithFlag) { CommandLine::ForCurrentProcess()->AppendSwitch( switches::kExtensionsOnChromeURLs); std::string error; @@ -45,5 +56,8 @@ TEST_F(ExtensionManifestTest, ContentScriptsOnChromeUrlsWithFlag) { LoadAndExpectSuccess("content_script_chrome_url_invalid.json"); EXPECT_EQ("", error); const GURL newtab_url("chrome://newtab/"); - EXPECT_TRUE(extension->HasContentScriptAtURL(newtab_url)); + EXPECT_TRUE(ContentScriptsInfo::ExtensionHasScriptAtURL(extension, + newtab_url)); } + +} // namespace extensions diff --git a/chrome/common/extensions/manifest_tests/extension_manifests_excludematches_unittest.cc b/chrome/common/extensions/manifest_handlers/exclude_matches_manifest_unittest.cc index 0f01638..37a4ae5 100644 --- a/chrome/common/extensions/manifest_tests/extension_manifests_excludematches_unittest.cc +++ b/chrome/common/extensions/manifest_handlers/exclude_matches_manifest_unittest.cc @@ -1,13 +1,24 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 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/manifest_tests/extension_manifest_test.h" #include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h" +#include "chrome/common/extensions/manifest_tests/extension_manifest_test.h" #include "testing/gtest/include/gtest/gtest.h" -TEST_F(ExtensionManifestTest, ExcludeMatchPatterns) { +namespace extensions { + +class ExcludeMatchesManifestTest : public ExtensionManifestTest { + protected: + virtual void SetUp() OVERRIDE { + ExtensionManifestTest::SetUp(); + (new ContentScriptsHandler)->Register(); + } +}; + +TEST_F(ExcludeMatchesManifestTest, ExcludeMatchPatterns) { Testcase testcases[] = { Testcase("exclude_matches.json"), Testcase("exclude_matches_empty.json") @@ -25,3 +36,5 @@ TEST_F(ExtensionManifestTest, ExcludeMatchPatterns) { RunTestcases(testcases2, arraysize(testcases2), EXPECT_TYPE_ERROR); } + +} // namespace extensions diff --git a/chrome/common/extensions/manifest_tests/extension_manifests_initvalue_unittest.cc b/chrome/common/extensions/manifest_tests/extension_manifests_initvalue_unittest.cc index 9dc40e4..580e52e 100644 --- a/chrome/common/extensions/manifest_tests/extension_manifests_initvalue_unittest.cc +++ b/chrome/common/extensions/manifest_tests/extension_manifests_initvalue_unittest.cc @@ -12,6 +12,7 @@ #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_manifest_constants.h" #include "chrome/common/extensions/manifest_handler.h" +#include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h" #include "chrome/common/extensions/manifest_tests/extension_manifest_test.h" #include "chrome/common/extensions/manifest_url_handler.h" #include "testing/gtest/include/gtest/gtest.h" @@ -34,6 +35,7 @@ class InitValueManifestTest : public ExtensionManifestTest { (new extensions::IconsHandler)->Register(); (new extensions::OptionsPageHandler)->Register(); (new extensions::PageActionHandler)->Register(); + (new extensions::ContentScriptsHandler)->Register(); } }; diff --git a/chrome/common/extensions/permissions/permission_set.cc b/chrome/common/extensions/permissions/permission_set.cc index 380f72a..704255b 100644 --- a/chrome/common/extensions/permissions/permission_set.cc +++ b/chrome/common/extensions/permissions/permission_set.cc @@ -10,6 +10,7 @@ #include "chrome/common/extensions/api/plugins/plugins_handler.h" #include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h" #include "chrome/common/extensions/manifest_url_handler.h" #include "chrome/common/extensions/permissions/permissions_info.h" #include "content/public/common/url_constants.h" @@ -535,8 +536,9 @@ void PermissionSet::InitImplicitExtensionPermissions( // Add the scriptable hosts. for (extensions::UserScriptList::const_iterator content_script = - extension->content_scripts().begin(); - content_script != extension->content_scripts().end(); ++content_script) { + ContentScriptsInfo::GetContentScripts(extension).begin(); + content_script != ContentScriptsInfo::GetContentScripts(extension).end(); + ++content_script) { URLPatternSet::const_iterator pattern = content_script->url_patterns().begin(); for (; pattern != content_script->url_patterns().end(); ++pattern) diff --git a/chrome/common/extensions/permissions/permission_set_unittest.cc b/chrome/common/extensions/permissions/permission_set_unittest.cc index 478aa5e..c569f49 100644 --- a/chrome/common/extensions/permissions/permission_set_unittest.cc +++ b/chrome/common/extensions/permissions/permission_set_unittest.cc @@ -14,6 +14,7 @@ #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/features/feature.h" #include "chrome/common/extensions/manifest_handler.h" +#include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h" #include "chrome/common/extensions/permissions/permission_set.h" #include "chrome/common/extensions/permissions/permissions_info.h" #include "chrome/common/extensions/permissions/socket_permission.h" @@ -77,11 +78,12 @@ bool Contains(const std::vector<string16>& warnings, } // namespace - class PermissionsTest : public testing::Test { + protected: virtual void SetUp() OVERRIDE { testing::Test::SetUp(); (new BackgroundManifestHandler)->Register(); + (new ContentScriptsHandler)->Register(); (new PluginsHandler)->Register(); } |