summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkalman@chromium.org <kalman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-08-04 19:39:52 +0000
committerkalman@chromium.org <kalman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-08-04 19:39:52 +0000
commit6bc5985086b44b73590ae17ded63a08434762613 (patch)
tree21c7515e328ddb6feb827a064715526173e37b8a
parent1ebd66d98a72b9cb73d1bd2b855682e6ff67faa6 (diff)
downloadchromium_src-6bc5985086b44b73590ae17ded63a08434762613.zip
chromium_src-6bc5985086b44b73590ae17ded63a08434762613.tar.gz
chromium_src-6bc5985086b44b73590ae17ded63a08434762613.tar.bz2
Merge 280354 "Have the Debugger extension api check that it has ..."
> Have the Debugger extension api check that it has access to the tab > > Check PermissionsData::CanAccessTab() prior to attaching the debugger. > > BUG=367567 > > Review URL: https://codereview.chromium.org/352523003 TBR=rdevlin.cronin@chromium.org Review URL: https://codereview.chromium.org/439843002 git-svn-id: svn://svn.chromium.org/chrome/branches/2062/src@287394 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/api/debugger/debugger_api.cc30
-rw-r--r--chrome/browser/extensions/api/debugger/debugger_api_constants.cc2
-rw-r--r--chrome/browser/extensions/api/debugger/debugger_api_constants.h1
-rw-r--r--chrome/browser/extensions/api/debugger/debugger_apitest.cc134
-rw-r--r--chrome/browser/extensions/api/debugger/debugger_extension_apitest.cc2
-rw-r--r--chrome/test/data/extensions/api_test/debugger/background.js2
-rw-r--r--extensions/common/permissions/permissions_data.cc67
-rw-r--r--extensions/common/permissions/permissions_data.h8
-rw-r--r--extensions/common/permissions/permissions_data_unittest.cc91
-rw-r--r--extensions/common/url_pattern.cc9
-rw-r--r--extensions/common/url_pattern.h3
11 files changed, 310 insertions, 39 deletions
diff --git a/chrome/browser/extensions/api/debugger/debugger_api.cc b/chrome/browser/extensions/api/debugger/debugger_api.cc
index ebbc102..f9e5ad7 100644
--- a/chrome/browser/extensions/api/debugger/debugger_api.cc
+++ b/chrome/browser/extensions/api/debugger/debugger_api.cc
@@ -47,8 +47,12 @@
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_registry_observer.h"
#include "extensions/browser/extension_system.h"
+#include "extensions/common/constants.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/extension.h"
+#include "extensions/common/manifest_constants.h"
+#include "extensions/common/permissions/permissions_data.h"
+#include "extensions/common/switches.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
@@ -503,6 +507,7 @@ void DebuggerFunction::FormatErrorMessage(const std::string& format) {
}
bool DebuggerFunction::InitAgentHost() {
+ const Extension* extension = GetExtension();
if (debuggee_.tab_id) {
WebContents* web_contents = NULL;
bool result = ExtensionTabUtil::GetTabById(*debuggee_.tab_id,
@@ -513,12 +518,10 @@ bool DebuggerFunction::InitAgentHost() {
&web_contents,
NULL);
if (result && web_contents) {
- if (content::HasWebUIScheme(web_contents->GetURL())) {
- error_ = ErrorUtils::FormatErrorMessage(
- keys::kAttachToWebUIError,
- web_contents->GetURL().scheme());
+ // TODO(rdevlin.cronin) This should definitely be GetLastCommittedURL().
+ GURL url = web_contents->GetVisibleURL();
+ if (PermissionsData::IsRestrictedUrl(url, url, extension, &error_))
return false;
- }
agent_host_ = DevToolsAgentHost::GetOrCreateFor(web_contents);
}
} else if (debuggee_.extension_id) {
@@ -527,6 +530,12 @@ bool DebuggerFunction::InitAgentHost() {
->process_manager()
->GetBackgroundHostForExtension(*debuggee_.extension_id);
if (extension_host) {
+ if (PermissionsData::IsRestrictedUrl(extension_host->GetURL(),
+ extension_host->GetURL(),
+ extension,
+ &error_)) {
+ return false;
+ }
agent_host_ = DevToolsAgentHost::GetOrCreateFor(
extension_host->render_view_host());
}
@@ -588,25 +597,26 @@ bool DebuggerAttachFunction::RunAsync() {
return false;
}
+ const Extension* extension = GetExtension();
infobars::InfoBar* infobar = NULL;
if (!CommandLine::ForCurrentProcess()->
- HasSwitch(switches::kSilentDebuggerExtensionAPI)) {
+ HasSwitch(::switches::kSilentDebuggerExtensionAPI)) {
// Do not attach to the target if for any reason the infobar cannot be shown
// for this WebContents instance.
infobar = ExtensionDevToolsInfoBarDelegate::Create(
- agent_host_->GetRenderViewHost(), GetExtension()->name());
+ agent_host_->GetRenderViewHost(), extension->name());
if (!infobar) {
error_ = ErrorUtils::FormatErrorMessage(
keys::kSilentDebuggingRequired,
- switches::kSilentDebuggerExtensionAPI);
+ ::switches::kSilentDebuggerExtensionAPI);
return false;
}
}
new ExtensionDevToolsClientHost(GetProfile(),
agent_host_.get(),
- GetExtension()->id(),
- GetExtension()->name(),
+ extension->id(),
+ extension->name(),
debuggee_,
infobar);
SendResponse(true);
diff --git a/chrome/browser/extensions/api/debugger/debugger_api_constants.cc b/chrome/browser/extensions/api/debugger/debugger_api_constants.cc
index 70da45b..7ddcd82 100644
--- a/chrome/browser/extensions/api/debugger/debugger_api_constants.cc
+++ b/chrome/browser/extensions/api/debugger/debugger_api_constants.cc
@@ -8,8 +8,6 @@ namespace debugger_api_constants {
const char kAlreadyAttachedError[] =
"Another debugger is already attached to the * with id: *.";
-const char kAttachToWebUIError[] =
- "Can not attach to the page with the \"*://\" scheme.";
const char kNoTargetError[] = "No * with given id *.";
const char kInvalidTargetError[] =
"Either tab id or extension id must be specified.";
diff --git a/chrome/browser/extensions/api/debugger/debugger_api_constants.h b/chrome/browser/extensions/api/debugger/debugger_api_constants.h
index 0c7eaa7..f79b7b8 100644
--- a/chrome/browser/extensions/api/debugger/debugger_api_constants.h
+++ b/chrome/browser/extensions/api/debugger/debugger_api_constants.h
@@ -15,7 +15,6 @@ extern const char kOnDetach[];
// Errors.
extern const char kAlreadyAttachedError[];
-extern const char kAttachToWebUIError[];
extern const char kNoTargetError[];
extern const char kInvalidTargetError[];
extern const char kNotAttachedError[];
diff --git a/chrome/browser/extensions/api/debugger/debugger_apitest.cc b/chrome/browser/extensions/api/debugger/debugger_apitest.cc
index 878135a..f50ecbb 100644
--- a/chrome/browser/extensions/api/debugger/debugger_apitest.cc
+++ b/chrome/browser/extensions/api/debugger/debugger_apitest.cc
@@ -2,10 +2,144 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <string>
+
#include "base/command_line.h"
+#include "base/memory/ref_counted.h"
+#include "base/path_service.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/browser/extensions/api/debugger/debugger_api.h"
#include "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/browser/extensions/extension_function_test_utils.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "extensions/browser/extension_function.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_builder.h"
+#include "extensions/common/manifest_constants.h"
+#include "extensions/common/switches.h"
+#include "extensions/common/value_builder.h"
+
+namespace extensions {
+
+class DebuggerApiTest : public ExtensionApiTest {
+ protected:
+ virtual ~DebuggerApiTest() {}
+
+ virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE;
+ virtual void SetUpOnMainThread() OVERRIDE;
+
+ // Run the attach function. If |expected_error| is not empty, then the
+ // function should fail with the error. Otherwise, the function is expected
+ // to succeed.
+ testing::AssertionResult RunAttachFunction(const GURL& url,
+ const std::string& expected_error);
+
+ const Extension* extension() const { return extension_; }
+ base::CommandLine* command_line() const { return command_line_; }
+
+ private:
+ // The command-line for the test process, preserved in order to modify
+ // mid-test.
+ base::CommandLine* command_line_;
+
+ // A basic extension with the debugger permission.
+ scoped_refptr<const Extension> extension_;
+};
+
+void DebuggerApiTest::SetUpCommandLine(base::CommandLine* command_line) {
+ ExtensionApiTest::SetUpCommandLine(command_line);
+ // We need to hold onto |command_line| in order to modify it during the test.
+ command_line_ = command_line;
+}
+
+void DebuggerApiTest::SetUpOnMainThread() {
+ ExtensionApiTest::SetUpOnMainThread();
+ extension_ =
+ ExtensionBuilder().SetManifest(
+ DictionaryBuilder().Set("name", "debugger")
+ .Set("version", "0.1")
+ .Set("manifest_version", 2)
+ .Set("permissions",
+ ListBuilder().Append("debugger"))).Build();
+}
+
+testing::AssertionResult DebuggerApiTest::RunAttachFunction(
+ const GURL& url, const std::string& expected_error) {
+ ui_test_utils::NavigateToURL(browser(), url);
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ int tab_id = SessionID::IdForTab(web_contents);
+ scoped_refptr<DebuggerAttachFunction> attach_function =
+ new DebuggerAttachFunction();
+ attach_function->set_extension(extension_);
+ std::string args = base::StringPrintf("[{\"tabId\": %d}, \"1.1\"]", tab_id);
+
+ if (!expected_error.empty()) {
+ std::string actual_error =
+ extension_function_test_utils::RunFunctionAndReturnError(
+ attach_function, args, browser());
+ if (actual_error != expected_error) {
+ return testing::AssertionFailure() << "Did not get correct error: "
+ << "expected: " << expected_error << ", found: " << actual_error;
+ }
+ } else {
+ if (!RunFunction(attach_function,
+ args,
+ browser(),
+ extension_function_test_utils::NONE)) {
+ return testing::AssertionFailure() << "Could not run function: "
+ << attach_function->GetError();
+ }
+
+ // Clean up and detach.
+ scoped_refptr<DebuggerDetachFunction> detach_function =
+ new DebuggerDetachFunction();
+ detach_function->set_extension(extension_);
+ if (!RunFunction(detach_function,
+ base::StringPrintf("[{\"tabId\": %d}]", tab_id),
+ browser(),
+ extension_function_test_utils::NONE)) {
+ return testing::AssertionFailure() << "Could not detach: "
+ << detach_function->GetError();
+ }
+ }
+ return testing::AssertionSuccess();
+}
IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Debugger) {
ASSERT_TRUE(RunExtensionTest("debugger")) << message_;
}
+
+IN_PROC_BROWSER_TEST_F(DebuggerApiTest,
+ DebuggerNotAllowedOnOtherExtensionPages) {
+ // Load another arbitrary extension with an associated resource (popup.html).
+ base::FilePath path;
+ ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &path));
+ path = path.AppendASCII("extensions").AppendASCII("good_unpacked");
+ const Extension* another_extension = LoadExtension(path);
+ ASSERT_TRUE(another_extension);
+
+ GURL other_ext_url =
+ GURL(base::StringPrintf("chrome-extension://%s/popup.html",
+ another_extension->id().c_str()));
+
+ // This extension should not be able to access another extension.
+ EXPECT_TRUE(RunAttachFunction(
+ other_ext_url, manifest_errors::kCannotAccessExtensionUrl));
+
+ // This extension *should* be able to debug itself.
+ EXPECT_TRUE(RunAttachFunction(
+ GURL(base::StringPrintf("chrome-extension://%s/foo.html",
+ extension()->id().c_str())),
+ std::string()));
+
+ // Append extensions on chrome urls switch. The extension should now be able
+ // to debug any extension.
+ command_line()->AppendSwitch(switches::kExtensionsOnChromeURLs);
+ EXPECT_TRUE(RunAttachFunction(other_ext_url, std::string()));
+}
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/debugger/debugger_extension_apitest.cc b/chrome/browser/extensions/api/debugger/debugger_extension_apitest.cc
index 753d3ff..2829a31 100644
--- a/chrome/browser/extensions/api/debugger/debugger_extension_apitest.cc
+++ b/chrome/browser/extensions/api/debugger/debugger_extension_apitest.cc
@@ -5,12 +5,14 @@
#include "base/command_line.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/common/chrome_switches.h"
+#include "extensions/common/switches.h"
class ExtensionApiTestWithSwitch : public ExtensionApiTest {
public:
virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
ExtensionApiTest::SetUpCommandLine(command_line);
command_line->AppendSwitch(switches::kSilentDebuggerExtensionAPI);
+ command_line->AppendSwitch(extensions::switches::kExtensionsOnChromeURLs);
}
};
diff --git a/chrome/test/data/extensions/api_test/debugger/background.js b/chrome/test/data/extensions/api_test/debugger/background.js
index a6e2e8d..b902f86 100644
--- a/chrome/test/data/extensions/api_test/debugger/background.js
+++ b/chrome/test/data/extensions/api_test/debugger/background.js
@@ -108,7 +108,7 @@ chrome.test.runTests([
chrome.tabs.create({url:"chrome://version"}, function(tab) {
var debuggee = {tabId: tab.id};
chrome.debugger.attach(debuggee, protocolVersion,
- fail("Can not attach to the page with the \"chrome://\" scheme."));
+ fail("Cannot access a chrome:// URL"));
chrome.tabs.remove(tab.id);
});
},
diff --git a/extensions/common/permissions/permissions_data.cc b/extensions/common/permissions/permissions_data.cc
index 72717f7..1b160ca 100644
--- a/extensions/common/permissions/permissions_data.cc
+++ b/extensions/common/permissions/permissions_data.cc
@@ -17,6 +17,7 @@
#include "extensions/common/url_pattern_set.h"
#include "extensions/common/user_script.h"
#include "url/gurl.h"
+#include "url/url_constants.h"
namespace extensions {
@@ -70,6 +71,48 @@ bool PermissionsData::CanExecuteScriptEverywhere(const Extension* extension) {
whitelist.end();
}
+// static
+bool PermissionsData::IsRestrictedUrl(const GURL& document_url,
+ const GURL& top_frame_url,
+ const Extension* extension,
+ std::string* error) {
+ if (CanExecuteScriptEverywhere(extension))
+ return false;
+
+ // Check if the scheme is valid for extensions. If not, return.
+ if (!URLPattern::IsValidSchemeForExtensions(document_url.scheme()) &&
+ document_url.spec() != url::kAboutBlankURL) {
+ if (error) {
+ *error = ErrorUtils::FormatErrorMessage(
+ manifest_errors::kCannotAccessPage,
+ document_url.spec());
+ }
+ return true;
+ }
+
+ if (!ExtensionsClient::Get()->IsScriptableURL(document_url, error))
+ return true;
+
+ bool allow_on_chrome_urls = base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kExtensionsOnChromeURLs);
+ if (document_url.SchemeIs(content::kChromeUIScheme) &&
+ !allow_on_chrome_urls) {
+ if (error)
+ *error = manifest_errors::kCannotAccessChromeUrl;
+ return true;
+ }
+
+ if (top_frame_url.SchemeIs(kExtensionScheme) &&
+ top_frame_url.host() != extension->id() &&
+ !allow_on_chrome_urls) {
+ if (error)
+ *error = manifest_errors::kCannotAccessExtensionUrl;
+ return true;
+ }
+
+ return false;
+}
+
void PermissionsData::SetActivePermissions(
const PermissionSet* permissions) const {
base::AutoLock auto_lock(runtime_lock_);
@@ -283,30 +326,8 @@ bool PermissionsData::CanRunOnPage(const Extension* extension,
return false;
}
- bool can_execute_everywhere = CanExecuteScriptEverywhere(extension);
- if (!can_execute_everywhere &&
- !ExtensionsClient::Get()->IsScriptableURL(document_url, error)) {
- return false;
- }
-
- if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kExtensionsOnChromeURLs)) {
- if (document_url.SchemeIs(content::kChromeUIScheme) &&
- !can_execute_everywhere) {
- if (error)
- *error = manifest_errors::kCannotAccessChromeUrl;
- return false;
- }
- }
-
- if (top_frame_url.SchemeIs(kExtensionScheme) &&
- top_frame_url.GetOrigin() !=
- Extension::GetBaseURLFromExtensionId(extension->id()).GetOrigin() &&
- !can_execute_everywhere) {
- if (error)
- *error = manifest_errors::kCannotAccessExtensionUrl;
+ if (IsRestrictedUrl(document_url, top_frame_url, extension, error))
return false;
- }
if (HasTabSpecificPermissionToExecuteScript(tab_id, top_frame_url))
return true;
diff --git a/extensions/common/permissions/permissions_data.h b/extensions/common/permissions/permissions_data.h
index a23fb2b..9470d58 100644
--- a/extensions/common/permissions/permissions_data.h
+++ b/extensions/common/permissions/permissions_data.h
@@ -68,6 +68,14 @@ class PermissionsData {
// whitelist of extensions that can script all pages.
static bool CanExecuteScriptEverywhere(const Extension* extension);
+ // Returns true if the given |url| is restricted for the given |extension|,
+ // as is commonly the case for chrome:// urls.
+ // NOTE: You probably want to use CanAccessPage().
+ static bool IsRestrictedUrl(const GURL& document_url,
+ const GURL& top_frame_url,
+ const Extension* extension,
+ std::string* error);
+
// Sets the runtime permissions of the given |extension| to |permissions|.
void SetActivePermissions(const PermissionSet* active) const;
diff --git a/extensions/common/permissions/permissions_data_unittest.cc b/extensions/common/permissions/permissions_data_unittest.cc
index dc3aa86..3df1d44 100644
--- a/extensions/common/permissions/permissions_data_unittest.cc
+++ b/extensions/common/permissions/permissions_data_unittest.cc
@@ -87,6 +87,71 @@ bool RequiresActionForScriptExecution(const std::string& extension_id,
GURL::EmptyGURL());
}
+// Checks that urls are properly restricted for the given extension.
+void CheckRestrictedUrls(const Extension* extension,
+ bool block_chrome_urls) {
+ // We log the name so we know _which_ extension failed here.
+ const std::string& name = extension->name();
+ const GURL chrome_settings_url("chrome://settings/");
+ const GURL chrome_extension_url("chrome-extension://foo/bar.html");
+ const GURL google_url("https://www.google.com/");
+ const GURL self_url("chrome-extension://" + extension->id() + "/foo.html");
+ const GURL invalid_url("chrome-debugger://foo/bar.html");
+
+ std::string error;
+ EXPECT_EQ(block_chrome_urls,
+ PermissionsData::IsRestrictedUrl(
+ chrome_settings_url,
+ chrome_settings_url,
+ extension,
+ &error)) << name;
+ if (block_chrome_urls)
+ EXPECT_EQ(manifest_errors::kCannotAccessChromeUrl, error) << name;
+ else
+ EXPECT_TRUE(error.empty()) << name;
+
+ error.clear();
+ EXPECT_EQ(block_chrome_urls,
+ PermissionsData::IsRestrictedUrl(
+ chrome_extension_url,
+ chrome_extension_url,
+ extension,
+ &error)) << name;
+ if (block_chrome_urls)
+ EXPECT_EQ(manifest_errors::kCannotAccessExtensionUrl, error) << name;
+ else
+ EXPECT_TRUE(error.empty()) << name;
+
+ // Google should never be a restricted url.
+ error.clear();
+ EXPECT_FALSE(PermissionsData::IsRestrictedUrl(
+ google_url, google_url, extension, &error)) << name;
+ EXPECT_TRUE(error.empty()) << name;
+
+ // We should always be able to access our own extension pages.
+ error.clear();
+ EXPECT_FALSE(PermissionsData::IsRestrictedUrl(
+ self_url, self_url, extension, &error)) << name;
+ EXPECT_TRUE(error.empty()) << name;
+
+ // We should only allow other schemes for extensions when it's a whitelisted
+ // extension.
+ error.clear();
+ bool allow_on_other_schemes =
+ PermissionsData::CanExecuteScriptEverywhere(extension);
+ EXPECT_EQ(!allow_on_other_schemes,
+ PermissionsData::IsRestrictedUrl(
+ invalid_url, invalid_url, extension, &error)) << name;
+ if (!allow_on_other_schemes) {
+ EXPECT_EQ(ErrorUtils::FormatErrorMessage(
+ manifest_errors::kCannotAccessPage,
+ invalid_url.spec()),
+ error) << name;
+ } else {
+ EXPECT_TRUE(error.empty());
+ }
+}
+
} // namespace
TEST(ExtensionPermissionsTest, EffectiveHostPermissions) {
@@ -242,6 +307,28 @@ TEST(ExtensionPermissionsTest, RequiresActionForScriptExecution) {
extension, 0, GURL("https://www.google.com/")));
}
+TEST(ExtensionPermissionsTest, IsRestrictedUrl) {
+ scoped_refptr<const Extension> extension =
+ GetExtensionWithHostPermission("normal_extension",
+ kAllHostsPermission,
+ Manifest::INTERNAL);
+ // Chrome urls should be blocked for normal extensions.
+ CheckRestrictedUrls(extension, true);
+
+ scoped_refptr<const Extension> component =
+ GetExtensionWithHostPermission("component",
+ kAllHostsPermission,
+ Manifest::COMPONENT);
+ // Chrome urls should be accessible by component extensions.
+ CheckRestrictedUrls(component, false);
+
+ base::CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kExtensionsOnChromeURLs);
+ // Enabling the switch should allow all extensions to access chrome urls.
+ CheckRestrictedUrls(extension, false);
+
+}
+
TEST(ExtensionPermissionsTest, GetPermissionMessages_ManyAPIPermissions) {
scoped_refptr<Extension> extension;
extension = LoadManifest("permissions", "many-apis.json");
@@ -553,8 +640,8 @@ TEST_F(ExtensionScriptAndCaptureVisibleTest, PermissionsWithChromeURLsEnabled) {
EXPECT_TRUE(AllowedScript(extension.get(), https_url, http_url_with_path));
EXPECT_TRUE(AllowedScript(extension.get(), http_url, within_extension_url));
EXPECT_TRUE(AllowedScript(extension.get(), https_url, within_extension_url));
- EXPECT_TRUE(BlockedScript(extension.get(), http_url, extension_url));
- EXPECT_TRUE(BlockedScript(extension.get(), https_url, extension_url));
+ EXPECT_TRUE(AllowedScript(extension.get(), http_url, extension_url));
+ EXPECT_TRUE(AllowedScript(extension.get(), https_url, extension_url));
const PermissionsData* permissions_data = extension->permissions_data();
EXPECT_FALSE(permissions_data->HasHostPermission(settings_url));
diff --git a/extensions/common/url_pattern.cc b/extensions/common/url_pattern.cc
index c7a3711..ee7d164 100644
--- a/extensions/common/url_pattern.cc
+++ b/extensions/common/url_pattern.cc
@@ -109,6 +109,15 @@ std::string StripTrailingWildcard(const std::string& path) {
} // namespace
+// static
+bool URLPattern::IsValidSchemeForExtensions(const std::string& scheme) {
+ for (size_t i = 0; i < arraysize(kValidSchemes); ++i) {
+ if (scheme == kValidSchemes[i])
+ return true;
+ }
+ return false;
+}
+
URLPattern::URLPattern()
: valid_schemes_(SCHEME_NONE),
match_all_urls_(false),
diff --git a/extensions/common/url_pattern.h b/extensions/common/url_pattern.h
index 70109db..b4c566b 100644
--- a/extensions/common/url_pattern.h
+++ b/extensions/common/url_pattern.h
@@ -78,6 +78,9 @@ class URLPattern {
// The <all_urls> string pattern.
static const char kAllUrlsPattern[];
+ // Returns true if the given |scheme| is considered valid for extensions.
+ static bool IsValidSchemeForExtensions(const std::string& scheme);
+
explicit URLPattern(int valid_schemes);
// Convenience to construct a URLPattern from a string. If the string is not