diff options
Diffstat (limited to 'chrome/common/extensions')
-rw-r--r-- | chrome/common/extensions/extension.cc | 52 | ||||
-rw-r--r-- | chrome/common/extensions/extension.h | 16 | ||||
-rw-r--r-- | chrome/common/extensions/extension_constants.cc | 7 | ||||
-rw-r--r-- | chrome/common/extensions/extension_constants.h | 5 | ||||
-rw-r--r-- | chrome/common/extensions/extension_error_utils.cc | 18 | ||||
-rw-r--r-- | chrome/common/extensions/extension_error_utils.h | 16 | ||||
-rw-r--r-- | chrome/common/extensions/user_script.cc | 75 | ||||
-rw-r--r-- | chrome/common/extensions/user_script.h | 55 | ||||
-rw-r--r-- | chrome/common/extensions/user_script_unittest.cc | 34 |
9 files changed, 253 insertions, 25 deletions
diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc index c617232..0aad3de 100644 --- a/chrome/common/extensions/extension.cc +++ b/chrome/common/extensions/extension.cc @@ -248,6 +248,17 @@ bool Extension::LoadUserScriptHelper(const DictionaryValue* content_script, result->add_url_pattern(pattern); } + // include/exclude globs (mostly for Greasemonkey compat) + 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 ListValue* js = NULL; if (content_script->HasKey(keys::kJs) && @@ -311,6 +322,38 @@ bool Extension::LoadUserScriptHelper(const DictionaryValue* content_script, return true; } +bool Extension::LoadGlobsHelper( + const DictionaryValue* content_script, + int content_script_index, + const wchar_t* globs_property_name, + std::string* error, + void (UserScript::*add_method) (const std::string& glob), + UserScript *instance) { + if (!content_script->HasKey(globs_property_name)) + return true; // they are optional + + ListValue* list = NULL; + if (!content_script->GetList(globs_property_name, &list)) { + *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidGlobList, + IntToString(content_script_index), WideToASCII(globs_property_name)); + return false; + } + + for (size_t i = 0; i < list->GetSize(); ++i) { + std::string glob; + if (!list->GetString(i, &glob)) { + *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidGlob, + IntToString(content_script_index), WideToASCII(globs_property_name), + IntToString(i)); + return false; + } + + (instance->*add_method)(glob); + } + + return true; +} + ExtensionAction* Extension::LoadExtensionActionHelper( const DictionaryValue* extension_action, std::string* error) { scoped_ptr<ExtensionAction> result(new ExtensionAction()); @@ -450,7 +493,8 @@ ExtensionResource Extension::GetResource(const FilePath& extension_path, } Extension::Extension(const FilePath& path) - : is_theme_(false), background_page_ready_(false) { + : converted_from_user_script_(false), is_theme_(false), + background_page_ready_(false) { DCHECK(path.IsAbsolute()); location_ = INVALID; @@ -668,6 +712,10 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_id, } } + // Initialize converted_from_user_script (if present) + source.GetBoolean(keys::kConvertedFromUserScript, + &converted_from_user_script_); + // Initialize icons (if present). if (source.HasKey(keys::kIcons)) { DictionaryValue* icons_value = NULL; @@ -943,6 +991,8 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_id, 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); content_scripts_.push_back(script); } } diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h index 050515b..d240388 100644 --- a/chrome/common/extensions/extension.h +++ b/chrome/common/extensions/extension.h @@ -195,6 +195,9 @@ class Extension { const std::string& name() const { return name_; } const std::string& public_key() const { return public_key_; } const std::string& description() const { return description_; } + bool converted_from_user_script() const { + return converted_from_user_script_; + } const UserScriptList& content_scripts() const { return content_scripts_; } ExtensionAction* page_action() const { return page_action_.get(); } ExtensionAction* browser_action() const { return browser_action_.get(); } @@ -290,6 +293,15 @@ class Extension { std::string* 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 DictionaryValue* content_script, + int content_script_index, + const wchar_t* globs_property_name, + std::string* error, + void (UserScript::*add_method) (const std::string& glob), + UserScript *instance); + // Helper method to load an ExtensionAction from the page_action or // browser_action entries in the manifest. ExtensionAction* LoadExtensionActionHelper( @@ -325,6 +337,10 @@ class Extension { // An optional longer description of the extension. std::string description_; + // True if the extension was generated from a user script. (We show slightly + // different UI if so). + bool converted_from_user_script_; + // Paths to the content scripts the extension contains. UserScriptList content_scripts_; diff --git a/chrome/common/extensions/extension_constants.cc b/chrome/common/extensions/extension_constants.cc index 59e7c14..0dbab1f 100644 --- a/chrome/common/extensions/extension_constants.cc +++ b/chrome/common/extensions/extension_constants.cc @@ -10,12 +10,15 @@ const wchar_t* kBackground = L"background_page"; const wchar_t* kBrowserAction = L"browser_action"; const wchar_t* kChromeURLOverrides = L"chrome_url_overrides"; const wchar_t* kContentScripts = L"content_scripts"; +const wchar_t* kConvertedFromUserScript = L"converted_from_user_script"; const wchar_t* kCss = L"css"; const wchar_t* kDefaultLocale = L"default_locale"; const wchar_t* kDescription = L"description"; const wchar_t* kIcons = L"icons"; const wchar_t* kJs = L"js"; const wchar_t* kMatches = L"matches"; +const wchar_t* kIncludeGlobs = L"include_globs"; +const wchar_t* kExcludeGlobs = L"exclude_globs"; const wchar_t* kName = L"name"; const wchar_t* kPageActionId = L"id"; const wchar_t* kPageAction = L"page_action"; @@ -76,6 +79,10 @@ const char* kInvalidCssList = "Required value 'content_scripts[*].css is invalid."; const char* kInvalidDescription = "Invalid value for 'description'."; +const char* kInvalidGlobList = + "Invalid value for 'content_scripts[*].*'."; +const char* kInvalidGlob = + "Invalid value for 'content_scripts[*].*[*]'."; const char* kInvalidIcons = "Invalid value for 'icons'."; const char* kInvalidIconPath = diff --git a/chrome/common/extensions/extension_constants.h b/chrome/common/extensions/extension_constants.h index b7826a7..d44f7b9 100644 --- a/chrome/common/extensions/extension_constants.h +++ b/chrome/common/extensions/extension_constants.h @@ -11,10 +11,13 @@ namespace extension_manifest_keys { extern const wchar_t* kBrowserAction; extern const wchar_t* kChromeURLOverrides; extern const wchar_t* kContentScripts; + extern const wchar_t* kConvertedFromUserScript; extern const wchar_t* kCss; extern const wchar_t* kDefaultLocale; extern const wchar_t* kDescription; + extern const wchar_t* kExcludeGlobs; extern const wchar_t* kIcons; + extern const wchar_t* kIncludeGlobs; extern const wchar_t* kJs; extern const wchar_t* kMatches; extern const wchar_t* kName; @@ -70,6 +73,8 @@ namespace extension_manifest_errors { extern const char* kInvalidDescription; extern const char* kInvalidIcons; extern const char* kInvalidIconPath; + extern const char* kInvalidGlobList; + extern const char* kInvalidGlob; extern const char* kInvalidJs; extern const char* kInvalidJsList; extern const char* kInvalidKey; diff --git a/chrome/common/extensions/extension_error_utils.cc b/chrome/common/extensions/extension_error_utils.cc index 724cdcd..0256c8d8 100644 --- a/chrome/common/extensions/extension_error_utils.cc +++ b/chrome/common/extensions/extension_error_utils.cc @@ -8,7 +8,7 @@ std::string ExtensionErrorUtils::FormatErrorMessage( const std::string& format, - const std::string s1) { + const std::string& s1) { std::string ret_val = format; ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1); return ret_val; @@ -16,10 +16,22 @@ std::string ExtensionErrorUtils::FormatErrorMessage( std::string ExtensionErrorUtils::FormatErrorMessage( const std::string& format, - const std::string s1, - const std::string s2) { + const std::string& s1, + const std::string& s2) { std::string ret_val = format; ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1); ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s2); return ret_val; } + +std::string ExtensionErrorUtils::FormatErrorMessage( + const std::string& format, + const std::string& s1, + const std::string& s2, + const std::string& s3) { + std::string ret_val = format; + ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1); + ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s2); + ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s3); + return ret_val; +} diff --git a/chrome/common/extensions/extension_error_utils.h b/chrome/common/extensions/extension_error_utils.h index f9f1e7c..fdd9b03 100644 --- a/chrome/common/extensions/extension_error_utils.h +++ b/chrome/common/extensions/extension_error_utils.h @@ -9,16 +9,18 @@ class ExtensionErrorUtils { public: - // Creates an error messages from a pattern. Places first instance if "*" - // with |s1|. + // Creates an error messages from a pattern. static std::string FormatErrorMessage(const std::string& format, - const std::string s1); + const std::string& s1); - // Creates an error messages from a pattern. Places first instance if "*" - // with |s1| and second instance of "*" with |s2|. static std::string FormatErrorMessage(const std::string& format, - const std::string s1, - const std::string s2); + const std::string& s1, + const std::string& s2); + + static std::string FormatErrorMessage(const std::string& format, + const std::string& s1, + const std::string& s2, + const std::string& s3); }; #endif // CHROME_COMMON_EXTENSIONS_EXTENSION_ERROR_UTILS_H_ diff --git a/chrome/common/extensions/user_script.cc b/chrome/common/extensions/user_script.cc index df34e52..10d0b60 100644 --- a/chrome/common/extensions/user_script.cc +++ b/chrome/common/extensions/user_script.cc @@ -7,21 +7,59 @@ #include "base/pickle.h" #include "base/string_util.h" -bool UserScript::MatchesUrl(const GURL& url) const { - for (std::vector<std::string>::const_iterator glob = globs_.begin(); - glob != globs_.end(); ++glob) { - if (MatchPattern(url.spec(), *glob)) +namespace { +static bool UrlMatchesPatterns(const UserScript::PatternList* patterns, + const GURL& url) { + for (UserScript::PatternList::const_iterator pattern = patterns->begin(); + pattern != patterns->end(); ++pattern) { + if (pattern->MatchesUrl(url)) return true; } - for (PatternList::const_iterator pattern = url_patterns_.begin(); - pattern != url_patterns_.end(); ++pattern) { - if (pattern->MatchesUrl(url)) + return false; +} + +static bool UrlMatchesGlobs(const std::vector<std::string>* globs, + const GURL& url) { + for (std::vector<std::string>::const_iterator glob = globs->begin(); + glob != globs->end(); ++glob) { + if (MatchPattern(url.spec(), *glob)) return true; } return false; } +} + +const char UserScript::kFileExtension[] = ".user.js"; + +bool UserScript::HasUserScriptFileExtension(const GURL& url) { + return EndsWith(url.ExtractFileName(), kFileExtension, false); +} + +bool UserScript::HasUserScriptFileExtension(const FilePath& path) { + static FilePath extension(FilePath().AppendASCII(kFileExtension)); + return EndsWith(path.BaseName().value(), extension.value(), false); +} + +bool UserScript::MatchesUrl(const GURL& url) const { + if (url_patterns_.size() > 0) { + if (!UrlMatchesPatterns(&url_patterns_, url)) + return false; + } + + if (globs_.size() > 0) { + if (!UrlMatchesGlobs(&globs_, url)) + return false; + } + + if (exclude_globs_.size() > 0) { + if (UrlMatchesGlobs(&exclude_globs_, url)) + return false; + } + + return true; +} void UserScript::File::Pickle(::Pickle* pickle) const { pickle->WriteString(url_.spec()); @@ -43,10 +81,17 @@ void UserScript::Pickle(::Pickle* pickle) const { // Write the extension id. pickle->WriteString(extension_id()); + // Write Greasemonkey emulation. + pickle->WriteBool(emulate_greasemonkey()); + // Write globs. + std::vector<std::string>::const_iterator glob; pickle->WriteSize(globs_.size()); - for (std::vector<std::string>::const_iterator glob = globs_.begin(); - glob != globs_.end(); ++glob) { + for (glob = globs_.begin(); glob != globs_.end(); ++glob) { + pickle->WriteString(*glob); + } + pickle->WriteSize(exclude_globs_.size()); + for (glob = exclude_globs_.begin(); glob != exclude_globs_.end(); ++glob) { pickle->WriteString(*glob); } @@ -82,10 +127,12 @@ void UserScript::Unpickle(const ::Pickle& pickle, void** iter) { // Read the extension ID. CHECK(pickle.ReadString(iter, &extension_id_)); + // Read Greasemonkey emulation. + CHECK(pickle.ReadBool(iter, &emulate_greasemonkey_)); + // Read globs. size_t num_globs = 0; CHECK(pickle.ReadSize(iter, &num_globs)); - globs_.clear(); for (size_t i = 0; i < num_globs; ++i) { std::string glob; @@ -93,6 +140,14 @@ void UserScript::Unpickle(const ::Pickle& pickle, void** iter) { globs_.push_back(glob); } + CHECK(pickle.ReadSize(iter, &num_globs)); + exclude_globs_.clear(); + for (size_t i = 0; i < num_globs; ++i) { + std::string glob; + CHECK(pickle.ReadString(iter, &glob)); + exclude_globs_.push_back(glob); + } + // Read url patterns. size_t num_patterns = 0; CHECK(pickle.ReadSize(iter, &num_patterns)); diff --git a/chrome/common/extensions/user_script.h b/chrome/common/extensions/user_script.h index bdd1e54..adcfa3f 100644 --- a/chrome/common/extensions/user_script.h +++ b/chrome/common/extensions/user_script.h @@ -22,6 +22,13 @@ class UserScript { public: typedef std::vector<URLPattern> PatternList; + // The file extension for standalone user scripts. + static const char kFileExtension[]; + + // Check if a file or URL has the user script file extension. + static bool HasUserScriptFileExtension(const GURL& url); + static bool HasUserScriptFileExtension(const FilePath& path); + // Locations that user scripts can be run inside the document. enum RunLocation { DOCUMENT_START, // After the documentElemnet is created, but before @@ -92,20 +99,45 @@ class UserScript { typedef std::vector<File> FileList; - // Constructor. Default the run location to document idle, which is similar - // to Greasemonkey but should result in better page load times for fast- - // loading pages. - UserScript() : run_location_(DOCUMENT_IDLE) {} + // Constructor. Default the run location to document end, which is like + // Greasemonkey and probably more useful for typical scripts. + UserScript() + : run_location_(DOCUMENT_IDLE), emulate_greasemonkey_(false) { + } + + const std::string& name_space() const { return name_space_; } + void set_name_space(const std::string& name_space) { + name_space_ = name_space; + } + + const std::string& name() const { return name_; } + void set_name(const std::string& name) { name_ = name; } + + const std::string& description() const { return description_; } + void set_description(const std::string& description) { + description_ = description; + } // The place in the document to run the script. RunLocation run_location() const { return run_location_; } void set_run_location(RunLocation location) { run_location_ = location; } + // Whether to emulate greasemonkey when running this script. + bool emulate_greasemonkey() const { return emulate_greasemonkey_; } + void set_emulate_greasemonkey(bool val) { emulate_greasemonkey_ = val; } + // The globs, if any, that determine which pages this script runs against. // These are only used with "standalone" Greasemonkey-like user scripts. const std::vector<std::string>& globs() const { return globs_; } void add_glob(const std::string& glob) { globs_.push_back(glob); } void clear_globs() { globs_.clear(); } + const std::vector<std::string>& exclude_globs() const { + return exclude_globs_; + } + void add_exclude_glob(const std::string& glob) { + exclude_globs_.push_back(glob); + } + void clear_exclude_globs() { exclude_globs_.clear(); } // The URLPatterns, if any, that determine which pages this script runs // against. @@ -145,9 +177,20 @@ class UserScript { // The location to run the script inside the document. RunLocation run_location_; + // The namespace of the script. This is used by Greasemonkey in the same way + // as XML namespaces. Only used when parsing Greasemonkey-style scripts. + std::string name_space_; + + // The script's name. Only used when parsing Greasemonkey-style scripts. + std::string name_; + + // A longer description. Only used when parsing Greasemonkey-style scripts. + std::string description_; + // Greasemonkey-style globs that determine pages to inject the script into. // These are only used with standalone scripts. std::vector<std::string> globs_; + std::vector<std::string> exclude_globs_; // URLPatterns that determine pages to inject the script into. These are // only used with scripts that are part of extensions. @@ -162,6 +205,10 @@ class UserScript { // The ID of the extension this script is a part of, if any. Can be empty if // the script is a "standlone" user script. std::string extension_id_; + + // Whether we should try to emulate Greasemonkey's APIs when running this + // script. + bool emulate_greasemonkey_; }; typedef std::vector<UserScript> UserScriptList; diff --git a/chrome/common/extensions/user_script_unittest.cc b/chrome/common/extensions/user_script_unittest.cc index 58ef77e..7b5cc6d 100644 --- a/chrome/common/extensions/user_script_unittest.cc +++ b/chrome/common/extensions/user_script_unittest.cc @@ -21,6 +21,10 @@ TEST(UserScriptTest, Match1) { EXPECT_TRUE(script.MatchesUrl(GURL("http://mail.yahoo.com/bar"))); EXPECT_TRUE(script.MatchesUrl(GURL("http://mail.msn.com/baz"))); EXPECT_FALSE(script.MatchesUrl(GURL("http://www.hotmail.com"))); + + script.add_exclude_glob("*foo*"); + EXPECT_TRUE(script.MatchesUrl(GURL("http://mail.google.com"))); + EXPECT_FALSE(script.MatchesUrl(GURL("http://mail.google.com/foo"))); } TEST(UserScriptTest, Match2) { @@ -70,6 +74,36 @@ TEST(UserScriptTest, Match6) { // NOTE: URLPattern is tested more extensively in url_pattern_unittest.cc. } +TEST(UserScriptTest, UrlPatternGlobInteraction) { + // If there are both, match intersection(union(globs), union(urlpatterns)). + UserScript script; + + URLPattern pattern; + ASSERT_TRUE(pattern.Parse("http://www.google.com/*")); + script.add_url_pattern(pattern); + + script.add_glob("*bar*"); + + // No match, because it doesn't match the glob. + EXPECT_FALSE(script.MatchesUrl(GURL("http://www.google.com/foo"))); + + script.add_exclude_glob("*baz*"); + + // No match, because it matches the exclude glob. + EXPECT_FALSE(script.MatchesUrl(GURL("http://www.google.com/baz"))); + + // Match, because it matches the glob, doesn't match the exclude glob. + EXPECT_TRUE(script.MatchesUrl(GURL("http://www.google.com/bar"))); + + // Try with just a single exclude glob. + script.clear_globs(); + EXPECT_TRUE(script.MatchesUrl(GURL("http://www.google.com/foo"))); + + // Try with no globs or exclude globs. + script.clear_exclude_globs(); + EXPECT_TRUE(script.MatchesUrl(GURL("http://www.google.com/foo"))); +} + TEST(UserScriptTest, Pickle) { URLPattern pattern1; URLPattern pattern2; |