diff options
author | yoz@chromium.org <yoz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-19 08:50:27 +0000 |
---|---|---|
committer | yoz@chromium.org <yoz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-19 08:50:27 +0000 |
commit | 49d9b14df40752a2b5063a7b5910554b125c301a (patch) | |
tree | 6a10013d63ff7fad4f4828110fcd583b390cb4b2 /extensions | |
parent | b135ba3c1dd4256c18472a24f931610c954981b5 (diff) | |
download | chromium_src-49d9b14df40752a2b5063a7b5910554b125c301a.zip chromium_src-49d9b14df40752a2b5063a7b5910554b125c301a.tar.gz chromium_src-49d9b14df40752a2b5063a7b5910554b125c301a.tar.bz2 |
Move UserScript and Extension switches to top-level extensions/.
BUG=162530
Review URL: https://chromiumcodereview.appspot.com/19678004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@212549 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'extensions')
-rw-r--r-- | extensions/common/switches.cc | 32 | ||||
-rw-r--r-- | extensions/common/switches.h | 23 | ||||
-rw-r--r-- | extensions/common/user_script.cc | 236 | ||||
-rw-r--r-- | extensions/common/user_script.h | 260 | ||||
-rw-r--r-- | extensions/common/user_script_unittest.cc | 220 |
5 files changed, 771 insertions, 0 deletions
diff --git a/extensions/common/switches.cc b/extensions/common/switches.cc new file mode 100644 index 0000000..f85ff4b --- /dev/null +++ b/extensions/common/switches.cc @@ -0,0 +1,32 @@ +// Copyright 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 "extensions/common/switches.h" + +namespace extensions { + +namespace switches { + +// Allows the browser to load extensions that lack a modern manifest when that +// would otherwise be forbidden. +const char kAllowLegacyExtensionManifests[] = + "allow-legacy-extension-manifests"; + +// Allows injecting extensions and user scripts on the extensions gallery +// site. Normally prevented for security reasons, but can be useful for +// automation testing of the gallery. +const char kAllowScriptingGallery[] = "allow-scripting-gallery"; + +// Enables extensions running scripts on chrome:// URLs. +// Extensions still need to explicitly request access to chrome:// URLs in the +// manifest. +const char kExtensionsOnChromeURLs[] = "extensions-on-chrome-urls"; + +// Makes component extensions appear in chrome://settings/extensions. +const char kShowComponentExtensionOptions[] = + "show-component-extension-options"; + +} // namespace switches + +} // namespace extensions diff --git a/extensions/common/switches.h b/extensions/common/switches.h new file mode 100644 index 0000000..c24d35b --- /dev/null +++ b/extensions/common/switches.h @@ -0,0 +1,23 @@ +// Copyright 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 EXTENSIONS_COMMON_SWITCHES_H_ +#define EXTENSIONS_COMMON_SWITCHES_H_ + +// All switches in alphabetical order. The switches should be documented +// alongside the definition of their values in the .cc file. +namespace extensions { + +namespace switches { + +extern const char kAllowLegacyExtensionManifests[]; +extern const char kAllowScriptingGallery[]; +extern const char kExtensionsOnChromeURLs[]; +extern const char kShowComponentExtensionOptions[]; + +} // namespace switches + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_SWITCHES_H_ diff --git a/extensions/common/user_script.cc b/extensions/common/user_script.cc new file mode 100644 index 0000000..84c1236 --- /dev/null +++ b/extensions/common/user_script.cc @@ -0,0 +1,236 @@ +// Copyright 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 "extensions/common/user_script.h" + +#include "base/command_line.h" +#include "base/pickle.h" +#include "base/strings/string_util.h" +#include "extensions/common/switches.h" + +namespace { + +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; +} + +} // namespace + +namespace extensions { + +// The bitmask for valid user script injectable schemes used by URLPattern. +enum { + kValidUserScriptSchemes = URLPattern::SCHEME_CHROMEUI | + URLPattern::SCHEME_HTTP | + URLPattern::SCHEME_HTTPS | + URLPattern::SCHEME_FILE | + URLPattern::SCHEME_FTP +}; + +// static +const char UserScript::kFileExtension[] = ".user.js"; + +bool UserScript::IsURLUserScript(const GURL& url, + const std::string& mime_type) { + return EndsWith(url.ExtractFileName(), kFileExtension, false) && + mime_type != "text/html"; +} + +// static +int UserScript::ValidUserScriptSchemes(bool canExecuteScriptEverywhere) { + if (canExecuteScriptEverywhere) + return URLPattern::SCHEME_ALL; + int valid_schemes = kValidUserScriptSchemes; + if (!CommandLine::ForCurrentProcess()->HasSwitch( + switches::kExtensionsOnChromeURLs)) { + valid_schemes &= ~URLPattern::SCHEME_CHROMEUI; + } + return valid_schemes; +} + +UserScript::File::File(const base::FilePath& extension_root, + const base::FilePath& relative_path, + const GURL& url) + : extension_root_(extension_root), + relative_path_(relative_path), + url_(url) { +} + +UserScript::File::File() {} + +UserScript::File::~File() {} + +UserScript::UserScript() + : run_location_(DOCUMENT_IDLE), emulate_greasemonkey_(false), + match_all_frames_(false), incognito_enabled_(false) { +} + +UserScript::~UserScript() { +} + +void UserScript::add_url_pattern(const URLPattern& pattern) { + url_set_.AddPattern(pattern); +} + +void UserScript::add_exclude_url_pattern(const URLPattern& pattern) { + exclude_url_set_.AddPattern(pattern); +} + +bool UserScript::MatchesURL(const GURL& url) const { + if (!url_set_.is_empty()) { + if (!url_set_.MatchesURL(url)) + return false; + } + + if (!exclude_url_set_.is_empty()) { + if (exclude_url_set_.MatchesURL(url)) + return false; + } + + if (!globs_.empty()) { + if (!UrlMatchesGlobs(&globs_, url)) + return false; + } + + if (!exclude_globs_.empty()) { + if (UrlMatchesGlobs(&exclude_globs_, url)) + return false; + } + + return true; +} + +void UserScript::File::Pickle(::Pickle* pickle) const { + pickle->WriteString(url_.spec()); + // Do not write path. It's not needed in the renderer. + // Do not write content. It will be serialized by other means. +} + +void UserScript::File::Unpickle(const ::Pickle& pickle, PickleIterator* iter) { + // Read the url from the pickle. + std::string url; + CHECK(pickle.ReadString(iter, &url)); + set_url(GURL(url)); +} + +void UserScript::Pickle(::Pickle* pickle) const { + // Write the simple types to the pickle. + pickle->WriteInt(run_location()); + pickle->WriteString(extension_id()); + pickle->WriteBool(emulate_greasemonkey()); + pickle->WriteBool(match_all_frames()); + pickle->WriteBool(is_incognito_enabled()); + + PickleGlobs(pickle, globs_); + PickleGlobs(pickle, exclude_globs_); + PickleURLPatternSet(pickle, url_set_); + PickleURLPatternSet(pickle, exclude_url_set_); + PickleScripts(pickle, js_scripts_); + PickleScripts(pickle, css_scripts_); +} + +void UserScript::PickleGlobs(::Pickle* pickle, + const std::vector<std::string>& globs) const { + pickle->WriteUInt64(globs.size()); + for (std::vector<std::string>::const_iterator glob = globs.begin(); + glob != globs.end(); ++glob) { + pickle->WriteString(*glob); + } +} + +void UserScript::PickleURLPatternSet(::Pickle* pickle, + const URLPatternSet& pattern_list) const { + pickle->WriteUInt64(pattern_list.patterns().size()); + for (URLPatternSet::const_iterator pattern = pattern_list.begin(); + pattern != pattern_list.end(); ++pattern) { + pickle->WriteInt(pattern->valid_schemes()); + pickle->WriteString(pattern->GetAsString()); + } +} + +void UserScript::PickleScripts(::Pickle* pickle, + const FileList& scripts) const { + pickle->WriteUInt64(scripts.size()); + for (FileList::const_iterator file = scripts.begin(); + file != scripts.end(); ++file) { + file->Pickle(pickle); + } +} + +void UserScript::Unpickle(const ::Pickle& pickle, PickleIterator* iter) { + // Read the run location. + int run_location = 0; + CHECK(pickle.ReadInt(iter, &run_location)); + CHECK(run_location >= 0 && run_location < RUN_LOCATION_LAST); + run_location_ = static_cast<RunLocation>(run_location); + + CHECK(pickle.ReadString(iter, &extension_id_)); + CHECK(pickle.ReadBool(iter, &emulate_greasemonkey_)); + CHECK(pickle.ReadBool(iter, &match_all_frames_)); + CHECK(pickle.ReadBool(iter, &incognito_enabled_)); + + UnpickleGlobs(pickle, iter, &globs_); + UnpickleGlobs(pickle, iter, &exclude_globs_); + UnpickleURLPatternSet(pickle, iter, &url_set_); + UnpickleURLPatternSet(pickle, iter, &exclude_url_set_); + UnpickleScripts(pickle, iter, &js_scripts_); + UnpickleScripts(pickle, iter, &css_scripts_); +} + +void UserScript::UnpickleGlobs(const ::Pickle& pickle, PickleIterator* iter, + std::vector<std::string>* globs) { + uint64 num_globs = 0; + CHECK(pickle.ReadUInt64(iter, &num_globs)); + globs->clear(); + for (uint64 i = 0; i < num_globs; ++i) { + std::string glob; + CHECK(pickle.ReadString(iter, &glob)); + globs->push_back(glob); + } +} + +void UserScript::UnpickleURLPatternSet(const ::Pickle& pickle, + PickleIterator* iter, + URLPatternSet* pattern_list) { + uint64 num_patterns = 0; + CHECK(pickle.ReadUInt64(iter, &num_patterns)); + + pattern_list->ClearPatterns(); + for (uint64 i = 0; i < num_patterns; ++i) { + int valid_schemes; + CHECK(pickle.ReadInt(iter, &valid_schemes)); + + std::string pattern_str; + CHECK(pickle.ReadString(iter, &pattern_str)); + + URLPattern pattern(kValidUserScriptSchemes); + URLPattern::ParseResult result = pattern.Parse(pattern_str); + CHECK(URLPattern::PARSE_SUCCESS == result) << + URLPattern::GetParseResultString(result) << " " << pattern_str.c_str(); + + pattern.SetValidSchemes(valid_schemes); + pattern_list->AddPattern(pattern); + } +} + +void UserScript::UnpickleScripts(const ::Pickle& pickle, PickleIterator* iter, + FileList* scripts) { + uint64 num_files = 0; + CHECK(pickle.ReadUInt64(iter, &num_files)); + scripts->clear(); + for (uint64 i = 0; i < num_files; ++i) { + File file; + file.Unpickle(pickle, iter); + scripts->push_back(file); + } +} + +} // namespace extensions diff --git a/extensions/common/user_script.h b/extensions/common/user_script.h new file mode 100644 index 0000000..76b64d4 --- /dev/null +++ b/extensions/common/user_script.h @@ -0,0 +1,260 @@ +// Copyright 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 EXTENSIONS_COMMON_USER_SCRIPT_H_ +#define EXTENSIONS_COMMON_USER_SCRIPT_H_ + +#include <string> +#include <vector> + +#include "base/files/file_path.h" +#include "base/strings/string_piece.h" +#include "extensions/common/url_pattern.h" +#include "extensions/common/url_pattern_set.h" +#include "url/gurl.h" + +class Pickle; +class PickleIterator; + +namespace extensions { + +// Represents a user script, either a standalone one, or one that is part of an +// extension. +class UserScript { + public: + // The file extension for standalone user scripts. + static const char kFileExtension[]; + + // Check if a URL should be treated as a user script and converted to an + // extension. + static bool IsURLUserScript(const GURL& url, const std::string& mime_type); + + // Get the valid user script schemes for the current process. If + // canExecuteScriptEverywhere is true, this will return ALL_SCHEMES. + static int ValidUserScriptSchemes(bool canExecuteScriptEverywhere = false); + + // Locations that user scripts can be run inside the document. + enum RunLocation { + UNDEFINED, + DOCUMENT_START, // After the documentElement is created, but before + // anything else happens. + DOCUMENT_END, // After the entire document is parsed. Same as + // DOMContentLoaded. + DOCUMENT_IDLE, // Sometime after DOMContentLoaded, as soon as the document + // is "idle". Currently this uses the simple heuristic of: + // min(DOM_CONTENT_LOADED + TIMEOUT, ONLOAD), but no + // particular injection point is guaranteed. + RUN_LOCATION_LAST // Leave this as the last item. + }; + + // Holds actual script file info. + class File { + public: + File(const base::FilePath& extension_root, + const base::FilePath& relative_path, + const GURL& url); + File(); + ~File(); + + const base::FilePath& extension_root() const { return extension_root_; } + const base::FilePath& relative_path() const { return relative_path_; } + + const GURL& url() const { return url_; } + void set_url(const GURL& url) { url_ = url; } + + // If external_content_ is set returns it as content otherwise it returns + // content_ + const base::StringPiece GetContent() const { + if (external_content_.data()) + return external_content_; + else + return content_; + } + void set_external_content(const base::StringPiece& content) { + external_content_ = content; + } + void set_content(const base::StringPiece& content) { + content_.assign(content.begin(), content.end()); + } + + // Serialization support. The content and FilePath members will not be + // serialized! + void Pickle(::Pickle* pickle) const; + void Unpickle(const ::Pickle& pickle, PickleIterator* iter); + + private: + // Where the script file lives on the disk. We keep the path split so that + // it can be localized at will. + base::FilePath extension_root_; + base::FilePath relative_path_; + + // The url to this scipt file. + GURL url_; + + // The script content. It can be set to either loaded_content_ or + // externally allocated string. + base::StringPiece external_content_; + + // Set when the content is loaded by LoadContent + std::string content_; + }; + + typedef std::vector<File> FileList; + + // Constructor. Default the run location to document end, which is like + // Greasemonkey and probably more useful for typical scripts. + UserScript(); + ~UserScript(); + + 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& version() const { return version_; } + void set_version(const std::string& version) { + version_ = version; + } + + 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; } + + // Whether to match all frames, or only the top one. + bool match_all_frames() const { return match_all_frames_; } + void set_match_all_frames(bool val) { match_all_frames_ = 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. + const URLPatternSet& url_patterns() const { return url_set_; } + void add_url_pattern(const URLPattern& pattern); + const URLPatternSet& exclude_url_patterns() const { + return exclude_url_set_; + } + void add_exclude_url_pattern(const URLPattern& pattern); + + // List of js scripts for this user script + FileList& js_scripts() { return js_scripts_; } + const FileList& js_scripts() const { return js_scripts_; } + + // List of css scripts for this user script + FileList& css_scripts() { return css_scripts_; } + const FileList& css_scripts() const { return css_scripts_; } + + const std::string& extension_id() const { return extension_id_; } + void set_extension_id(const std::string& id) { extension_id_ = id; } + + bool is_incognito_enabled() const { return incognito_enabled_; } + void set_incognito_enabled(bool enabled) { incognito_enabled_ = enabled; } + + bool is_standalone() const { return extension_id_.empty(); } + + // Returns true if the script should be applied to the specified URL, false + // otherwise. + bool MatchesURL(const GURL& url) const; + + // Serialize the UserScript into a pickle. The content of the scripts and + // paths to UserScript::Files will not be serialized! + void Pickle(::Pickle* pickle) const; + + // Deserialize the script from a pickle. Note that this always succeeds + // because presumably we were the one that pickled it, and we did it + // correctly. + void Unpickle(const ::Pickle& pickle, PickleIterator* iter); + + private: + // Pickle helper functions used to pickle the individual types of components. + void PickleGlobs(::Pickle* pickle, + const std::vector<std::string>& globs) const; + void PickleURLPatternSet(::Pickle* pickle, + const URLPatternSet& pattern_list) const; + void PickleScripts(::Pickle* pickle, const FileList& scripts) const; + + // Unpickle helper functions used to unpickle individual types of components. + void UnpickleGlobs(const ::Pickle& pickle, PickleIterator* iter, + std::vector<std::string>* globs); + void UnpickleURLPatternSet(const ::Pickle& pickle, PickleIterator* iter, + URLPatternSet* pattern_list); + void UnpickleScripts(const ::Pickle& pickle, PickleIterator* iter, + FileList* scripts); + + // 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_; + + // A version number of the script. Only used when parsing Greasemonkey-style + // scripts. + std::string version_; + + // 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. + URLPatternSet url_set_; + URLPatternSet exclude_url_set_; + + // List of js scripts defined in content_scripts + FileList js_scripts_; + + // List of css scripts defined in content_scripts + FileList css_scripts_; + + // 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_; + + // Whether the user script should run in all frames, or only just the top one. + // Defaults to false. + bool match_all_frames_; + + // True if the script should be injected into an incognito tab. + bool incognito_enabled_; +}; + +typedef std::vector<UserScript> UserScriptList; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_USER_SCRIPT_H_ diff --git a/extensions/common/user_script_unittest.cc b/extensions/common/user_script_unittest.cc new file mode 100644 index 0000000..ffec4f0 --- /dev/null +++ b/extensions/common/user_script_unittest.cc @@ -0,0 +1,220 @@ +// Copyright 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 "base/files/file_path.h" +#include "base/pickle.h" +#include "extensions/common/user_script.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +namespace extensions { + +static const int kAllSchemes = + URLPattern::SCHEME_HTTP | + URLPattern::SCHEME_HTTPS | + URLPattern::SCHEME_FILE | + URLPattern::SCHEME_FTP | + URLPattern::SCHEME_CHROMEUI; + +TEST(ExtensionUserScriptTest, Glob_HostString) { + UserScript script; + script.add_glob("*mail.google.com*"); + script.add_glob("*mail.yahoo.com*"); + script.add_glob("*mail.msn.com*"); + EXPECT_TRUE(script.MatchesURL(GURL("http://mail.google.com"))); + EXPECT_TRUE(script.MatchesURL(GURL("http://mail.google.com/foo"))); + EXPECT_TRUE(script.MatchesURL(GURL("https://mail.google.com/foo"))); + EXPECT_TRUE(script.MatchesURL(GURL("ftp://mail.google.com/foo"))); + EXPECT_TRUE(script.MatchesURL(GURL("http://woo.mail.google.com/foo"))); + 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(ExtensionUserScriptTest, Glob_TrailingSlash) { + UserScript script; + script.add_glob("*mail.google.com/"); + // GURL normalizes the URL to have a trailing "/" + EXPECT_TRUE(script.MatchesURL(GURL("http://mail.google.com"))); + EXPECT_TRUE(script.MatchesURL(GURL("http://mail.google.com/"))); + EXPECT_FALSE(script.MatchesURL(GURL("http://mail.google.com/foo"))); +} + +TEST(ExtensionUserScriptTest, Glob_TrailingSlashStar) { + UserScript script; + script.add_glob("http://mail.google.com/*"); + // GURL normalizes the URL to have a trailing "/" + EXPECT_TRUE(script.MatchesURL(GURL("http://mail.google.com"))); + EXPECT_TRUE(script.MatchesURL(GURL("http://mail.google.com/foo"))); + EXPECT_FALSE(script.MatchesURL(GURL("https://mail.google.com/foo"))); +} + +TEST(ExtensionUserScriptTest, Glob_Star) { + UserScript script; + script.add_glob("*"); + EXPECT_TRUE(script.MatchesURL(GURL("http://foo.com/bar"))); + EXPECT_TRUE(script.MatchesURL(GURL("http://hot.com/dog"))); + EXPECT_TRUE(script.MatchesURL(GURL("https://hot.com/dog"))); + EXPECT_TRUE(script.MatchesURL(GURL("file:///foo/bar"))); + EXPECT_TRUE(script.MatchesURL(GURL("file://localhost/foo/bar"))); +} + +TEST(ExtensionUserScriptTest, Glob_StringAnywhere) { + UserScript script; + script.add_glob("*foo*"); + EXPECT_TRUE(script.MatchesURL(GURL("http://foo.com/bar"))); + EXPECT_TRUE(script.MatchesURL(GURL("http://baz.org/foo/bar"))); + EXPECT_FALSE(script.MatchesURL(GURL("http://baz.org"))); +} + +TEST(ExtensionUserScriptTest, UrlPattern) { + URLPattern pattern(kAllSchemes); + ASSERT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("http://*/foo*")); + + UserScript script; + script.add_url_pattern(pattern); + EXPECT_TRUE(script.MatchesURL(GURL("http://monkey.com/foobar"))); + EXPECT_FALSE(script.MatchesURL(GURL("http://monkey.com/hotdog"))); + + // NOTE: URLPattern is tested more extensively in url_pattern_unittest.cc. +} + +TEST(ExtensionUserScriptTest, ExcludeUrlPattern) { + UserScript script; + + URLPattern pattern(kAllSchemes); + ASSERT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("http://*.nytimes.com/*")); + script.add_url_pattern(pattern); + + URLPattern exclude(kAllSchemes); + ASSERT_EQ(URLPattern::PARSE_SUCCESS, exclude.Parse("*://*/*business*")); + script.add_exclude_url_pattern(exclude); + + EXPECT_TRUE(script.MatchesURL(GURL("http://www.nytimes.com/health"))); + EXPECT_FALSE(script.MatchesURL(GURL("http://www.nytimes.com/business"))); + EXPECT_TRUE(script.MatchesURL(GURL("http://business.nytimes.com"))); +} + +TEST(ExtensionUserScriptTest, UrlPatternAndIncludeGlobs) { + UserScript script; + + URLPattern pattern(kAllSchemes); + ASSERT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("http://*.nytimes.com/*")); + script.add_url_pattern(pattern); + + script.add_glob("*nytimes.com/???s/*"); + + EXPECT_TRUE(script.MatchesURL(GURL("http://www.nytimes.com/arts/1.html"))); + EXPECT_TRUE(script.MatchesURL(GURL("http://www.nytimes.com/jobs/1.html"))); + EXPECT_FALSE(script.MatchesURL(GURL("http://www.nytimes.com/sports/1.html"))); +} + +TEST(ExtensionUserScriptTest, UrlPatternAndExcludeGlobs) { + UserScript script; + + URLPattern pattern(kAllSchemes); + ASSERT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("http://*.nytimes.com/*")); + script.add_url_pattern(pattern); + + script.add_exclude_glob("*science*"); + + EXPECT_TRUE(script.MatchesURL(GURL("http://www.nytimes.com"))); + EXPECT_FALSE(script.MatchesURL(GURL("http://science.nytimes.com"))); + EXPECT_FALSE(script.MatchesURL(GURL("http://www.nytimes.com/science"))); +} + +TEST(ExtensionUserScriptTest, UrlPatternGlobInteraction) { + // If there are both, match intersection(union(globs), union(urlpatterns)). + UserScript script; + + URLPattern pattern(kAllSchemes); + ASSERT_EQ(URLPattern::PARSE_SUCCESS,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(ExtensionUserScriptTest, Pickle) { + URLPattern pattern1(kAllSchemes); + URLPattern pattern2(kAllSchemes); + URLPattern exclude1(kAllSchemes); + URLPattern exclude2(kAllSchemes); + ASSERT_EQ(URLPattern::PARSE_SUCCESS, pattern1.Parse("http://*/foo*")); + ASSERT_EQ(URLPattern::PARSE_SUCCESS, pattern2.Parse("http://bar/baz*")); + ASSERT_EQ(URLPattern::PARSE_SUCCESS, exclude1.Parse("*://*/*bar")); + ASSERT_EQ(URLPattern::PARSE_SUCCESS, exclude2.Parse("https://*/*")); + + UserScript script1; + script1.js_scripts().push_back(UserScript::File( + base::FilePath(FILE_PATH_LITERAL("c:\\foo\\")), + base::FilePath(FILE_PATH_LITERAL("foo.user.js")), + GURL("chrome-extension://abc/foo.user.js"))); + script1.css_scripts().push_back(UserScript::File( + base::FilePath(FILE_PATH_LITERAL("c:\\foo\\")), + base::FilePath(FILE_PATH_LITERAL("foo.user.css")), + GURL("chrome-extension://abc/foo.user.css"))); + script1.css_scripts().push_back(UserScript::File( + base::FilePath(FILE_PATH_LITERAL("c:\\foo\\")), + base::FilePath(FILE_PATH_LITERAL("foo2.user.css")), + GURL("chrome-extension://abc/foo2.user.css"))); + script1.set_run_location(UserScript::DOCUMENT_START); + + script1.add_url_pattern(pattern1); + script1.add_url_pattern(pattern2); + script1.add_exclude_url_pattern(exclude1); + script1.add_exclude_url_pattern(exclude2); + + Pickle pickle; + script1.Pickle(&pickle); + + PickleIterator iter(pickle); + UserScript script2; + script2.Unpickle(pickle, &iter); + + EXPECT_EQ(1U, script2.js_scripts().size()); + EXPECT_EQ(script1.js_scripts()[0].url(), script2.js_scripts()[0].url()); + + EXPECT_EQ(2U, script2.css_scripts().size()); + for (size_t i = 0; i < script2.js_scripts().size(); ++i) { + EXPECT_EQ(script1.css_scripts()[i].url(), script2.css_scripts()[i].url()); + } + + ASSERT_EQ(script1.globs().size(), script2.globs().size()); + for (size_t i = 0; i < script1.globs().size(); ++i) { + EXPECT_EQ(script1.globs()[i], script2.globs()[i]); + } + + ASSERT_EQ(script1.url_patterns(), script2.url_patterns()); + ASSERT_EQ(script1.exclude_url_patterns(), script2.exclude_url_patterns()); +} + +TEST(ExtensionUserScriptTest, Defaults) { + UserScript script; + ASSERT_EQ(UserScript::DOCUMENT_IDLE, script.run_location()); +} + +} // namespace extensions |