diff options
Diffstat (limited to 'chrome/common/extensions')
-rw-r--r-- | chrome/common/extensions/extension.cc | 60 | ||||
-rw-r--r-- | chrome/common/extensions/extension.h | 29 | ||||
-rw-r--r-- | chrome/common/extensions/extension_messages.cc | 1 | ||||
-rw-r--r-- | chrome/common/extensions/extension_messages.h | 18 | ||||
-rw-r--r-- | chrome/common/extensions/extension_unittest.cc | 183 | ||||
-rw-r--r-- | chrome/common/extensions/url_pattern_set.cc | 40 | ||||
-rw-r--r-- | chrome/common/extensions/url_pattern_set.h | 7 | ||||
-rw-r--r-- | chrome/common/extensions/url_pattern_set_unittest.cc | 172 |
8 files changed, 468 insertions, 42 deletions
diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc index 63fffc7..a01096b 100644 --- a/chrome/common/extensions/extension.cc +++ b/chrome/common/extensions/extension.cc @@ -3409,6 +3409,7 @@ bool Extension::HasMultipleUISurfaces() const { } bool Extension::CanExecuteScriptOnPage(const GURL& page_url, + int tab_id, const UserScript* script, std::string* error) const { base::AutoLock auto_lock(runtime_data_lock_); @@ -3431,6 +3432,16 @@ bool Extension::CanExecuteScriptOnPage(const GURL& page_url, !CanExecuteScriptEverywhere()) return false; + // If a tab ID is specified, try the tab-specific permissions. + if (tab_id >= 0) { + const URLPatternSet* tab_permissions = + runtime_data_.GetTabSpecificHostPermissions(tab_id); + if (tab_permissions && + tab_permissions->MatchesSecurityOrigin(page_url)) { + return true; + } + } + // If a script is specified, use its matches. if (script) return script->MatchesURL(page_url); @@ -3493,7 +3504,17 @@ bool Extension::CanExecuteScriptEverywhere() const { } bool Extension::CanCaptureVisiblePage(const GURL& page_url, + int tab_id, std::string *error) const { + if (tab_id >= 0) { + const URLPatternSet* tab_permissions = + GetTabSpecificHostPermissions(tab_id); + if (tab_permissions && + tab_permissions->MatchesSecurityOrigin(page_url)) { + return true; + } + } + if (HasHostPermission(page_url) || page_url.GetOrigin() == url()) return true; @@ -3669,6 +3690,24 @@ ExtensionAction* Extension::GetScriptBadge() const { return script_badge_.get(); } +const URLPatternSet* Extension::GetTabSpecificHostPermissions( + int tab_id) const { + base::AutoLock auto_lock(runtime_data_lock_); + return runtime_data_.GetTabSpecificHostPermissions(tab_id); +} + +void Extension::SetTabSpecificHostPermissions( + int tab_id, + const URLPatternSet& permissions) const { + base::AutoLock auto_lock(runtime_data_lock_); + runtime_data_.SetTabSpecificHostPermissions(tab_id, permissions); +} + +void Extension::ClearTabSpecificHostPermissions(int tab_id) const { + base::AutoLock auto_lock(runtime_data_lock_); + runtime_data_.ClearTabSpecificHostPermissions(tab_id); +} + bool Extension::CheckPlatformAppFeatures(std::string* utf8_error) { if (!is_platform_app()) return true; @@ -3714,6 +3753,27 @@ void Extension::RuntimeData::SetActivePermissions( active_permissions_ = active; } +const URLPatternSet* + Extension::RuntimeData::GetTabSpecificHostPermissions(int tab_id) const { + CHECK_GE(tab_id, 0); + TabHostPermissionsMap::const_iterator it = + tab_specific_host_permissions_.find(tab_id); + return (it != tab_specific_host_permissions_.end()) ? it->second.get() : NULL; +} + +void Extension::RuntimeData::SetTabSpecificHostPermissions( + int tab_id, + const URLPatternSet& hosts) { + CHECK_GE(tab_id, 0); + tab_specific_host_permissions_[tab_id] = + make_linked_ptr(new URLPatternSet(hosts)); +} + +void Extension::RuntimeData::ClearTabSpecificHostPermissions(int tab_id) { + CHECK_GE(tab_id, 0); + tab_specific_host_permissions_.erase(tab_id); +} + UnloadedExtensionInfo::UnloadedExtensionInfo( const Extension* extension, extension_misc::UnloadedExtensionReason reason) diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h index 6fac262..0239b80 100644 --- a/chrome/common/extensions/extension.h +++ b/chrome/common/extensions/extension.h @@ -494,6 +494,7 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // This method is also aware of certain special pages that extensions are // usually not allowed to run script on. bool CanExecuteScriptOnPage(const GURL& page_url, + int tab_id, const UserScript* script, std::string* error) const; @@ -505,7 +506,9 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // page as an image. Since a page may contain sensitive information, this // is restricted to the extension's host permissions as well as the // extension page itself. - bool CanCaptureVisiblePage(const GURL& page_url, std::string* error) const; + bool CanCaptureVisiblePage(const GURL& page_url, + int tab_id, + std::string* error) const; // Returns true if this extension updates itself using the extension // gallery. @@ -534,6 +537,20 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // shown for this extension in the location bar. ExtensionAction* GetScriptBadge() const; + // Gets the tab-specific host permissions of |tab_id|, or NULL if there + // aren't any. + // + // This is a weak pointer. Callers should create a copy before mutating any + // tab specific permissions. + const URLPatternSet* GetTabSpecificHostPermissions(int tab_id) const; + + // Sets the tab-specific host permissions of |tab_id| to |permissions|. + void SetTabSpecificHostPermissions(int tab_id, + const URLPatternSet& permissions) const; + + // Clears the tab-specific host permissions of |tab_id|. + void ClearTabSpecificHostPermissions(int tab_id) const; + // Accessors: const FilePath& path() const { return path_; } @@ -685,9 +702,19 @@ class Extension : public base::RefCountedThreadSafe<Extension> { void SetActivePermissions(const ExtensionPermissionSet* active); scoped_refptr<const ExtensionPermissionSet> GetActivePermissions() const; + const URLPatternSet* GetTabSpecificHostPermissions(int tab_id) const; + void SetTabSpecificHostPermissions(int tab_id, + const URLPatternSet& permissions); + void ClearTabSpecificHostPermissions(int tab_id); + private: friend class base::RefCountedThreadSafe<RuntimeData>; + scoped_refptr<const ExtensionPermissionSet> active_permissions_; + + typedef std::map<int, linked_ptr<const URLPatternSet> > + TabHostPermissionsMap; + TabHostPermissionsMap tab_specific_host_permissions_; }; // Chooses the extension ID for an extension based on a variety of criteria. diff --git a/chrome/common/extensions/extension_messages.cc b/chrome/common/extensions/extension_messages.cc index db167d1..82930970 100644 --- a/chrome/common/extensions/extension_messages.cc +++ b/chrome/common/extensions/extension_messages.cc @@ -43,6 +43,7 @@ scoped_refptr<Extension> extension->SetActivePermissions( new ExtensionPermissionSet(apis, explicit_hosts, scriptable_hosts)); + return extension; } diff --git a/chrome/common/extensions/extension_messages.h b/chrome/common/extensions/extension_messages.h index 42aab16..a79c9de 100644 --- a/chrome/common/extensions/extension_messages.h +++ b/chrome/common/extensions/extension_messages.h @@ -234,10 +234,14 @@ IPC_MESSAGE_CONTROL1(ExtensionMsg_UpdateUserScripts, IPC_MESSAGE_ROUTED1(ExtensionMsg_GetApplicationInfo, int32 /*page_id*/) -// Tell the renderer which browser window it's being attached to. +// Tell the render view which browser window it's being attached to. IPC_MESSAGE_ROUTED1(ExtensionMsg_UpdateBrowserWindowId, int /* id of browser window */) +// Tell the render view what its tab ID is. +IPC_MESSAGE_ROUTED1(ExtensionMsg_SetTabId, + int /* id of tab */) + // Tell the renderer to update an extension's permission set. IPC_MESSAGE_CONTROL5(ExtensionMsg_UpdatePermissions, int /* UpdateExtensionPermissionsInfo::REASON */, @@ -246,6 +250,18 @@ IPC_MESSAGE_CONTROL5(ExtensionMsg_UpdatePermissions, URLPatternSet /* explicit_hosts */, URLPatternSet /* scriptable_hosts */) +// Tell the renderer about new tab-specific permissions for an extension. +IPC_MESSAGE_CONTROL4(ExtensionMsg_UpdateTabSpecificPermissions, + int32 /* page_id (only relevant for the target tab) */, + int /* tab_id */, + std::string /* extension_id */, + URLPatternSet /* host */) + +// Tell the renderer to clear tab-specific permissions for some extensions. +IPC_MESSAGE_CONTROL2(ExtensionMsg_ClearTabSpecificPermissions, + int /* tab_id */, + std::vector<std::string> /* extension_ids */) + // Tell the renderer which type this view is. IPC_MESSAGE_ROUTED1(ExtensionMsg_NotifyRenderViewType, chrome::ViewType /* view_type */) diff --git a/chrome/common/extensions/extension_unittest.cc b/chrome/common/extensions/extension_unittest.cc index 4c5f57a..eee2da6 100644 --- a/chrome/common/extensions/extension_unittest.cc +++ b/chrome/common/extensions/extension_unittest.cc @@ -615,62 +615,62 @@ TEST(ExtensionTest, WantsFileAccess) { // <all_urls> permission extension = LoadManifest("permissions", "permissions_all_urls.json"); EXPECT_TRUE(extension->wants_file_access()); - EXPECT_FALSE(extension->CanExecuteScriptOnPage(file_url, NULL, NULL)); + EXPECT_FALSE(extension->CanExecuteScriptOnPage(file_url, -1, NULL, NULL)); extension = LoadManifest( "permissions", "permissions_all_urls.json", Extension::ALLOW_FILE_ACCESS); EXPECT_TRUE(extension->wants_file_access()); - EXPECT_TRUE(extension->CanExecuteScriptOnPage(file_url, NULL, NULL)); + EXPECT_TRUE(extension->CanExecuteScriptOnPage(file_url, -1, NULL, NULL)); // file:///* permission extension = LoadManifest("permissions", "permissions_file_scheme.json"); EXPECT_TRUE(extension->wants_file_access()); - EXPECT_FALSE(extension->CanExecuteScriptOnPage(file_url, NULL, NULL)); + EXPECT_FALSE(extension->CanExecuteScriptOnPage(file_url, -1, NULL, NULL)); extension = LoadManifest("permissions", "permissions_file_scheme.json", Extension::ALLOW_FILE_ACCESS); EXPECT_TRUE(extension->wants_file_access()); - EXPECT_TRUE(extension->CanExecuteScriptOnPage(file_url, NULL, NULL)); + EXPECT_TRUE(extension->CanExecuteScriptOnPage(file_url, -1, NULL, NULL)); // http://* permission extension = LoadManifest("permissions", "permissions_http_scheme.json"); EXPECT_FALSE(extension->wants_file_access()); - EXPECT_FALSE(extension->CanExecuteScriptOnPage(file_url, NULL, NULL)); + EXPECT_FALSE(extension->CanExecuteScriptOnPage(file_url, -1, NULL, NULL)); extension = LoadManifest("permissions", "permissions_http_scheme.json", Extension::ALLOW_FILE_ACCESS); EXPECT_FALSE(extension->wants_file_access()); - EXPECT_FALSE(extension->CanExecuteScriptOnPage(file_url, NULL, NULL)); + EXPECT_FALSE(extension->CanExecuteScriptOnPage(file_url, -1, NULL, NULL)); // <all_urls> content script match extension = LoadManifest("permissions", "content_script_all_urls.json"); EXPECT_TRUE(extension->wants_file_access()); EXPECT_FALSE(extension->CanExecuteScriptOnPage( - file_url, &extension->content_scripts()[0], NULL)); + file_url, -1, &extension->content_scripts()[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, &extension->content_scripts()[0], NULL)); + file_url, -1, &extension->content_scripts()[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, &extension->content_scripts()[0], NULL)); + file_url, -1, &extension->content_scripts()[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, &extension->content_scripts()[0], NULL)); + file_url, -1, &extension->content_scripts()[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, &extension->content_scripts()[0], NULL)); + file_url, -1, &extension->content_scripts()[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, &extension->content_scripts()[0], NULL)); + file_url, -1, &extension->content_scripts()[0], NULL)); } TEST(ExtensionTest, ExtraFlags) { @@ -689,49 +689,92 @@ TEST(ExtensionTest, ExtraFlags) { // Base class for testing the CanExecuteScriptOnPage and CanCaptureVisiblePage // methods of Extension for extensions with various permissions. class ExtensionScriptAndCaptureVisibleTest : public testing::Test { - public: - ExtensionScriptAndCaptureVisibleTest() { - PathService::Get(chrome::DIR_TEST_DATA, &dirpath_); + protected: + ExtensionScriptAndCaptureVisibleTest() + : http_url("http://www.google.com"), + http_url_with_path("http://www.google.com/index.html"), + https_url("https://www.google.com"), + file_url("file:///foo/bar"), + favicon_url("chrome://favicon/http://www.google.com"), + extension_url("chrome-extension://" + + Extension::GenerateIdForPath(FilePath(FILE_PATH_LITERAL("foo")))), + settings_url("chrome://settings"), + about_url("about:flags") { + urls_.insert(http_url); + urls_.insert(http_url_with_path); + urls_.insert(https_url); + urls_.insert(file_url); + urls_.insert(favicon_url); + urls_.insert(extension_url); + urls_.insert(settings_url); + urls_.insert(about_url); } bool Allowed(const Extension* extension, const GURL& url) { - return (extension->CanExecuteScriptOnPage(url, NULL, NULL) && - extension->CanCaptureVisiblePage(url, NULL)); + return Allowed(extension, url, -1); + } + + bool Allowed(const Extension* extension, const GURL& url, int tab_id) { + return (extension->CanExecuteScriptOnPage(url, tab_id, NULL, NULL) && + extension->CanCaptureVisiblePage(url, tab_id, NULL)); } bool CaptureOnly(const Extension* extension, const GURL& url) { - return !extension->CanExecuteScriptOnPage(url, NULL, NULL) && - extension->CanCaptureVisiblePage(url, NULL); + return CaptureOnly(extension, url, -1); + } + + bool CaptureOnly(const Extension* extension, const GURL& url, int tab_id) { + return !extension->CanExecuteScriptOnPage(url, tab_id, NULL, NULL) && + extension->CanCaptureVisiblePage(url, tab_id, NULL); } bool Blocked(const Extension* extension, const GURL& url) { - return !(extension->CanExecuteScriptOnPage(url, NULL, NULL) || - extension->CanCaptureVisiblePage(url, NULL)); + return Blocked(extension, url, -1); } - protected: - FilePath dirpath_; -}; + bool Blocked(const Extension* extension, const GURL& url, int tab_id) { + return !(extension->CanExecuteScriptOnPage(url, tab_id, NULL, NULL) || + extension->CanCaptureVisiblePage(url, tab_id, NULL)); + } + + bool AllowedExclusivelyOnTab( + const Extension* extension, + const std::set<GURL>& allowed_urls, + int tab_id) { + bool result = true; + for (std::set<GURL>::iterator it = urls_.begin(); it != urls_.end(); ++it) { + const GURL& url = *it; + if (allowed_urls.count(url)) + result &= Allowed(extension, url, tab_id); + else + result &= Blocked(extension, url, tab_id); + } + return result; + } -TEST_F(ExtensionScriptAndCaptureVisibleTest, Permissions) { - scoped_refptr<Extension> extension; // URLs that are "safe" to provide scripting and capture visible tab access // to if the permissions allow it. - GURL http_url("http://www.google.com"); - GURL https_url("https://www.google.com"); - GURL file_url("file:///foo/bar"); + const GURL http_url; + const GURL http_url_with_path; + const GURL https_url; + const GURL file_url; // We should allow host permission but not scripting permission for favicon // urls. - GURL favicon_url("chrome://favicon/http://www.google.com"); - - std::string dummy_id = - Extension::GenerateIdForPath(FilePath(FILE_PATH_LITERAL("whatever"))); + const GURL favicon_url; // URLs that regular extensions should never get access to. - GURL extension_url("chrome-extension://" + dummy_id); - GURL settings_url("chrome://settings"); - GURL about_url("about:flags"); + const GURL extension_url; + const GURL settings_url; + const GURL about_url; + + private: + // The set of all URLs above. + std::set<GURL> urls_; +}; + +TEST_F(ExtensionScriptAndCaptureVisibleTest, Permissions) { + scoped_refptr<Extension> extension; // Test <all_urls> for regular extensions. extension = LoadManifestStrict("script_and_capture", @@ -810,6 +853,74 @@ TEST_F(ExtensionScriptAndCaptureVisibleTest, Permissions) { EXPECT_FALSE(extension->HasHostPermission(settings_url)); } +TEST_F(ExtensionScriptAndCaptureVisibleTest, TabSpecific) { + scoped_refptr<Extension> extension = + LoadManifestStrict("script_and_capture", "tab_specific.json"); + + EXPECT_EQ(NULL, extension->GetTabSpecificHostPermissions(0)); + EXPECT_EQ(NULL, extension->GetTabSpecificHostPermissions(1)); + EXPECT_EQ(NULL, extension->GetTabSpecificHostPermissions(2)); + + std::set<GURL> no_urls; + + EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 0)); + EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 1)); + EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 2)); + + URLPatternSet allowed_hosts; + allowed_hosts.AddPattern(URLPattern(URLPattern::SCHEME_ALL, + http_url.spec())); + std::set<GURL> allowed_urls; + allowed_urls.insert(http_url); + // http_url_with_path() will also be allowed, because Extension should be + // considering the security origin of the URL not the URL itself, and + // http_url is in allowed_hosts. + allowed_urls.insert(http_url_with_path); + + extension->SetTabSpecificHostPermissions(0, allowed_hosts); + EXPECT_EQ(allowed_hosts, *extension->GetTabSpecificHostPermissions(0)); + + EXPECT_TRUE(AllowedExclusivelyOnTab(extension, allowed_urls, 0)); + EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 1)); + EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 2)); + + extension->ClearTabSpecificHostPermissions(0); + EXPECT_EQ(NULL, extension->GetTabSpecificHostPermissions(0)); + + EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 0)); + EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 1)); + EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 2)); + + std::set<GURL> more_allowed_urls = allowed_urls; + more_allowed_urls.insert(https_url); + URLPatternSet more_allowed_hosts = allowed_hosts; + more_allowed_hosts.AddPattern(URLPattern(URLPattern::SCHEME_ALL, + https_url.spec())); + + extension->SetTabSpecificHostPermissions(0, allowed_hosts); + EXPECT_EQ(allowed_hosts, *extension->GetTabSpecificHostPermissions(0)); + extension->SetTabSpecificHostPermissions(1, more_allowed_hosts); + EXPECT_EQ(more_allowed_hosts, *extension->GetTabSpecificHostPermissions(1)); + + EXPECT_TRUE(AllowedExclusivelyOnTab(extension, allowed_urls, 0)); + EXPECT_TRUE(AllowedExclusivelyOnTab(extension, more_allowed_urls, 1)); + EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 2)); + + extension->ClearTabSpecificHostPermissions(0); + EXPECT_EQ(NULL, extension->GetTabSpecificHostPermissions(0)); + + EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 0)); + EXPECT_TRUE(AllowedExclusivelyOnTab(extension, more_allowed_urls, 1)); + EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 2)); + + extension->ClearTabSpecificHostPermissions(1); + EXPECT_EQ(NULL, extension->GetTabSpecificHostPermissions(1)); + + EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 0)); + EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 1)); + EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 2)); +} + TEST(ExtensionTest, GenerateId) { std::string result; EXPECT_TRUE(Extension::GenerateId("", &result)); diff --git a/chrome/common/extensions/url_pattern_set.cc b/chrome/common/extensions/url_pattern_set.cc index c84df14..0948f81 100644 --- a/chrome/common/extensions/url_pattern_set.cc +++ b/chrome/common/extensions/url_pattern_set.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -8,6 +8,7 @@ #include <iterator> #include "base/logging.h" +#include "base/memory/linked_ptr.h" #include "base/values.h" #include "chrome/common/extensions/extension_error_utils.h" #include "chrome/common/extensions/url_pattern.h" @@ -53,6 +54,39 @@ void URLPatternSet::CreateUnion(const URLPatternSet& set1, out->patterns_, out->patterns_.begin())); } +// static +void URLPatternSet::CreateUnion(const std::vector<URLPatternSet>& sets, + URLPatternSet* out) { + out->ClearPatterns(); + if (sets.empty()) + return; + + // N-way union algorithm is basic O(nlog(n)) merge algorithm. + // + // Do the first merge step into a working set so that we don't mutate any of + // the input. + std::vector<URLPatternSet> working; + for (size_t i = 0; i < sets.size(); i += 2) { + if (i + 1 < sets.size()) { + URLPatternSet u; + URLPatternSet::CreateUnion(sets[i], sets[i + 1], &u); + working.push_back(u); + } else { + working.push_back(sets[i]); + } + } + + for (size_t skip = 1; skip < working.size(); skip *= 2) { + for (size_t i = 0; i < (working.size() - skip); i += skip) { + URLPatternSet u; + URLPatternSet::CreateUnion(working[i], working[i + skip], &u); + working[i].patterns_.swap(u.patterns_); + } + } + + out->patterns_.swap(working[0].patterns_); +} + URLPatternSet::URLPatternSet() {} URLPatternSet::URLPatternSet(const URLPatternSet& rhs) @@ -76,6 +110,10 @@ bool URLPatternSet::is_empty() const { return patterns_.empty(); } +size_t URLPatternSet::size() const { + return patterns_.size(); +} + void URLPatternSet::AddPattern(const URLPattern& pattern) { patterns_.insert(pattern); } diff --git a/chrome/common/extensions/url_pattern_set.h b/chrome/common/extensions/url_pattern_set.h index c912558..b94239f 100644 --- a/chrome/common/extensions/url_pattern_set.h +++ b/chrome/common/extensions/url_pattern_set.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -40,6 +40,10 @@ class URLPatternSet { const URLPatternSet& set2, URLPatternSet* out); + // Clears |out| and populates it with the union of all sets in |sets|. + static void CreateUnion(const std::vector<URLPatternSet>& sets, + URLPatternSet* out); + URLPatternSet(); URLPatternSet(const URLPatternSet& rhs); explicit URLPatternSet(const std::set<URLPattern>& patterns); @@ -49,6 +53,7 @@ class URLPatternSet { bool operator==(const URLPatternSet& rhs) const; bool is_empty() const; + size_t size() const; const std::set<URLPattern>& patterns() const { return patterns_; } const_iterator begin() const { return patterns_.begin(); } const_iterator end() const { return patterns_.end(); } diff --git a/chrome/common/extensions/url_pattern_set_unittest.cc b/chrome/common/extensions/url_pattern_set_unittest.cc index ff78d90..ed45948 100644 --- a/chrome/common/extensions/url_pattern_set_unittest.cc +++ b/chrome/common/extensions/url_pattern_set_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -9,12 +9,27 @@ namespace { -static void AddPattern(URLPatternSet* set, const std::string& pattern) { +void AddPattern(URLPatternSet* set, const std::string& pattern) { int schemes = URLPattern::SCHEME_ALL; set->AddPattern(URLPattern(schemes, pattern)); } +URLPatternSet Patterns(const std::string& pattern) { + URLPatternSet set; + AddPattern(&set, pattern); + return set; } + +URLPatternSet Patterns(const std::string& pattern1, + const std::string& pattern2) { + URLPatternSet set; + AddPattern(&set, pattern1); + AddPattern(&set, pattern2); + return set; +} + +} + TEST(URLPatternSetTest, Empty) { URLPatternSet set; EXPECT_FALSE(set.MatchesURL(GURL("http://www.foo.com/bar"))); @@ -199,3 +214,156 @@ TEST(URLPatternSetTest, Duplicates) { // The sets should still be equal after adding a duplicate. EXPECT_EQ(set2, set1); } + +TEST(URLPatternSetTest, NwayUnion) { + std::string google_a = "http://www.google.com/a*"; + std::string google_b = "http://www.google.com/b*"; + std::string google_c = "http://www.google.com/c*"; + std::string yahoo_a = "http://www.yahoo.com/a*"; + std::string yahoo_b = "http://www.yahoo.com/b*"; + std::string yahoo_c = "http://www.yahoo.com/c*"; + std::string reddit_a = "http://www.reddit.com/a*"; + std::string reddit_b = "http://www.reddit.com/b*"; + std::string reddit_c = "http://www.reddit.com/c*"; + + // Empty list. + { + std::vector<URLPatternSet> empty; + + URLPatternSet result; + URLPatternSet::CreateUnion(empty, &result); + + URLPatternSet expected; + EXPECT_EQ(expected, result); + } + + // Singleton list. + { + std::vector<URLPatternSet> test; + test.push_back(Patterns(google_a)); + + URLPatternSet result; + URLPatternSet::CreateUnion(test, &result); + + URLPatternSet expected = Patterns(google_a); + EXPECT_EQ(expected, result); + } + + // List with 2 elements. + { + + std::vector<URLPatternSet> test; + test.push_back(Patterns(google_a, google_b)); + test.push_back(Patterns(google_b, google_c)); + + URLPatternSet result; + URLPatternSet::CreateUnion(test, &result); + + URLPatternSet expected; + AddPattern(&expected, google_a); + AddPattern(&expected, google_b); + AddPattern(&expected, google_c); + EXPECT_EQ(expected, result); + } + + // List with 3 elements. + { + std::vector<URLPatternSet> test; + test.push_back(Patterns(google_a, google_b)); + test.push_back(Patterns(google_b, google_c)); + test.push_back(Patterns(yahoo_a, yahoo_b)); + + URLPatternSet result; + URLPatternSet::CreateUnion(test, &result); + + URLPatternSet expected; + AddPattern(&expected, google_a); + AddPattern(&expected, google_b); + AddPattern(&expected, google_c); + AddPattern(&expected, yahoo_a); + AddPattern(&expected, yahoo_b); + EXPECT_EQ(expected, result); + } + + // List with 7 elements. + { + std::vector<URLPatternSet> test; + test.push_back(Patterns(google_a)); + test.push_back(Patterns(google_b)); + test.push_back(Patterns(google_c)); + test.push_back(Patterns(yahoo_a)); + test.push_back(Patterns(yahoo_b)); + test.push_back(Patterns(yahoo_c)); + test.push_back(Patterns(reddit_a)); + + URLPatternSet result; + URLPatternSet::CreateUnion(test, &result); + + URLPatternSet expected; + AddPattern(&expected, google_a); + AddPattern(&expected, google_b); + AddPattern(&expected, google_c); + AddPattern(&expected, yahoo_a); + AddPattern(&expected, yahoo_b); + AddPattern(&expected, yahoo_c); + AddPattern(&expected, reddit_a); + EXPECT_EQ(expected, result); + } + + // List with 8 elements. + { + std::vector<URLPatternSet> test; + test.push_back(Patterns(google_a)); + test.push_back(Patterns(google_b)); + test.push_back(Patterns(google_c)); + test.push_back(Patterns(yahoo_a)); + test.push_back(Patterns(yahoo_b)); + test.push_back(Patterns(yahoo_c)); + test.push_back(Patterns(reddit_a)); + test.push_back(Patterns(reddit_b)); + + URLPatternSet result; + URLPatternSet::CreateUnion(test, &result); + + URLPatternSet expected; + AddPattern(&expected, google_a); + AddPattern(&expected, google_b); + AddPattern(&expected, google_c); + AddPattern(&expected, yahoo_a); + AddPattern(&expected, yahoo_b); + AddPattern(&expected, yahoo_c); + AddPattern(&expected, reddit_a); + AddPattern(&expected, reddit_b); + EXPECT_EQ(expected, result); + } + + // List with 9 elements. + { + + std::vector<URLPatternSet> test; + test.push_back(Patterns(google_a)); + test.push_back(Patterns(google_b)); + test.push_back(Patterns(google_c)); + test.push_back(Patterns(yahoo_a)); + test.push_back(Patterns(yahoo_b)); + test.push_back(Patterns(yahoo_c)); + test.push_back(Patterns(reddit_a)); + test.push_back(Patterns(reddit_b)); + test.push_back(Patterns(reddit_c)); + + URLPatternSet result; + URLPatternSet::CreateUnion(test, &result); + + URLPatternSet expected; + AddPattern(&expected, google_a); + AddPattern(&expected, google_b); + AddPattern(&expected, google_c); + AddPattern(&expected, yahoo_a); + AddPattern(&expected, yahoo_b); + AddPattern(&expected, yahoo_c); + AddPattern(&expected, reddit_a); + AddPattern(&expected, reddit_b); + AddPattern(&expected, reddit_c); + EXPECT_EQ(expected, result); + } +} |