summaryrefslogtreecommitdiffstats
path: root/chrome/common/extensions
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/common/extensions')
-rw-r--r--chrome/common/extensions/extension.cc60
-rw-r--r--chrome/common/extensions/extension.h29
-rw-r--r--chrome/common/extensions/extension_messages.cc1
-rw-r--r--chrome/common/extensions/extension_messages.h18
-rw-r--r--chrome/common/extensions/extension_unittest.cc183
-rw-r--r--chrome/common/extensions/url_pattern_set.cc40
-rw-r--r--chrome/common/extensions/url_pattern_set.h7
-rw-r--r--chrome/common/extensions/url_pattern_set_unittest.cc172
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);
+ }
+}