diff options
author | mpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-30 21:52:07 +0000 |
---|---|---|
committer | mpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-30 21:52:07 +0000 |
commit | 7715d63a3b9da9c149cdd0f544dbdf4cb13a8ed7 (patch) | |
tree | c018df57b687d9b3a6c8e0d06d297ca2e6d18c30 /chrome | |
parent | f33b82f2e0be4b3c739f3e51f34b680080e0150e (diff) | |
download | chromium_src-7715d63a3b9da9c149cdd0f544dbdf4cb13a8ed7.zip chromium_src-7715d63a3b9da9c149cdd0f544dbdf4cb13a8ed7.tar.gz chromium_src-7715d63a3b9da9c149cdd0f544dbdf4cb13a8ed7.tar.bz2 |
Add a wildcard scheme and a special 'all_urls' pattern to URLPattern.
BUG=47179
Review URL: http://codereview.chromium.org/2884008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@51295 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
20 files changed, 341 insertions, 228 deletions
diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc index 0ebd217..bced865 100644 --- a/chrome/browser/extensions/crx_installer.cc +++ b/chrome/browser/extensions/crx_installer.cc @@ -168,7 +168,7 @@ void CrxInstaller::OnUnpackSuccess(const FilePath& temp_dir, // Require that apps are served from the domain they claim in their extent, // or some ancestor domain. if (extension_->is_app() && limit_web_extent_to_download_host_) { - URLPattern pattern; + URLPattern pattern(URLPattern::SCHEMES_ALL); pattern.set_host(original_url_.host()); pattern.set_match_subdomains(true); diff --git a/chrome/browser/extensions/extensions_ui.cc b/chrome/browser/extensions/extensions_ui.cc index 84a6f33c..c4c55ec 100644 --- a/chrome/browser/extensions/extensions_ui.cc +++ b/chrome/browser/extensions/extensions_ui.cc @@ -791,7 +791,7 @@ static bool ExtensionWantsFileAccess(const Extension* extension) { for (UserScript::PatternList::const_iterator pattern = it->url_patterns().begin(); pattern != it->url_patterns().end(); ++pattern) { - if (pattern->scheme() == chrome::kFileScheme) + if (pattern->MatchesScheme(chrome::kFileScheme)) return true; } } diff --git a/chrome/browser/extensions/user_script_master.cc b/chrome/browser/extensions/user_script_master.cc index c651c2e..2f2ced5 100644 --- a/chrome/browser/extensions/user_script_master.cc +++ b/chrome/browser/extensions/user_script_master.cc @@ -107,7 +107,7 @@ bool UserScriptMaster::ScriptReloader::ParseMetadataHeader( } else if (GetDeclarationValue(line, kDescriptionDeclaration, &value)) { script->set_description(value); } else if (GetDeclarationValue(line, kMatchDeclaration, &value)) { - URLPattern pattern; + URLPattern pattern(UserScript::kValidUserScriptSchemes); if (!pattern.Parse(value)) return false; script->add_url_pattern(pattern); diff --git a/chrome/browser/resources/calendar_app/manifest.json b/chrome/browser/resources/calendar_app/manifest.json index 11ec952..df388fa 100644 --- a/chrome/browser/resources/calendar_app/manifest.json +++ b/chrome/browser/resources/calendar_app/manifest.json @@ -1,22 +1,21 @@ -{
- "key": "XX3fMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCf1MLMJ1mzNVJOVqaFFX+fQ7gJLVeZN+Sq3tKnZM33oWP82xDDs345/TsFTqGV3Nj5KvmjIN5NwcW/AeBOpVeOGFujFDSTOCZv0JDKkTXLyCegSwF+ljBi0TbCrsgv2T+8Jt891+hSyw5LPjXoTX2bKz+bu016tQnGnhb6fXyCBQIDAQAB",
- "name": "Google Calendar",
- "version": "1.3",
- "permissions": [ "notifications" ],
- "icons": {
- "128": "128.png",
- "24": "24.png",
- "32": "32.png",
- "48": "48.png"
- },
- "app": {
- "urls": [
- "http://www.google.com/calendar/",
- "https://www.google.com/calendar/"
- ],
- "launch": {
- "container": "tab",
- "web_url": "https://www.google.com/calendar/"
- }
- }
-}
+{ + "key": "XX3fMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCf1MLMJ1mzNVJOVqaFFX+fQ7gJLVeZN+Sq3tKnZM33oWP82xDDs345/TsFTqGV3Nj5KvmjIN5NwcW/AeBOpVeOGFujFDSTOCZv0JDKkTXLyCegSwF+ljBi0TbCrsgv2T+8Jt891+hSyw5LPjXoTX2bKz+bu016tQnGnhb6fXyCBQIDAQAB", + "name": "Google Calendar", + "version": "1.3", + "permissions": [ "notifications" ], + "icons": { + "128": "128.png", + "24": "24.png", + "32": "32.png", + "48": "48.png" + }, + "app": { + "urls": [ + "*://www.google.com/calendar/" + ], + "launch": { + "container": "tab", + "web_url": "https://www.google.com/calendar/" + } + } +} diff --git a/chrome/browser/resources/chat_manager/manifest.json b/chrome/browser/resources/chat_manager/manifest.json index f107bd5..ea4b743 100644 --- a/chrome/browser/resources/chat_manager/manifest.json +++ b/chrome/browser/resources/chat_manager/manifest.json @@ -16,8 +16,7 @@ "js/chatbridgehook.js" ], "matches": [ - "http://talkgadget.google.com/*", - "https://talkgadget.google.com/*" + "*://talkgadget.google.com/*" ], "run_at": "document_end", "all_frames": true @@ -27,15 +26,13 @@ "js/gmailbridgehook.js" ], "matches": [ - "http://mail.google.com/*", - "https://mail.google.com/*" + "*://mail.google.com/*" ], "run_at": "document_end" }], "permissions": [ "tabs", - "http://talkgadget.google.com/*", - "https://talkgadget.google.com/*" + "*://talkgadget.google.com/*" ], "launch": { "local_path": "central_roster_viewer.html", diff --git a/chrome/browser/resources/docs_app/manifest.json b/chrome/browser/resources/docs_app/manifest.json index d4b56fc4..1df3c5c 100644 --- a/chrome/browser/resources/docs_app/manifest.json +++ b/chrome/browser/resources/docs_app/manifest.json @@ -1,64 +1,41 @@ -{
- "key": "XX1fMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCfjDZDDE/CHFEYjpPSDjdI3zphzGo7fSxO3+/pQs++FwvA+OpKKhmBga2Sa+f53ujDlPR8Q6mCvy1lXM4M4zD4Hg3lH2LC1wT/YXxJ28afRYW1yEo6/pbpHazij3+FneGMT2xcTyGvgoacJHXOTUqWyCN7qMOCiFDwQ6Uk1zJOPQIDAQAB",
- "name": "Google Docs",
- "version": "1",
- "icons": {
- "128": "128.png",
- "24": "24.png",
- "32": "32.png",
- "48": "48.png"
- },
- "app": {
- "urls": [
- "http://www.google.com/docs/",
- "https://www.google.com/docs/",
- "http://docs.google.com/",
- "https://docs.google.com/",
- "http://docs0.google.com/",
- "https://docs0.google.com/",
- "http://docs1.google.com/",
- "https://docs1.google.com/",
- "http://docs2.google.com/",
- "https://docs2.google.com/",
- "http://docs3.google.com/",
- "https://docs3.google.com/",
- "http://docs4.google.com/",
- "https://docs4.google.com/",
- "http://docs5.google.com/",
- "https://docs5.google.com/",
- "http://docs6.google.com/",
- "https://docs6.google.com/",
- "http://docs7.google.com/",
- "https://docs7.google.com/",
- "http://docs8.google.com/",
- "https://docs8.google.com/",
- "http://docs9.google.com/",
- "https://docs9.google.com/",
- "http://spreadsheets.google.com/",
- "https://spreadsheets.google.com/",
- "http://spreadsheets0.google.com/",
- "https://spreadsheets0.google.com/",
- "http://spreadsheets1.google.com/",
- "https://spreadsheets1.google.com/",
- "http://spreadsheets2.google.com/",
- "https://spreadsheets2.google.com/",
- "http://spreadsheets3.google.com/",
- "https://spreadsheets3.google.com/",
- "http://spreadsheets4.google.com/",
- "https://spreadsheets4.google.com/",
- "http://spreadsheets5.google.com/",
- "https://spreadsheets5.google.com/",
- "http://spreadsheets6.google.com/",
- "https://spreadsheets6.google.com/",
- "http://spreadsheets7.google.com/",
- "https://spreadsheets7.google.com/",
- "http://spreadsheets8.google.com/",
- "https://spreadsheets8.google.com/",
- "http://spreadsheets9.google.com/",
- "https://spreadsheets9.google.com/"
- ],
- "launch": {
- "web_url": "https://docs.google.com/"
- }
- }
-}
+{ + "key": "XX1fMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCfjDZDDE/CHFEYjpPSDjdI3zphzGo7fSxO3+/pQs++FwvA+OpKKhmBga2Sa+f53ujDlPR8Q6mCvy1lXM4M4zD4Hg3lH2LC1wT/YXxJ28afRYW1yEo6/pbpHazij3+FneGMT2xcTyGvgoacJHXOTUqWyCN7qMOCiFDwQ6Uk1zJOPQIDAQAB", + "name": "Google Docs", + "version": "1", + "icons": { + "128": "128.png", + "24": "24.png", + "32": "32.png", + "48": "48.png" + }, + "app": { + "urls": [ + "*://www.google.com/docs/", + "*://docs.google.com/", + "*://docs0.google.com/", + "*://docs1.google.com/", + "*://docs2.google.com/", + "*://docs3.google.com/", + "*://docs4.google.com/", + "*://docs5.google.com/", + "*://docs6.google.com/", + "*://docs7.google.com/", + "*://docs8.google.com/", + "*://docs9.google.com/", + "*://spreadsheets.google.com/", + "*://spreadsheets0.google.com/", + "*://spreadsheets1.google.com/", + "*://spreadsheets2.google.com/", + "*://spreadsheets3.google.com/", + "*://spreadsheets4.google.com/", + "*://spreadsheets5.google.com/", + "*://spreadsheets6.google.com/", + "*://spreadsheets7.google.com/", + "*://spreadsheets8.google.com/", + "*://spreadsheets9.google.com/" + ], + "launch": { + "web_url": "https://docs.google.com/" + } + } +} diff --git a/chrome/browser/resources/gmail_app/manifest.json b/chrome/browser/resources/gmail_app/manifest.json index 378a2a9..4a90be2 100644 --- a/chrome/browser/resources/gmail_app/manifest.json +++ b/chrome/browser/resources/gmail_app/manifest.json @@ -1,25 +1,22 @@ -{
- "key": "XX2fMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCfjDZDDE/CHFEYjpPSDjdI3zphzGo7fSxO3+/pQs++FwvA+OpKKhmBga2Sa+f53ujDlPR8Q6mCvy1lXM4M4zD4Hg3lH2LC1wT/YXxJ28afRYW1yEo6/pbpHazij3+FneGMT2xcTyGvgoacJHXOTUqWyCN7qMOCiFDwQ6Uk1zJOPQIDAQAB",
- "name": "Google Mail",
- "version": "1",
- "icons": {
- "128": "128.png",
- "24": "24.png",
- "32": "32.png",
- "48": "48.png"
- },
- "permissions": [ "notifications" ],
- "app": {
- "urls": [
- "http://mail.google.com/mail/",
- "https://mail.google.com/mail/",
- "http://gmail.com/",
- "https://gmail.com/",
- "http://www.gmail.com/",
- "https://www.gmail.com/"
- ],
- "launch": {
- "web_url": "https://mail.google.com/mail/"
- }
- }
-}
+{ + "key": "XX2fMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCfjDZDDE/CHFEYjpPSDjdI3zphzGo7fSxO3+/pQs++FwvA+OpKKhmBga2Sa+f53ujDlPR8Q6mCvy1lXM4M4zD4Hg3lH2LC1wT/YXxJ28afRYW1yEo6/pbpHazij3+FneGMT2xcTyGvgoacJHXOTUqWyCN7qMOCiFDwQ6Uk1zJOPQIDAQAB", + "name": "Google Mail", + "version": "1", + "icons": { + "128": "128.png", + "24": "24.png", + "32": "32.png", + "48": "48.png" + }, + "permissions": [ "notifications" ], + "app": { + "urls": [ + "*://mail.google.com/mail/", + "*://gmail.com/", + "*://www.gmail.com/" + ], + "launch": { + "web_url": "https://mail.google.com/mail/" + } + } +} diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc index b8c5163..a1b5255 100644 --- a/chrome/common/extensions/extension.cc +++ b/chrome/common/extensions/extension.cc @@ -65,22 +65,8 @@ static void ConvertHexadecimalToIDAlphabet(std::string* id) { (*id)[i] = HexStringToInt(id->substr(i, 1)) + 'a'; } -const char* kValidUserScriptSchemes[] = { - chrome::kHttpScheme, - chrome::kHttpsScheme, - chrome::kFileScheme, - chrome::kFtpScheme, -}; - -// Whether the URL pattern is allowed for user scripts. -bool ValidUrlPatternInUserScript(const URLPattern& pattern) { - const std::string scheme = pattern.scheme(); - for (size_t i = 0; i < arraysize(kValidUserScriptSchemes); ++i) { - if (scheme == kValidUserScriptSchemes[i]) - return true; - } - return false; -} +const int kValidWebExtentSchemes = + URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS; } // namespace @@ -278,8 +264,8 @@ bool Extension::LoadUserScriptHelper(const DictionaryValue* content_script, return false; } - URLPattern pattern; - if (!pattern.Parse(match_str) || !ValidUrlPatternInUserScript(pattern)) { + URLPattern pattern(UserScript::kValidUserScriptSchemes); + if (!pattern.Parse(match_str)) { *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidMatch, IntToString(definition_index), IntToString(j)); return false; @@ -566,7 +552,7 @@ bool Extension::LoadWebURLs(const DictionaryValue* manifest, return false; } - URLPattern pattern; + URLPattern pattern(kValidWebExtentSchemes); if (!pattern.Parse(pattern_string)) { *error = ExtensionErrorUtils::FormatErrorMessage( errors::kInvalidWebURL, UintToString(i)); @@ -636,8 +622,11 @@ bool Extension::LoadLaunchURL(const DictionaryValue* manifest, // If there is no extent, we default the extent based on the launch URL. if (web_extent_.is_empty() && !launch_web_url_.empty()) { GURL launch_url(launch_web_url_); - URLPattern pattern; - pattern.set_scheme(launch_url.scheme()); + URLPattern pattern(kValidWebExtentSchemes); + if (!pattern.SetScheme("*")) { + *error = errors::kInvalidLaunchWebURL; + return false; + } pattern.set_host(launch_url.host()); pattern.set_path("/*"); web_extent_.AddPattern(pattern); @@ -1403,7 +1392,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, } // Otherwise, it's a host pattern permission. - URLPattern pattern; + URLPattern pattern(URLPattern::SCHEMES_ALL); if (!pattern.Parse(permission_str)) { *error = ExtensionErrorUtils::FormatErrorMessage( errors::kInvalidPermission, IntToString(i)); @@ -1651,22 +1640,16 @@ Extension::Icons Extension::GetIconPathAllowLargerSize( return EXTENSION_ICON_LARGE; } -// We support http:// and https:// as well as chrome://favicon//. -// chrome://resources/ is supported but only for component extensions. bool Extension::CanAccessURL(const URLPattern pattern) const { - if (pattern.scheme() == chrome::kHttpScheme || - pattern.scheme() == chrome::kHttpsScheme) { - return true; - } - if (pattern.scheme() == chrome::kChromeUIScheme && - pattern.host() == chrome::kChromeUIFavIconHost) { - return true; - } - if (location() == Extension::COMPONENT && - pattern.scheme() == chrome::kChromeUIScheme) { - return true; + if (pattern.MatchesScheme(chrome::kChromeUIScheme)) { + // Only allow access to chrome://favicon to regular extensions. Component + // extensions can have access to all of chrome://*. + return (pattern.host() == chrome::kChromeUIFavIconHost || + location() == Extension::COMPONENT); } - return false; + + // Otherwise, the valid schemes were handled by URLPattern. + return true; } bool Extension::HasHostPermission(const GURL& url) const { diff --git a/chrome/common/extensions/extension_extent_unittest.cc b/chrome/common/extensions/extension_extent_unittest.cc index 89f1f83..2284f8e 100644 --- a/chrome/common/extensions/extension_extent_unittest.cc +++ b/chrome/common/extensions/extension_extent_unittest.cc @@ -7,6 +7,8 @@ #include "googleurl/src/gurl.h" #include "testing/gtest/include/gtest/gtest.h" +static const int kSchemes = URLPattern::SCHEMES_ALL; + TEST(ExtensionExtentTest, Empty) { ExtensionExtent extent; EXPECT_FALSE(extent.ContainsURL(GURL("http://www.foo.com/bar"))); @@ -16,7 +18,7 @@ TEST(ExtensionExtentTest, Empty) { TEST(ExtensionExtentTest, One) { ExtensionExtent extent; - extent.AddPattern(URLPattern("http://www.google.com/*")); + extent.AddPattern(URLPattern(kSchemes, "http://www.google.com/*")); EXPECT_TRUE(extent.ContainsURL(GURL("http://www.google.com/"))); EXPECT_TRUE(extent.ContainsURL(GURL("http://www.google.com/monkey"))); @@ -26,8 +28,8 @@ TEST(ExtensionExtentTest, One) { TEST(ExtensionExtentTest, Two) { ExtensionExtent extent; - extent.AddPattern(URLPattern("http://www.google.com/*")); - extent.AddPattern(URLPattern("http://www.yahoo.com/*")); + extent.AddPattern(URLPattern(kSchemes, "http://www.google.com/*")); + extent.AddPattern(URLPattern(kSchemes, "http://www.yahoo.com/*")); EXPECT_TRUE(extent.ContainsURL(GURL("http://www.google.com/monkey"))); EXPECT_TRUE(extent.ContainsURL(GURL("http://www.yahoo.com/monkey"))); @@ -36,16 +38,16 @@ TEST(ExtensionExtentTest, Two) { TEST(ExtensionExtentTest, OverlapsWith) { ExtensionExtent extent1; - extent1.AddPattern(URLPattern("http://www.google.com/f*")); - extent1.AddPattern(URLPattern("http://www.yahoo.com/b*")); + extent1.AddPattern(URLPattern(kSchemes, "http://www.google.com/f*")); + extent1.AddPattern(URLPattern(kSchemes, "http://www.yahoo.com/b*")); ExtensionExtent extent2; - extent2.AddPattern(URLPattern("http://www.reddit.com/f*")); - extent2.AddPattern(URLPattern("http://www.yahoo.com/z*")); + extent2.AddPattern(URLPattern(kSchemes, "http://www.reddit.com/f*")); + extent2.AddPattern(URLPattern(kSchemes, "http://www.yahoo.com/z*")); ExtensionExtent extent3; - extent3.AddPattern(URLPattern("http://www.google.com/q/*")); - extent3.AddPattern(URLPattern("http://www.yahoo.com/b/*")); + extent3.AddPattern(URLPattern(kSchemes, "http://www.google.com/q/*")); + extent3.AddPattern(URLPattern(kSchemes, "http://www.yahoo.com/b/*")); EXPECT_FALSE(extent1.OverlapsWith(extent2)); EXPECT_FALSE(extent2.OverlapsWith(extent1)); diff --git a/chrome/common/extensions/extension_manifests_unittest.cc b/chrome/common/extensions/extension_manifests_unittest.cc index f91fdef..86a6bb1 100644 --- a/chrome/common/extensions/extension_manifests_unittest.cc +++ b/chrome/common/extensions/extension_manifests_unittest.cc @@ -113,7 +113,7 @@ TEST_F(ManifestTest, AppWebUrls) { scoped_ptr<Extension> extension( LoadAndExpectSuccess("web_urls_default.json")); ASSERT_EQ(1u, extension->web_extent().patterns().size()); - EXPECT_EQ("http://www.google.com/*", + EXPECT_EQ("*://www.google.com/*", extension->web_extent().patterns()[0].GetAsString()); } diff --git a/chrome/common/extensions/extension_unittest.cc b/chrome/common/extensions/extension_unittest.cc index fdba5de..ec75b73 100644 --- a/chrome/common/extensions/extension_unittest.cc +++ b/chrome/common/extensions/extension_unittest.cc @@ -236,13 +236,6 @@ TEST(ExtensionTest, InitFromValueInvalid) { EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); EXPECT_TRUE(MatchPatternASCII(error, errors::kInvalidPermission)); - // Test permissions scheme. - input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy())); - input_value->GetList(keys::kPermissions, &permissions); - permissions->Set(0, Value::CreateStringValue("file:///C:/foo.txt")); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); - EXPECT_TRUE(MatchPatternASCII(error, errors::kInvalidPermissionScheme)); - // Multiple page actions are not allowed. input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy())); DictionaryValue* action = new DictionaryValue; @@ -320,6 +313,12 @@ TEST(ExtensionTest, InitFromValueValid) { EXPECT_EQ(extension.id(), extension.url().host()); EXPECT_EQ(path.value(), extension.path().value()); + // Test permissions scheme. + ListValue* permissions = new ListValue; + permissions->Set(0, Value::CreateStringValue("file:///C:/foo.txt")); + input_value.Set(keys::kPermissions, permissions); + EXPECT_TRUE(extension.InitFromValue(input_value, false, &error)); + // Test with an options page. input_value.SetString(keys::kOptionsPage, "options.html"); EXPECT_TRUE(extension.InitFromValue(input_value, false, &error)); diff --git a/chrome/common/extensions/url_pattern.cc b/chrome/common/extensions/url_pattern.cc index 2b1e4ce..979d14b 100644 --- a/chrome/common/extensions/url_pattern.cc +++ b/chrome/common/extensions/url_pattern.cc @@ -10,6 +10,7 @@ // TODO(aa): Consider adding chrome-extension? What about more obscure ones // like data: and javascript: ? +// Note: keep this array in sync with kValidSchemeMasks. static const char* kValidSchemes[] = { chrome::kHttpScheme, chrome::kHttpsScheme, @@ -18,34 +19,51 @@ static const char* kValidSchemes[] = { chrome::kChromeUIScheme, }; -static const char kPathSeparator[] = "/"; +static const int kValidSchemeMasks[] = { + URLPattern::SCHEME_HTTP, + URLPattern::SCHEME_HTTPS, + URLPattern::SCHEME_FILE, + URLPattern::SCHEME_FTP, + URLPattern::SCHEME_CHROMEUI, +}; -// static -bool URLPattern::IsValidScheme(const std::string& scheme) { - for (size_t i = 0; i < arraysize(kValidSchemes); ++i) { - if (scheme == kValidSchemes[i]) - return true; - } +COMPILE_ASSERT(arraysize(kValidSchemes) == arraysize(kValidSchemeMasks), + must_keep_these_arrays_in_sync); - return false; -} +static const char kPathSeparator[] = "/"; + +static const char kAllUrlsPattern[] = "<all_urls>"; URLPattern::URLPattern() - : match_subdomains_(false) {} + : valid_schemes_(0), match_all_urls_(false), match_subdomains_(false) {} + +URLPattern::URLPattern(int valid_schemes) + : valid_schemes_(valid_schemes), match_all_urls_(false), + match_subdomains_(false) {} -URLPattern::URLPattern(const std::string& pattern) - : match_subdomains_(false) { +URLPattern::URLPattern(int valid_schemes, const std::string& pattern) + : valid_schemes_(valid_schemes), match_all_urls_(false), + match_subdomains_(false) { if (!Parse(pattern)) NOTREACHED() << "URLPattern is invalid: " << pattern; } bool URLPattern::Parse(const std::string& pattern) { + // Special case pattern to match every valid URL. + if (pattern == kAllUrlsPattern) { + match_all_urls_ = true; + match_subdomains_ = true; + scheme_ = "*"; + host_.clear(); + path_ = "/*"; + return true; + } + size_t scheme_end_pos = pattern.find(chrome::kStandardSchemeSeparator); if (scheme_end_pos == std::string::npos) return false; - scheme_ = pattern.substr(0, scheme_end_pos); - if (!IsValidScheme(scheme_)) + if (!SetScheme(pattern.substr(0, scheme_end_pos))) return false; size_t host_start_pos = scheme_end_pos + @@ -91,8 +109,27 @@ bool URLPattern::Parse(const std::string& pattern) { return true; } +bool URLPattern::SetScheme(const std::string& scheme) { + scheme_ = scheme; + if (scheme_ == "*") { + valid_schemes_ &= (SCHEME_HTTP | SCHEME_HTTPS); + } else if (!IsValidScheme(scheme_)) { + return false; + } + return true; +} + +bool URLPattern::IsValidScheme(const std::string& scheme) const { + for (size_t i = 0; i < arraysize(kValidSchemes); ++i) { + if (scheme == kValidSchemes[i] && (valid_schemes_ & kValidSchemeMasks[i])) + return true; + } + + return false; +} + bool URLPattern::MatchesUrl(const GURL &test) const { - if (test.scheme() != scheme_) + if (!MatchesScheme(test.scheme())) return false; if (!MatchesHost(test)) @@ -104,6 +141,13 @@ bool URLPattern::MatchesUrl(const GURL &test) const { return true; } +bool URLPattern::MatchesScheme(const std::string& test) const { + if (scheme_ == "*") + return IsValidScheme(test); + + return test == scheme_; +} + bool URLPattern::MatchesHost(const std::string& host) const { std::string test(chrome::kHttpScheme); test += chrome::kStandardSchemeSeparator; @@ -157,6 +201,9 @@ bool URLPattern::MatchesPath(const std::string& test) const { } std::string URLPattern::GetAsString() const { + if (match_all_urls_) + return kAllUrlsPattern; + std::string spec = scheme_ + chrome::kStandardSchemeSeparator; if (match_subdomains_) { @@ -175,7 +222,7 @@ std::string URLPattern::GetAsString() const { } bool URLPattern::OverlapsWith(const URLPattern& other) const { - if (scheme_ != other.scheme()) + if (!MatchesScheme(other.scheme_) && !other.MatchesScheme(scheme_)) return false; if (!MatchesHost(other.host()) && !other.MatchesHost(host_)) diff --git a/chrome/common/extensions/url_pattern.h b/chrome/common/extensions/url_pattern.h index d74520d7..ba4bdea 100644 --- a/chrome/common/extensions/url_pattern.h +++ b/chrome/common/extensions/url_pattern.h @@ -11,13 +11,16 @@ // A pattern that can be used to match URLs. A URLPattern is a very restricted // subset of URL syntax: // -// <url-pattern> := <scheme>://<host><path> -// <scheme> := 'http' | 'https' | 'file' | 'ftp' | 'chrome' +// <url-pattern> := <scheme>://<host><path> | '<all_urls>' +// <scheme> := '*' | 'http' | 'https' | 'file' | 'ftp' | 'chrome' // <host> := '*' | '*.' <anychar except '/' and '*'>+ // <path> := '/' <any chars> // // * Host is not used when the scheme is 'file'. // * The path can have embedded '*' characters which act as glob wildcards. +// * '<all_urls>' is a special pattern that matches any URL that contains a +// valid scheme (as specified by valid_schemes_). +// * The '*' scheme pattern excludes file URLs. // // Examples of valid patterns: // - http://*/* @@ -69,25 +72,38 @@ // than the original glob, which is probably better than nothing. class URLPattern { public: - // Returns true if the specified scheme can be used in URL patterns, and false - // otherwise. - static bool IsValidScheme(const std::string& scheme); - + // A collection of scheme bitmasks for use with valid_schemes. + enum SchemeMasks { + SCHEME_HTTP = 1<<0, + SCHEME_HTTPS = 1<<1, + SCHEME_FILE = 1<<2, + SCHEME_FTP = 1<<3, + SCHEME_CHROMEUI = 1<<4, + + SCHEMES_ALL = + SCHEME_HTTP | SCHEME_HTTPS | SCHEME_FILE | SCHEME_FTP | SCHEME_CHROMEUI, + }; + + // Note: don't use this directly. This exists so URLPattern can be used + // with STL containers. URLPattern(); + // Construct an URLPattern with the given set of allowable schemes. See + // valid_schemes_ for more info. + explicit URLPattern(int valid_schemes); + // Convenience to construct a URLPattern from a string. The string is expected // to be a valid pattern. If the string is not known ahead of time, use // Parse() instead, which returns success or failure. - explicit URLPattern(const std::string& pattern); + URLPattern(int valid_schemes, const std::string& pattern); - // Get the scheme the pattern matches. This will always return a valid scheme - // if is_valid() returns true. - std::string scheme() const { return scheme_; } - void set_scheme(const std::string& scheme) { scheme_ = scheme; } + // Gets the bitmask of valid schemes. + int valid_schemes() const { return valid_schemes_; } + void set_valid_schemes(int valid_schemes) { valid_schemes_ = valid_schemes; } // Gets the host the pattern matches. This can be an empty string if the // pattern matches all hosts (the input was <scheme>://*/<whatever>). - std::string host() const { return host_; } + const std::string& host() const { return host_; } void set_host(const std::string& host) { host_ = host; } // Gets whether to match subdomains of host(). @@ -96,7 +112,7 @@ class URLPattern { // Gets the path the pattern matches with the leading slash. This can have // embedded asterisks which are interpreted using glob rules. - std::string path() const { return path_; } + const std::string& path() const { return path_; } void set_path(const std::string& path) { path_ = path; path_escaped_ = ""; @@ -106,9 +122,24 @@ class URLPattern { // instance will have some intermediate values and is in an invalid state. bool Parse(const std::string& pattern_str); + // Sets the scheme for pattern matches. This can be a single '*' if the + // pattern matches all valid schemes (as defined by the valid_schemes_ + // property). Returns false on failure (if the scheme is not valid). + bool SetScheme(const std::string& scheme); + // Note: You should use MatchesScheme() instead of this getter unless you + // absolutely need the exact scheme. This is exposed for testing. + const std::string& scheme() const { return scheme_; } + + // Returns true if the specified scheme can be used in this URL pattern, and + // false otherwise. Uses valid_schemes_ to determine validity. + bool IsValidScheme(const std::string& scheme) const; + // Returns true if this instance matches the specified URL. bool MatchesUrl(const GURL& url) const; + // Returns true if |test| matches our scheme. + bool MatchesScheme(const std::string& test) const; + // Returns true if |test| matches our host. bool MatchesHost(const std::string& test) const; bool MatchesHost(const GURL& test) const; @@ -125,6 +156,15 @@ class URLPattern { bool OverlapsWith(const URLPattern& other) const; private: + // A bitmask containing the schemes which are considered valid for this + // pattern. Parse() uses this to decide whether a pattern contains a valid + // scheme. MatchesScheme uses this to decide whether a wildcard scheme_ + // matches a given test scheme. + int valid_schemes_; + + // True if this is a special-case "<all_urls>" pattern. + bool match_all_urls_; + // The scheme for the pattern. std::string scheme_; diff --git a/chrome/common/extensions/url_pattern_unittest.cc b/chrome/common/extensions/url_pattern_unittest.cc index c4395bc..3887602 100644 --- a/chrome/common/extensions/url_pattern_unittest.cc +++ b/chrome/common/extensions/url_pattern_unittest.cc @@ -28,7 +28,7 @@ TEST(URLPatternTest, ParseInvalid) { // all pages for a given scheme TEST(URLPatternTest, Match1) { - URLPattern pattern; + URLPattern pattern(URLPattern::SCHEMES_ALL); EXPECT_TRUE(pattern.Parse("http://*/*")); EXPECT_EQ("http", pattern.scheme()); EXPECT_EQ("", pattern.host()); @@ -43,7 +43,7 @@ TEST(URLPatternTest, Match1) { // all domains TEST(URLPatternTest, Match2) { - URLPattern pattern; + URLPattern pattern(URLPattern::SCHEMES_ALL); EXPECT_TRUE(pattern.Parse("https://*/foo*")); EXPECT_EQ("https", pattern.scheme()); EXPECT_EQ("", pattern.host()); @@ -57,7 +57,7 @@ TEST(URLPatternTest, Match2) { // subdomains TEST(URLPatternTest, Match3) { - URLPattern pattern; + URLPattern pattern(URLPattern::SCHEMES_ALL); EXPECT_TRUE(pattern.Parse("http://*.google.com/foo*bar")); EXPECT_EQ("http", pattern.scheme()); EXPECT_EQ("google.com", pattern.host()); @@ -72,7 +72,7 @@ TEST(URLPatternTest, Match3) { // glob escaping TEST(URLPatternTest, Match5) { - URLPattern pattern; + URLPattern pattern(URLPattern::SCHEMES_ALL); EXPECT_TRUE(pattern.Parse("file:///foo?bar\\*baz")); EXPECT_EQ("file", pattern.scheme()); EXPECT_EQ("", pattern.host()); @@ -84,7 +84,7 @@ TEST(URLPatternTest, Match5) { // ip addresses TEST(URLPatternTest, Match6) { - URLPattern pattern; + URLPattern pattern(URLPattern::SCHEMES_ALL); EXPECT_TRUE(pattern.Parse("http://127.0.0.1/*")); EXPECT_EQ("http", pattern.scheme()); EXPECT_EQ("127.0.0.1", pattern.host()); @@ -95,7 +95,7 @@ TEST(URLPatternTest, Match6) { // subdomain matching with ip addresses TEST(URLPatternTest, Match7) { - URLPattern pattern; + URLPattern pattern(URLPattern::SCHEMES_ALL); EXPECT_TRUE(pattern.Parse("http://*.0.0.1/*")); // allowed, but useless EXPECT_EQ("http", pattern.scheme()); EXPECT_EQ("0.0.1", pattern.host()); @@ -107,7 +107,7 @@ TEST(URLPatternTest, Match7) { // unicode TEST(URLPatternTest, Match8) { - URLPattern pattern; + URLPattern pattern(URLPattern::SCHEMES_ALL); // The below is the ASCII encoding of the following URL: // http://*.\xe1\x80\xbf/a\xc2\x81\xe1* EXPECT_TRUE(pattern.Parse("http://*.xn--gkd/a%C2%81%E1*")); @@ -123,7 +123,7 @@ TEST(URLPatternTest, Match8) { // chrome:// TEST(URLPatternTest, Match9) { - URLPattern pattern; + URLPattern pattern(URLPattern::SCHEMES_ALL); EXPECT_TRUE(pattern.Parse("chrome://favicon/*")); EXPECT_EQ("chrome", pattern.scheme()); EXPECT_EQ("favicon", pattern.host()); @@ -134,6 +134,37 @@ TEST(URLPatternTest, Match9) { EXPECT_FALSE(pattern.MatchesUrl(GURL("chrome://history"))); }; +// *:// +TEST(URLPatternTest, Match10) { + URLPattern pattern(URLPattern::SCHEMES_ALL); + EXPECT_TRUE(pattern.Parse("*://*/*")); + EXPECT_TRUE(pattern.MatchesScheme("http")); + EXPECT_TRUE(pattern.MatchesScheme("https")); + EXPECT_FALSE(pattern.MatchesScheme("chrome")); + EXPECT_FALSE(pattern.MatchesScheme("file")); + EXPECT_FALSE(pattern.MatchesScheme("ftp")); + EXPECT_TRUE(pattern.match_subdomains()); + EXPECT_EQ("/*", pattern.path()); + EXPECT_TRUE(pattern.MatchesUrl(GURL("http://127.0.0.1"))); + EXPECT_FALSE(pattern.MatchesUrl(GURL("chrome://favicon/http://google.com"))); + EXPECT_FALSE(pattern.MatchesUrl(GURL("file:///foo/bar"))); +}; + +// <all_urls> +TEST(URLPatternTest, Match11) { + URLPattern pattern(URLPattern::SCHEMES_ALL); + EXPECT_TRUE(pattern.Parse("<all_urls>")); + EXPECT_TRUE(pattern.MatchesScheme("chrome")); + EXPECT_TRUE(pattern.MatchesScheme("http")); + EXPECT_TRUE(pattern.MatchesScheme("https")); + EXPECT_TRUE(pattern.MatchesScheme("file")); + EXPECT_TRUE(pattern.match_subdomains()); + EXPECT_EQ("/*", pattern.path()); + EXPECT_TRUE(pattern.MatchesUrl(GURL("chrome://favicon/http://google.com"))); + EXPECT_TRUE(pattern.MatchesUrl(GURL("http://127.0.0.1"))); + EXPECT_TRUE(pattern.MatchesUrl(GURL("file:///foo/bar"))); +}; + void TestPatternOverlap(const URLPattern& pattern1, const URLPattern& pattern2, bool expect_overlap) { EXPECT_EQ(expect_overlap, pattern1.OverlapsWith(pattern2)) @@ -143,12 +174,17 @@ void TestPatternOverlap(const URLPattern& pattern1, const URLPattern& pattern2, } TEST(URLPatternTest, OverlapsWith) { - URLPattern pattern1("http://www.google.com/foo/*"); - URLPattern pattern2("https://www.google.com/foo/*"); - URLPattern pattern3("http://*.google.com/foo/*"); - URLPattern pattern4("http://*.yahooo.com/foo/*"); - URLPattern pattern5("http://www.yahooo.com/bar/*"); - URLPattern pattern6("http://www.yahooo.com/bar/baz/*"); + URLPattern pattern1(URLPattern::SCHEMES_ALL, "http://www.google.com/foo/*"); + URLPattern pattern2(URLPattern::SCHEMES_ALL, "https://www.google.com/foo/*"); + URLPattern pattern3(URLPattern::SCHEMES_ALL, "http://*.google.com/foo/*"); + URLPattern pattern4(URLPattern::SCHEMES_ALL, "http://*.yahooo.com/foo/*"); + URLPattern pattern5(URLPattern::SCHEMES_ALL, "http://www.yahooo.com/bar/*"); + URLPattern pattern6(URLPattern::SCHEMES_ALL, + "http://www.yahooo.com/bar/baz/*"); + URLPattern pattern7(URLPattern::SCHEMES_ALL, "file:///*"); + URLPattern pattern8(URLPattern::SCHEMES_ALL, "*://*/*"); + URLPattern pattern9(URLPattern::SCHEME_HTTPS, "*://*/*"); + URLPattern pattern10(URLPattern::SCHEMES_ALL, "<all_urls>"); TestPatternOverlap(pattern1, pattern1, true); TestPatternOverlap(pattern1, pattern2, false); @@ -157,4 +193,13 @@ TEST(URLPatternTest, OverlapsWith) { TestPatternOverlap(pattern3, pattern4, false); TestPatternOverlap(pattern4, pattern5, false); TestPatternOverlap(pattern5, pattern6, true); + + // Test that scheme restrictions work. + TestPatternOverlap(pattern1, pattern8, true); + TestPatternOverlap(pattern1, pattern9, false); + TestPatternOverlap(pattern1, pattern10, true); + + // Test that '<all_urls>' includes file URLs, while scheme '*' does not. + TestPatternOverlap(pattern7, pattern8, false); + TestPatternOverlap(pattern7, pattern10, true); } diff --git a/chrome/common/extensions/user_script.cc b/chrome/common/extensions/user_script.cc index b55a10c..c568e8e 100644 --- a/chrome/common/extensions/user_script.cc +++ b/chrome/common/extensions/user_script.cc @@ -31,8 +31,14 @@ static bool UrlMatchesGlobs(const std::vector<std::string>* globs, } } +// static const char UserScript::kFileExtension[] = ".user.js"; +// static +const int UserScript::kValidUserScriptSchemes = + URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS | + URLPattern::SCHEME_FILE | URLPattern::SCHEME_FTP; + bool UserScript::HasUserScriptFileExtension(const GURL& url) { return EndsWith(url.ExtractFileName(), kFileExtension, false); } @@ -98,6 +104,7 @@ void UserScript::Pickle(::Pickle* pickle) const { pickle->WriteSize(url_patterns_.size()); for (PatternList::const_iterator pattern = url_patterns_.begin(); pattern != url_patterns_.end(); ++pattern) { + pickle->WriteInt(pattern->valid_schemes()); pickle->WriteString(pattern->GetAsString()); } @@ -153,8 +160,10 @@ void UserScript::Unpickle(const ::Pickle& pickle, void** iter) { url_patterns_.clear(); for (size_t i = 0; i < num_patterns; ++i) { + int valid_schemes; + CHECK(pickle.ReadInt(iter, &valid_schemes)); std::string pattern_str; - URLPattern pattern; + URLPattern pattern(valid_schemes); CHECK(pickle.ReadString(iter, &pattern_str)); CHECK(pattern.Parse(pattern_str)); url_patterns_.push_back(pattern); diff --git a/chrome/common/extensions/user_script.h b/chrome/common/extensions/user_script.h index ac8f836..6c79019 100644 --- a/chrome/common/extensions/user_script.h +++ b/chrome/common/extensions/user_script.h @@ -24,6 +24,9 @@ class UserScript { // The file extension for standalone user scripts. static const char kFileExtension[]; + // The bitmask for valid user script injectable schemes used by URLPattern. + static const int kValidUserScriptSchemes; + // Check if a file or URL has the user script file extension. static bool HasUserScriptFileExtension(const GURL& url); static bool HasUserScriptFileExtension(const FilePath& path); diff --git a/chrome/common/extensions/user_script_unittest.cc b/chrome/common/extensions/user_script_unittest.cc index 7b5cc6d..d177089 100644 --- a/chrome/common/extensions/user_script_unittest.cc +++ b/chrome/common/extensions/user_script_unittest.cc @@ -63,7 +63,7 @@ TEST(UserScriptTest, Match5) { } TEST(UserScriptTest, Match6) { - URLPattern pattern; + URLPattern pattern(URLPattern::SCHEMES_ALL); ASSERT_TRUE(pattern.Parse("http://*/foo*")); UserScript script; @@ -78,7 +78,7 @@ TEST(UserScriptTest, UrlPatternGlobInteraction) { // If there are both, match intersection(union(globs), union(urlpatterns)). UserScript script; - URLPattern pattern; + URLPattern pattern(URLPattern::SCHEMES_ALL); ASSERT_TRUE(pattern.Parse("http://www.google.com/*")); script.add_url_pattern(pattern); @@ -105,8 +105,8 @@ TEST(UserScriptTest, UrlPatternGlobInteraction) { } TEST(UserScriptTest, Pickle) { - URLPattern pattern1; - URLPattern pattern2; + URLPattern pattern1(URLPattern::SCHEMES_ALL); + URLPattern pattern2(URLPattern::SCHEMES_ALL); ASSERT_TRUE(pattern1.Parse("http://*/foo*")); ASSERT_TRUE(pattern2.Parse("http://bar/baz*")); diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h index a08f109..3274a02 100644 --- a/chrome/common/render_messages.h +++ b/chrome/common/render_messages.h @@ -2221,13 +2221,17 @@ template <> struct ParamTraits<URLPattern> { typedef URLPattern param_type; static void Write(Message* m, const param_type& p) { + WriteParam(m, p.valid_schemes()); WriteParam(m, p.GetAsString()); } static bool Read(const Message* m, void** iter, param_type* p) { + int valid_schemes; std::string spec; - if (!ReadParam(m, iter, &spec)) + if (!ReadParam(m, iter, &valid_schemes) || + !ReadParam(m, iter, &spec)) return false; + p->set_valid_schemes(valid_schemes); return p->Parse(spec); } static void Log(const param_type& p, std::wstring* l) { diff --git a/chrome/renderer/extensions/extension_process_bindings.cc b/chrome/renderer/extensions/extension_process_bindings.cc index 39ed124..0e1d179 100644 --- a/chrome/renderer/extensions/extension_process_bindings.cc +++ b/chrome/renderer/extensions/extension_process_bindings.cc @@ -628,11 +628,21 @@ void ExtensionProcessBindings::SetHostPermissions( const GURL& extension_url, const std::vector<URLPattern>& permissions) { for (size_t i = 0; i < permissions.size(); ++i) { - WebSecurityPolicy::addOriginAccessWhitelistEntry( - extension_url, - WebKit::WebString::fromUTF8(permissions[i].scheme()), - WebKit::WebString::fromUTF8(permissions[i].host()), - permissions[i].match_subdomains()); + const char* schemes[] = { + chrome::kHttpScheme, + chrome::kHttpsScheme, + chrome::kFileScheme, + chrome::kChromeUIScheme, + }; + for (size_t j = 0; j < arraysize(schemes); ++j) { + if (permissions[i].MatchesScheme(schemes[j])) { + WebSecurityPolicy::addOriginAccessWhitelistEntry( + extension_url, + WebKit::WebString::fromUTF8(schemes[j]), + WebKit::WebString::fromUTF8(permissions[i].host()), + permissions[i].match_subdomains()); + } + } } } diff --git a/chrome/renderer/user_script_slave.cc b/chrome/renderer/user_script_slave.cc index c5812ad..b2b7a04 100644 --- a/chrome/renderer/user_script_slave.cc +++ b/chrome/renderer/user_script_slave.cc @@ -145,7 +145,8 @@ bool UserScriptSlave::InjectScripts(WebFrame* frame, UserScript::RunLocation location) { GURL frame_url = GURL(frame->url()); // Don't bother if this is not a URL we inject script into. - if (!URLPattern::IsValidScheme(frame_url.scheme())) + if (!URLPattern(UserScript::kValidUserScriptSchemes).IsValidScheme( + frame_url.scheme())) return true; // Don't inject user scripts into the gallery itself. This prevents |