summaryrefslogtreecommitdiff
path: root/chromium/extensions
diff options
context:
space:
mode:
authorZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
committerZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
commit679147eead574d186ebf3069647b4c23e8ccace6 (patch)
treefc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/extensions
downloadqtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz
Initial import.
Diffstat (limited to 'chromium/extensions')
-rw-r--r--chromium/extensions/DEPS13
-rw-r--r--chromium/extensions/OWNERS11
-rw-r--r--chromium/extensions/README3
-rw-r--r--chromium/extensions/browser/DEPS3
-rw-r--r--chromium/extensions/browser/extension_error.cc172
-rw-r--r--chromium/extensions/browser/extension_error.h121
-rw-r--r--chromium/extensions/browser/extension_prefs_scope.h28
-rw-r--r--chromium/extensions/browser/file_reader.cc32
-rw-r--r--chromium/extensions/browser/file_reader.h46
-rw-r--r--chromium/extensions/browser/file_reader_unittest.cc105
-rw-r--r--chromium/extensions/browser/pref_names.cc37
-rw-r--r--chromium/extensions/browser/pref_names.h42
-rw-r--r--chromium/extensions/browser/view_type_utils.cc44
-rw-r--r--chromium/extensions/browser/view_type_utils.h24
-rw-r--r--chromium/extensions/common/constants.cc40
-rw-r--r--chromium/extensions/common/constants.h57
-rw-r--r--chromium/extensions/common/crx_file.cc81
-rw-r--r--chromium/extensions/common/crx_file.h80
-rw-r--r--chromium/extensions/common/draggable_region.cc13
-rw-r--r--chromium/extensions/common/draggable_region.h21
-rw-r--r--chromium/extensions/common/error_utils.cc66
-rw-r--r--chromium/extensions/common/error_utils.h44
-rw-r--r--chromium/extensions/common/event_filter.cc181
-rw-r--r--chromium/extensions/common/event_filter.h127
-rw-r--r--chromium/extensions/common/event_filter_unittest.cc253
-rw-r--r--chromium/extensions/common/event_filtering_info.cc48
-rw-r--r--chromium/extensions/common/event_filtering_info.h53
-rw-r--r--chromium/extensions/common/event_matcher.cc61
-rw-r--r--chromium/extensions/common/event_matcher.h57
-rw-r--r--chromium/extensions/common/extension_paths.cc41
-rw-r--r--chromium/extensions/common/extension_paths.h27
-rw-r--r--chromium/extensions/common/extension_resource.cc128
-rw-r--r--chromium/extensions/common/extension_resource.h89
-rw-r--r--chromium/extensions/common/extension_resource_unittest.cc164
-rw-r--r--chromium/extensions/common/extensions_client.cc32
-rw-r--r--chromium/extensions/common/extensions_client.h38
-rw-r--r--chromium/extensions/common/features/feature_provider.cc16
-rw-r--r--chromium/extensions/common/features/feature_provider.h36
-rw-r--r--chromium/extensions/common/id_util.cc73
-rw-r--r--chromium/extensions/common/id_util.h35
-rw-r--r--chromium/extensions/common/id_util_unittest.cc44
-rw-r--r--chromium/extensions/common/install_warning.cc24
-rw-r--r--chromium/extensions/common/install_warning.h38
-rw-r--r--chromium/extensions/common/manifest_constants.cc151
-rw-r--r--chromium/extensions/common/manifest_constants.h157
-rw-r--r--chromium/extensions/common/matcher/DEPS3
-rw-r--r--chromium/extensions/common/matcher/OWNERS1
-rw-r--r--chromium/extensions/common/matcher/regex_set_matcher.cc113
-rw-r--r--chromium/extensions/common/matcher/regex_set_matcher.h82
-rw-r--r--chromium/extensions/common/matcher/regex_set_matcher_unittest.cc61
-rw-r--r--chromium/extensions/common/matcher/string_pattern.cc20
-rw-r--r--chromium/extensions/common/matcher/string_pattern.h42
-rw-r--r--chromium/extensions/common/matcher/string_pattern_unittest.cc23
-rw-r--r--chromium/extensions/common/matcher/substring_set_matcher.cc272
-rw-r--r--chromium/extensions/common/matcher/substring_set_matcher.h140
-rw-r--r--chromium/extensions/common/matcher/substring_set_matcher_unittest.cc167
-rw-r--r--chromium/extensions/common/matcher/url_matcher.cc885
-rw-r--r--chromium/extensions/common/matcher/url_matcher.h355
-rw-r--r--chromium/extensions/common/matcher/url_matcher_constants.cc34
-rw-r--r--chromium/extensions/common/matcher/url_matcher_constants.h39
-rw-r--r--chromium/extensions/common/matcher/url_matcher_factory.cc277
-rw-r--r--chromium/extensions/common/matcher/url_matcher_factory.h62
-rw-r--r--chromium/extensions/common/matcher/url_matcher_factory_unittest.cc339
-rw-r--r--chromium/extensions/common/matcher/url_matcher_helpers.cc31
-rw-r--r--chromium/extensions/common/matcher/url_matcher_helpers.h27
-rw-r--r--chromium/extensions/common/matcher/url_matcher_unittest.cc682
-rw-r--r--chromium/extensions/common/one_shot_event.cc70
-rw-r--r--chromium/extensions/common/one_shot_event.h98
-rw-r--r--chromium/extensions/common/one_shot_event_unittest.cc111
-rw-r--r--chromium/extensions/common/permissions/permissions_provider.h37
-rw-r--r--chromium/extensions/common/switches.cc36
-rw-r--r--chromium/extensions/common/switches.h24
-rw-r--r--chromium/extensions/common/url_pattern.cc540
-rw-r--r--chromium/extensions/common/url_pattern.h245
-rw-r--r--chromium/extensions/common/url_pattern_set.cc233
-rw-r--r--chromium/extensions/common/url_pattern_set.h105
-rw-r--r--chromium/extensions/common/url_pattern_set_unittest.cc396
-rw-r--r--chromium/extensions/common/url_pattern_unittest.cc802
-rw-r--r--chromium/extensions/common/user_script.cc236
-rw-r--r--chromium/extensions/common/user_script.h260
-rw-r--r--chromium/extensions/common/user_script_unittest.cc220
-rw-r--r--chromium/extensions/common/view_type.cc19
-rw-r--r--chromium/extensions/common/view_type.h45
83 files changed, 10088 insertions, 0 deletions
diff --git a/chromium/extensions/DEPS b/chromium/extensions/DEPS
new file mode 100644
index 00000000000..850ea194bf9
--- /dev/null
+++ b/chromium/extensions/DEPS
@@ -0,0 +1,13 @@
+include_rules = [
+ "+content/public/common",
+ "+crypto",
+ "+testing",
+ "+ui"
+]
+
+# More specific rules for what we are allowed to include.
+specific_include_rules = {
+ ".*test\.cc": [
+ "+content/public/test",
+ ]
+}
diff --git a/chromium/extensions/OWNERS b/chromium/extensions/OWNERS
new file mode 100644
index 00000000000..a6cd87dfa0d
--- /dev/null
+++ b/chromium/extensions/OWNERS
@@ -0,0 +1,11 @@
+# This should match chrome/browser/extensions/OWNERS
+asargent@chromium.org
+benwells@chromium.org
+finnur@chromium.org
+jyasskin@chromium.org
+kalman@chromium.org
+koz@chromium.org
+mek@chromium.org
+miket@chromium.org
+mpcomplete@chromium.org
+yoz@chromium.org
diff --git a/chromium/extensions/README b/chromium/extensions/README
new file mode 100644
index 00000000000..c994549997e
--- /dev/null
+++ b/chromium/extensions/README
@@ -0,0 +1,3 @@
+This will become a reusable extensions module. It implements the core parts of
+Chrome's extension system, and can be used with any host of the 'content'
+module.
diff --git a/chromium/extensions/browser/DEPS b/chromium/extensions/browser/DEPS
new file mode 100644
index 00000000000..1c35d9ca694
--- /dev/null
+++ b/chromium/extensions/browser/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+content/public/browser",
+]
diff --git a/chromium/extensions/browser/extension_error.cc b/chromium/extensions/browser/extension_error.cc
new file mode 100644
index 00000000000..8b6196c4200
--- /dev/null
+++ b/chromium/extensions/browser/extension_error.cc
@@ -0,0 +1,172 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/extension_error.h"
+
+#include "base/json/json_reader.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "extensions/common/constants.h"
+#include "url/gurl.h"
+
+using base::string16;
+
+namespace extensions {
+
+namespace {
+
+const char kLineNumberKey[] = "lineNumber";
+const char kColumnNumberKey[] = "columnNumber";
+const char kURLKey[] = "url";
+const char kFunctionNameKey[] = "functionName";
+const char kExecutionContextURLKey[] = "executionContextURL";
+const char kStackTraceKey[] = "stackTrace";
+
+// Try to retrieve an extension ID from a |url|. On success, returns true and
+// populates |extension_id| with the ID. On failure, returns false and leaves
+// extension_id untouched.
+bool GetExtensionIDFromGURL(const GURL& url, std::string* extension_id) {
+ if (url.SchemeIs(kExtensionScheme)) {
+ *extension_id = url.host();
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+ExtensionError::ExtensionError(Type type,
+ const std::string& extension_id,
+ bool from_incognito,
+ const string16& source,
+ const string16& message)
+ : type_(type),
+ extension_id_(extension_id),
+ from_incognito_(from_incognito),
+ source_(source),
+ message_(message) {
+}
+
+ExtensionError::~ExtensionError() {
+}
+
+std::string ExtensionError::PrintForTest() const {
+ return std::string("Extension Error:") +
+ "\n OTR: " + std::string(from_incognito_ ? "true" : "false") +
+ "\n Source: " + base::UTF16ToUTF8(source_) +
+ "\n Message: " + base::UTF16ToUTF8(message_) +
+ "\n ID: " + extension_id_;
+}
+
+ManifestParsingError::ManifestParsingError(const std::string& extension_id,
+ const string16& message)
+ : ExtensionError(ExtensionError::MANIFEST_PARSING_ERROR,
+ extension_id,
+ false, // extensions can't be installed while incognito.
+ base::FilePath(kManifestFilename).AsUTF16Unsafe(),
+ message) {
+}
+
+ManifestParsingError::~ManifestParsingError() {
+}
+
+std::string ManifestParsingError::PrintForTest() const {
+ return ExtensionError::PrintForTest() +
+ "\n Type: ManifestParsingError";
+}
+
+JavascriptRuntimeError::StackFrame::StackFrame() : line_number(-1),
+ column_number(-1) {
+}
+
+JavascriptRuntimeError::StackFrame::StackFrame(size_t frame_line,
+ size_t frame_column,
+ const string16& frame_url,
+ const string16& frame_function)
+ : line_number(frame_line),
+ column_number(frame_column),
+ url(frame_url),
+ function(frame_function) {
+}
+
+JavascriptRuntimeError::StackFrame::~StackFrame() {
+}
+
+JavascriptRuntimeError::JavascriptRuntimeError(bool from_incognito,
+ const string16& source,
+ const string16& message,
+ logging::LogSeverity level,
+ const string16& details)
+ : ExtensionError(ExtensionError::JAVASCRIPT_RUNTIME_ERROR,
+ std::string(), // We don't know the id yet.
+ from_incognito,
+ source,
+ message),
+ level_(level) {
+ ParseDetails(details);
+ DetermineExtensionID();
+}
+
+JavascriptRuntimeError::~JavascriptRuntimeError() {
+}
+
+std::string JavascriptRuntimeError::PrintForTest() const {
+ std::string result = ExtensionError::PrintForTest() +
+ "\n Type: JavascriptRuntimeError"
+ "\n Context: " + base::UTF16ToUTF8(execution_context_url_) +
+ "\n Stack Trace: ";
+ for (StackTrace::const_iterator iter = stack_trace_.begin();
+ iter != stack_trace_.end(); ++iter) {
+ result += "\n {"
+ "\n Line: " + base::IntToString(iter->line_number) +
+ "\n Column: " + base::IntToString(iter->column_number) +
+ "\n URL: " + base::UTF16ToUTF8(iter->url) +
+ "\n Function: " + base::UTF16ToUTF8(iter->function) +
+ "\n }";
+ }
+ return result;
+}
+
+void JavascriptRuntimeError::ParseDetails(const string16& details) {
+ scoped_ptr<base::Value> value(
+ base::JSONReader::Read(base::UTF16ToUTF8(details)));
+ const base::DictionaryValue* details_value;
+ const base::ListValue* trace_value = NULL;
+
+ // The |details| value should contain an execution context url and a stack
+ // trace.
+ if (!value.get() ||
+ !value->GetAsDictionary(&details_value) ||
+ !details_value->GetString(kExecutionContextURLKey,
+ &execution_context_url_) ||
+ !details_value->GetList(kStackTraceKey, &trace_value)) {
+ NOTREACHED();
+ return;
+ }
+
+ int line = 0;
+ int column = 0;
+ string16 url;
+
+ for (size_t i = 0; i < trace_value->GetSize(); ++i) {
+ const base::DictionaryValue* frame_value = NULL;
+ CHECK(trace_value->GetDictionary(i, &frame_value));
+
+ frame_value->GetInteger(kLineNumberKey, &line);
+ frame_value->GetInteger(kColumnNumberKey, &column);
+ frame_value->GetString(kURLKey, &url);
+
+ string16 function;
+ frame_value->GetString(kFunctionNameKey, &function); // This can be empty.
+ stack_trace_.push_back(StackFrame(line, column, url, function));
+ }
+}
+
+void JavascriptRuntimeError::DetermineExtensionID() {
+ if (!GetExtensionIDFromGURL(GURL(source_), &extension_id_))
+ GetExtensionIDFromGURL(GURL(execution_context_url_), &extension_id_);
+}
+
+} // namespace extensions
diff --git a/chromium/extensions/browser/extension_error.h b/chromium/extensions/browser/extension_error.h
new file mode 100644
index 00000000000..1cd4a7b69bf
--- /dev/null
+++ b/chromium/extensions/browser/extension_error.h
@@ -0,0 +1,121 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_BROWSER_EXTENSION_ERROR_H_
+#define EXTENSIONS_BROWSER_EXTENSION_ERROR_H_
+
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/strings/string16.h"
+
+namespace extensions {
+
+class ExtensionError {
+ public:
+ enum Type {
+ MANIFEST_PARSING_ERROR,
+ JAVASCRIPT_RUNTIME_ERROR
+ };
+
+ virtual ~ExtensionError();
+
+ virtual std::string PrintForTest() const;
+
+ Type type() const { return type_; }
+ const base::string16& source() const { return source_; }
+ const base::string16& message() const { return message_; }
+ const std::string& extension_id() const { return extension_id_; }
+ bool from_incognito() const { return from_incognito_; }
+
+ protected:
+ ExtensionError(Type type,
+ const std::string& extension_id,
+ bool from_incognito,
+ const base::string16& source,
+ const base::string16& message);
+
+ // Which type of error this is.
+ Type type_;
+ // The ID of the extension which caused the error.
+ std::string extension_id_;
+ // Whether or not the error was caused while incognito.
+ bool from_incognito_;
+ // The source for the error; this can be a script, web page, or manifest file.
+ // This is stored as a string (rather than a url) since it can be a Chrome
+ // script file (e.g., event_bindings.js).
+ base::string16 source_;
+ // The error message itself.
+ base::string16 message_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionError);
+};
+
+class ManifestParsingError : public ExtensionError {
+ public:
+ ManifestParsingError(const std::string& extension_id,
+ const base::string16& message);
+ virtual ~ManifestParsingError();
+
+ virtual std::string PrintForTest() const OVERRIDE;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ManifestParsingError);
+};
+
+class JavascriptRuntimeError : public ExtensionError {
+ public:
+ struct StackFrame {
+ size_t line_number;
+ size_t column_number;
+ // This is stored as a string (rather than a url) since it can be a
+ // Chrome script file (e.g., event_bindings.js).
+ base::string16 url;
+ base::string16 function; // optional
+
+ // STL-Required constructor
+ StackFrame();
+
+ StackFrame(size_t frame_line,
+ size_t frame_column,
+ const base::string16& frame_url,
+ const base::string16& frame_function /* can be empty */);
+
+ ~StackFrame();
+ };
+ typedef std::vector<StackFrame> StackTrace;
+
+ JavascriptRuntimeError(bool from_incognito,
+ const base::string16& source,
+ const base::string16& message,
+ logging::LogSeverity level,
+ const base::string16& details);
+ virtual ~JavascriptRuntimeError();
+
+ virtual std::string PrintForTest() const OVERRIDE;
+
+ logging::LogSeverity level() const { return level_; }
+ const base::string16& execution_context_url() const {
+ return execution_context_url_;
+ }
+ const StackTrace& stack_trace() const { return stack_trace_; }
+ private:
+ // Parse the JSON |details| passed to the error. This includes a stack trace
+ // and an execution context url.
+ void ParseDetails(const base::string16& details);
+ // Try to determine the ID of the extension. This may be obtained through the
+ // reported source, or through the execution context url.
+ void DetermineExtensionID();
+
+ logging::LogSeverity level_;
+ base::string16 execution_context_url_;
+ StackTrace stack_trace_;
+
+ DISALLOW_COPY_AND_ASSIGN(JavascriptRuntimeError);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_BROWSER_EXTENSION_ERROR_H_
diff --git a/chromium/extensions/browser/extension_prefs_scope.h b/chromium/extensions/browser/extension_prefs_scope.h
new file mode 100644
index 00000000000..3747ee817fb
--- /dev/null
+++ b/chromium/extensions/browser/extension_prefs_scope.h
@@ -0,0 +1,28 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_BROWSER_EXTENSION_PREFS_SCOPE_H_
+#define EXTENSIONS_BROWSER_EXTENSION_PREFS_SCOPE_H_
+
+#include "base/basictypes.h"
+
+namespace extensions {
+
+// Scope for a preference.
+enum ExtensionPrefsScope {
+ // Regular profile and incognito.
+ kExtensionPrefsScopeRegular,
+ // Regular profile only.
+ kExtensionPrefsScopeRegularOnly,
+ // Incognito profile; preference is persisted to disk and remains active
+ // after a browser restart.
+ kExtensionPrefsScopeIncognitoPersistent,
+ // Incognito profile; preference is kept in memory and deleted when the
+ // incognito session is terminated.
+ kExtensionPrefsScopeIncognitoSessionOnly
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_BROWSER_EXTENSION_PREFS_SCOPE_H_
diff --git a/chromium/extensions/browser/file_reader.cc b/chromium/extensions/browser/file_reader.cc
new file mode 100644
index 00000000000..f1a560016f4
--- /dev/null
+++ b/chromium/extensions/browser/file_reader.cc
@@ -0,0 +1,32 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/file_reader.h"
+
+#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/message_loop/message_loop.h"
+#include "content/public/browser/browser_thread.h"
+
+using content::BrowserThread;
+
+FileReader::FileReader(const extensions::ExtensionResource& resource,
+ const Callback& callback)
+ : resource_(resource),
+ callback_(callback),
+ origin_loop_(base::MessageLoop::current()) {}
+
+void FileReader::Start() {
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&FileReader::ReadFileOnBackgroundThread, this));
+}
+
+FileReader::~FileReader() {}
+
+void FileReader::ReadFileOnBackgroundThread() {
+ std::string data;
+ bool success = file_util::ReadFileToString(resource_.GetFilePath(), &data);
+ origin_loop_->PostTask(FROM_HERE, base::Bind(callback_, success, data));
+}
diff --git a/chromium/extensions/browser/file_reader.h b/chromium/extensions/browser/file_reader.h
new file mode 100644
index 00000000000..109a97a8b47
--- /dev/null
+++ b/chromium/extensions/browser/file_reader.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_BROWSER_FILE_READER_H_
+#define EXTENSIONS_BROWSER_FILE_READER_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "extensions/common/extension_resource.h"
+
+namespace base {
+class MessageLoop;
+}
+
+// This file defines an interface for reading a file asynchronously on a
+// background thread.
+// Consider abstracting out a FilePathProvider (ExtensionResource) and moving
+// back to chrome/browser/net if other subsystems want to use it.
+class FileReader : public base::RefCountedThreadSafe<FileReader> {
+ public:
+ // Reports success or failure and the data of the file upon success.
+ typedef base::Callback<void(bool, const std::string&)> Callback;
+
+ FileReader(const extensions::ExtensionResource& resource,
+ const Callback& callback);
+
+ // Called to start reading the file on a background thread. Upon completion,
+ // the callback will be notified of the results.
+ void Start();
+
+ private:
+ friend class base::RefCountedThreadSafe<FileReader>;
+
+ virtual ~FileReader();
+
+ void ReadFileOnBackgroundThread();
+
+ extensions::ExtensionResource resource_;
+ Callback callback_;
+ base::MessageLoop* origin_loop_;
+};
+
+#endif // EXTENSIONS_BROWSER_FILE_READER_H_
diff --git a/chromium/extensions/browser/file_reader_unittest.cc b/chromium/extensions/browser/file_reader_unittest.cc
new file mode 100644
index 00000000000..17a466ca4c1
--- /dev/null
+++ b/chromium/extensions/browser/file_reader_unittest.cc
@@ -0,0 +1,105 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/message_loop/message_loop.h"
+#include "base/path_service.h"
+#include "content/public/test/test_browser_thread.h"
+#include "extensions/browser/file_reader.h"
+#include "extensions/common/extension_paths.h"
+#include "extensions/common/extension_resource.h"
+#include "extensions/common/id_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using content::BrowserThread;
+
+namespace extensions {
+
+class FileReaderTest : public testing::Test {
+ public:
+ FileReaderTest() : file_thread_(BrowserThread::FILE) {
+ file_thread_.Start();
+ }
+ private:
+ base::MessageLoop message_loop_;
+ content::TestBrowserThread file_thread_;
+};
+
+class Receiver {
+ public:
+ Receiver() : succeeded_(false) {
+ }
+
+ FileReader::Callback NewCallback() {
+ return base::Bind(&Receiver::DidReadFile, base::Unretained(this));
+ }
+
+ bool succeeded() const { return succeeded_; }
+ const std::string& data() const { return data_; }
+
+ private:
+ void DidReadFile(bool success, const std::string& data) {
+ succeeded_ = success;
+ data_ = data;
+ base::MessageLoop::current()->Quit();
+ }
+
+ bool succeeded_;
+ std::string data_;
+};
+
+void RunBasicTest(const char* filename) {
+ base::FilePath path;
+ PathService::Get(DIR_TEST_DATA, &path);
+ std::string extension_id = id_util::GenerateId("test");
+ ExtensionResource resource(
+ extension_id, path, base::FilePath().AppendASCII(filename));
+ path = path.AppendASCII(filename);
+
+ std::string file_contents;
+ ASSERT_TRUE(file_util::ReadFileToString(path, &file_contents));
+
+ Receiver receiver;
+
+ scoped_refptr<FileReader> file_reader(
+ new FileReader(resource, receiver.NewCallback()));
+ file_reader->Start();
+
+ base::MessageLoop::current()->Run();
+
+ EXPECT_TRUE(receiver.succeeded());
+ EXPECT_EQ(file_contents, receiver.data());
+}
+
+TEST_F(FileReaderTest, SmallFile) {
+ RunBasicTest("smallfile");
+}
+
+TEST_F(FileReaderTest, BiggerFile) {
+ RunBasicTest("bigfile");
+}
+
+TEST_F(FileReaderTest, NonExistantFile) {
+ base::FilePath path;
+ PathService::Get(DIR_TEST_DATA, &path);
+ std::string extension_id = id_util::GenerateId("test");
+ ExtensionResource resource(extension_id, path, base::FilePath(
+ FILE_PATH_LITERAL("file_that_does_not_exist")));
+ path = path.AppendASCII("file_that_does_not_exist");
+
+ Receiver receiver;
+
+ scoped_refptr<FileReader> file_reader(
+ new FileReader(resource, receiver.NewCallback()));
+ file_reader->Start();
+
+ base::MessageLoop::current()->Run();
+
+ EXPECT_FALSE(receiver.succeeded());
+}
+
+} // namespace extensions
diff --git a/chromium/extensions/browser/pref_names.cc b/chromium/extensions/browser/pref_names.cc
new file mode 100644
index 00000000000..56de3397d9b
--- /dev/null
+++ b/chromium/extensions/browser/pref_names.cc
@@ -0,0 +1,37 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/pref_names.h"
+
+#include "base/logging.h"
+
+namespace extensions {
+namespace pref_names {
+
+bool ScopeToPrefName(ExtensionPrefsScope scope, std::string* result) {
+ switch (scope) {
+ case kExtensionPrefsScopeRegular:
+ *result = kPrefPreferences;
+ return true;
+ case kExtensionPrefsScopeRegularOnly:
+ *result = kPrefRegularOnlyPreferences;
+ return true;
+ case kExtensionPrefsScopeIncognitoPersistent:
+ *result = kPrefIncognitoPreferences;
+ return true;
+ case kExtensionPrefsScopeIncognitoSessionOnly:
+ return false;
+ }
+ NOTREACHED();
+ return false;
+}
+
+const char kPrefPreferences[] = "preferences";
+const char kPrefIncognitoPreferences[] = "incognito_preferences";
+const char kPrefRegularOnlyPreferences[] = "regular_only_preferences";
+const char kPrefContentSettings[] = "content_settings";
+const char kPrefIncognitoContentSettings[] = "incognito_content_settings";
+
+} // namespace pref_names
+} // namespace extensions
diff --git a/chromium/extensions/browser/pref_names.h b/chromium/extensions/browser/pref_names.h
new file mode 100644
index 00000000000..f70b525b15c
--- /dev/null
+++ b/chromium/extensions/browser/pref_names.h
@@ -0,0 +1,42 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_BROWSER_PREF_NAMES_H_
+#define EXTENSIONS_BROWSER_PREF_NAMES_H_
+
+#include <string>
+
+#include "extensions/browser/extension_prefs_scope.h"
+
+namespace extensions {
+
+// Preference keys which are needed by both the ExtensionPrefs and by external
+// clients, such as APIs.
+namespace pref_names {
+
+// If the given |scope| is persisted, return true and populate |result| with the
+// appropriate pref name. If |scope| is not persisted, return false, and leave
+// |result| unchanged.
+bool ScopeToPrefName(ExtensionPrefsScope scope, std::string* result);
+
+// A preference that contains any extension-controlled preferences.
+extern const char kPrefPreferences[];
+
+// A preference that contains any extension-controlled incognito preferences.
+extern const char kPrefIncognitoPreferences[];
+
+// A preference that contains any extension-controlled regular-only preferences.
+extern const char kPrefRegularOnlyPreferences[];
+
+// A preference that contains extension-set content settings.
+extern const char kPrefContentSettings[];
+
+// A preference that contains extension-set content settings.
+extern const char kPrefIncognitoContentSettings[];
+
+} // namespace pref_names
+
+} // namespace extensions
+
+#endif // EXTENSIONS_BROWSER_PREF_NAMES_H_
diff --git a/chromium/extensions/browser/view_type_utils.cc b/chromium/extensions/browser/view_type_utils.cc
new file mode 100644
index 00000000000..a28381f2db0
--- /dev/null
+++ b/chromium/extensions/browser/view_type_utils.cc
@@ -0,0 +1,44 @@
+// 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.
+
+#include "extensions/browser/view_type_utils.h"
+
+#include "base/lazy_instance.h"
+#include "content/public/browser/web_contents.h"
+
+using content::WebContents;
+
+namespace extensions {
+
+namespace {
+
+const char kViewTypeUserDataKey[] = "ViewTypeUserData";
+
+class ViewTypeUserData : public base::SupportsUserData::Data {
+ public:
+ explicit ViewTypeUserData(ViewType type) : type_(type) {}
+ virtual ~ViewTypeUserData() {}
+ ViewType type() { return type_; }
+
+ private:
+ ViewType type_;
+};
+
+} // namespace
+
+ViewType GetViewType(WebContents* tab) {
+ if (!tab)
+ return VIEW_TYPE_INVALID;
+
+ ViewTypeUserData* user_data = static_cast<ViewTypeUserData*>(
+ tab->GetUserData(&kViewTypeUserDataKey));
+
+ return user_data ? user_data->type() : VIEW_TYPE_INVALID;
+}
+
+void SetViewType(WebContents* tab, ViewType type) {
+ tab->SetUserData(&kViewTypeUserDataKey, new ViewTypeUserData(type));
+}
+
+} // namespace chrome
diff --git a/chromium/extensions/browser/view_type_utils.h b/chromium/extensions/browser/view_type_utils.h
new file mode 100644
index 00000000000..4667be438f2
--- /dev/null
+++ b/chromium/extensions/browser/view_type_utils.h
@@ -0,0 +1,24 @@
+// 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.
+
+#ifndef EXTENSIONS_BROWSER_VIEW_TYPE_UTILS_H_
+#define EXTENSIONS_BROWSER_VIEW_TYPE_UTILS_H_
+
+#include "extensions/common/view_type.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace extensions {
+
+// Get/Set the type of a WebContents.
+// GetViewType handles a NULL |tab| for convenience by returning
+// VIEW_TYPE_INVALID.
+ViewType GetViewType(content::WebContents* tab);
+void SetViewType(content::WebContents* tab, ViewType type);
+
+} // namespace chrome
+
+#endif // EXTENSIONS_BROWSER_VIEW_TYPE_UTILS_H_
diff --git a/chromium/extensions/common/constants.cc b/chromium/extensions/common/constants.cc
new file mode 100644
index 00000000000..b73906121fc
--- /dev/null
+++ b/chromium/extensions/common/constants.cc
@@ -0,0 +1,40 @@
+// 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.
+
+#include "extensions/common/constants.h"
+
+#include "base/files/file_path.h"
+
+namespace extensions {
+
+const char kExtensionScheme[] = "chrome-extension";
+
+const base::FilePath::CharType kManifestFilename[] =
+ FILE_PATH_LITERAL("manifest.json");
+const base::FilePath::CharType kLocaleFolder[] =
+ FILE_PATH_LITERAL("_locales");
+const base::FilePath::CharType kMessagesFilename[] =
+ FILE_PATH_LITERAL("messages.json");
+const base::FilePath::CharType kPlatformSpecificFolder[] =
+ FILE_PATH_LITERAL("_platform_specific");
+
+const char kInstallDirectoryName[] = "Extensions";
+
+const char kTempExtensionName[] = "CRX_INSTALL";
+
+const char kDecodedImagesFilename[] = "DECODED_IMAGES";
+
+const char kDecodedMessageCatalogsFilename[] = "DECODED_MESSAGE_CATALOGS";
+
+const char kGeneratedBackgroundPageFilename[] =
+ "_generated_background_page.html";
+
+const char kModulesDir[] = "_modules";
+
+const base::FilePath::CharType kExtensionFileExtension[] =
+ FILE_PATH_LITERAL(".crx");
+const base::FilePath::CharType kExtensionKeyFileExtension[] =
+ FILE_PATH_LITERAL(".pem");
+
+} // namespace extensions
diff --git a/chromium/extensions/common/constants.h b/chromium/extensions/common/constants.h
new file mode 100644
index 00000000000..492ddbc27b4
--- /dev/null
+++ b/chromium/extensions/common/constants.h
@@ -0,0 +1,57 @@
+// 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.
+
+#ifndef EXTENSIONS_COMMON_CONSTANTS_H_
+#define EXTENSIONS_COMMON_CONSTANTS_H_
+
+#include "base/files/file_path.h"
+
+namespace extensions {
+
+// Scheme we serve extension content from.
+extern const char kExtensionScheme[];
+
+ // The name of the manifest inside an extension.
+extern const base::FilePath::CharType kManifestFilename[];
+
+ // The name of locale folder inside an extension.
+extern const base::FilePath::CharType kLocaleFolder[];
+
+ // The name of the messages file inside an extension.
+extern const base::FilePath::CharType kMessagesFilename[];
+
+// The base directory for subdirectories with platform-specific code.
+extern const base::FilePath::CharType kPlatformSpecificFolder[];
+
+// The name of the directory inside the profile where extensions are
+// installed to.
+extern const char kInstallDirectoryName[];
+
+// The name of a temporary directory to install an extension into for
+// validation before finalizing install.
+extern const char kTempExtensionName[];
+
+// The file to write our decoded images to, relative to the extension_path.
+extern const char kDecodedImagesFilename[];
+
+// The file to write our decoded message catalogs to, relative to the
+// extension_path.
+extern const char kDecodedMessageCatalogsFilename[];
+
+// The filename to use for a background page generated from
+// background.scripts.
+extern const char kGeneratedBackgroundPageFilename[];
+
+// Path to imported modules.
+extern const char kModulesDir[];
+
+// The file extension (.crx) for extensions.
+extern const base::FilePath::CharType kExtensionFileExtension[];
+
+// The file extension (.pem) for private key files.
+extern const base::FilePath::CharType kExtensionKeyFileExtension[];
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_CONSTANTS_H_
diff --git a/chromium/extensions/common/crx_file.cc b/chromium/extensions/common/crx_file.cc
new file mode 100644
index 00000000000..73e7f7b73d6
--- /dev/null
+++ b/chromium/extensions/common/crx_file.cc
@@ -0,0 +1,81 @@
+// 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.
+
+#include "extensions/common/crx_file.h"
+
+namespace extensions {
+
+namespace {
+
+// The current version of the crx format.
+static const uint32 kCurrentVersion = 2;
+
+// The current version of the crx diff format.
+static const uint32 kCurrentDiffVersion = 0;
+
+// The maximum size the crx parser will tolerate for a public key.
+static const uint32 kMaxPublicKeySize = 1 << 16;
+
+// The maximum size the crx parser will tolerate for a signature.
+static const uint32 kMaxSignatureSize = 1 << 16;
+
+} // namespace
+
+// The magic string embedded in the header.
+const char kCrxFileHeaderMagic[] = "Cr24";
+const char kCrxDiffFileHeaderMagic[] = "CrOD";
+
+scoped_ptr<CrxFile> CrxFile::Parse(const CrxFile::Header& header,
+ CrxFile::Error* error) {
+ if (HeaderIsValid(header, error))
+ return scoped_ptr<CrxFile>(new CrxFile(header));
+ return scoped_ptr<CrxFile>();
+}
+
+scoped_ptr<CrxFile> CrxFile::Create(const uint32 key_size,
+ const uint32 signature_size,
+ CrxFile::Error* error) {
+ CrxFile::Header header;
+ memcpy(&header.magic, kCrxFileHeaderMagic, kCrxFileHeaderMagicSize);
+ header.version = kCurrentVersion;
+ header.key_size = key_size;
+ header.signature_size = signature_size;
+ if (HeaderIsValid(header, error))
+ return scoped_ptr<CrxFile>(new CrxFile(header));
+ return scoped_ptr<CrxFile>();
+}
+
+CrxFile::CrxFile(const Header& header) : header_(header) {
+}
+
+bool CrxFile::HeaderIsDelta(const CrxFile::Header& header) {
+ return !strncmp(kCrxDiffFileHeaderMagic, header.magic, sizeof(header.magic));
+}
+
+bool CrxFile::HeaderIsValid(const CrxFile::Header& header,
+ CrxFile::Error* error) {
+ bool valid = false;
+ bool diffCrx = false;
+ if (!strncmp(kCrxDiffFileHeaderMagic, header.magic, sizeof(header.magic)))
+ diffCrx = true;
+ if (strncmp(kCrxFileHeaderMagic, header.magic, sizeof(header.magic)) &&
+ !diffCrx)
+ *error = kWrongMagic;
+ else if (header.version != kCurrentVersion
+ && !(diffCrx && header.version == kCurrentDiffVersion))
+ *error = kInvalidVersion;
+ else if (header.key_size > kMaxPublicKeySize)
+ *error = kInvalidKeyTooLarge;
+ else if (header.key_size == 0)
+ *error = kInvalidKeyTooSmall;
+ else if (header.signature_size > kMaxSignatureSize)
+ *error = kInvalidSignatureTooLarge;
+ else if (header.signature_size == 0)
+ *error = kInvalidSignatureTooSmall;
+ else
+ valid = true;
+ return valid;
+}
+
+} // namespace extensions
diff --git a/chromium/extensions/common/crx_file.h b/chromium/extensions/common/crx_file.h
new file mode 100644
index 00000000000..3ac81897925
--- /dev/null
+++ b/chromium/extensions/common/crx_file.h
@@ -0,0 +1,80 @@
+// 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.
+
+#ifndef EXTENSIONS_COMMON_CRX_FILE_H_
+#define EXTENSIONS_COMMON_CRX_FILE_H_
+
+#include <sys/types.h>
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace extensions {
+
+// CRX files have a header that includes a magic key, version number, and
+// some signature sizing information. Use CrxFile object to validate whether
+// the header is valid or not.
+class CrxFile {
+ public:
+
+ // The size of the magic character sequence at the beginning of each crx
+ // file, in bytes. This should be a multiple of 4.
+ static const size_t kCrxFileHeaderMagicSize = 4;
+
+ // This header is the first data at the beginning of an extension. Its
+ // contents are purposely 32-bit aligned so that it can just be slurped into
+ // a struct without manual parsing.
+ struct Header {
+ char magic[kCrxFileHeaderMagicSize];
+ uint32 version;
+ uint32 key_size; // The size of the public key, in bytes.
+ uint32 signature_size; // The size of the signature, in bytes.
+ // An ASN.1-encoded PublicKeyInfo structure follows.
+ // The signature follows.
+ };
+
+ enum Error {
+ kWrongMagic,
+ kInvalidVersion,
+ kInvalidKeyTooLarge,
+ kInvalidKeyTooSmall,
+ kInvalidSignatureTooLarge,
+ kInvalidSignatureTooSmall,
+ };
+
+ // Construct a new CRX file header object with bytes of a header
+ // read from a CRX file. If a null scoped_ptr is returned, |error|
+ // contains an error code with additional information.
+ static scoped_ptr<CrxFile> Parse(const Header& header, Error* error);
+
+ // Construct a new header for the given key and signature sizes.
+ // Returns a null scoped_ptr if erroneous values of |key_size| and/or
+ // |signature_size| are provided. |error| contains an error code with
+ // additional information.
+ // Use this constructor and then .header() to obtain the Header
+ // for writing out to a CRX file.
+ static scoped_ptr<CrxFile> Create(const uint32 key_size,
+ const uint32 signature_size,
+ Error* error);
+
+ // Returns the header structure for writing out to a CRX file.
+ const Header& header() const { return header_; }
+
+ // Checks a valid |header| to determine whether or not the CRX represents a
+ // differential CRX.
+ static bool HeaderIsDelta(const Header& header);
+
+ private:
+ Header header_;
+
+ // Constructor is private. Clients should use static factory methods above.
+ explicit CrxFile(const Header& header);
+
+ // Checks the |header| for validity and returns true if the values are valid.
+ // If false is returned, more detailed error code is returned in |error|.
+ static bool HeaderIsValid(const Header& header, Error* error);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_CRX_FILE_H_
diff --git a/chromium/extensions/common/draggable_region.cc b/chromium/extensions/common/draggable_region.cc
new file mode 100644
index 00000000000..baee6eecf5c
--- /dev/null
+++ b/chromium/extensions/common/draggable_region.cc
@@ -0,0 +1,13 @@
+// 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.
+
+#include "extensions/common/draggable_region.h"
+
+namespace extensions {
+
+DraggableRegion::DraggableRegion()
+ : draggable(false) {
+}
+
+} // namespace extensions
diff --git a/chromium/extensions/common/draggable_region.h b/chromium/extensions/common/draggable_region.h
new file mode 100644
index 00000000000..8b22aa332c5
--- /dev/null
+++ b/chromium/extensions/common/draggable_region.h
@@ -0,0 +1,21 @@
+// 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.
+
+#ifndef EXTENSIONS_COMMON_DRAGGABLE_REGION_H_
+#define EXTENSIONS_COMMON_DRAGGABLE_REGION_H_
+
+#include "ui/gfx/rect.h"
+
+namespace extensions {
+
+struct DraggableRegion {
+ bool draggable;
+ gfx::Rect bounds;
+
+ DraggableRegion();
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_DRAGGABLE_REGION_H_
diff --git a/chromium/extensions/common/error_utils.cc b/chromium/extensions/common/error_utils.cc
new file mode 100644
index 00000000000..bd4d32e0d2a
--- /dev/null
+++ b/chromium/extensions/common/error_utils.cc
@@ -0,0 +1,66 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/common/error_utils.h"
+
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+
+namespace extensions {
+
+std::string ErrorUtils::FormatErrorMessage(const std::string& format,
+ const std::string& s1) {
+ std::string ret_val = format;
+ ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1);
+ return ret_val;
+}
+
+std::string ErrorUtils::FormatErrorMessage(const std::string& format,
+ const std::string& s1,
+ const std::string& s2) {
+ std::string ret_val = format;
+ ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1);
+ ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s2);
+ return ret_val;
+}
+
+std::string ErrorUtils::FormatErrorMessage(const std::string& format,
+ const std::string& s1,
+ const std::string& s2,
+ const std::string& s3) {
+ std::string ret_val = format;
+ ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1);
+ ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s2);
+ ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s3);
+ return ret_val;
+}
+
+string16 ErrorUtils::FormatErrorMessageUTF16(const std::string& format,
+ const std::string& s1) {
+ std::string ret_val = format;
+ ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1);
+ return UTF8ToUTF16(ret_val);
+}
+
+string16 ErrorUtils::FormatErrorMessageUTF16(const std::string& format,
+ const std::string& s1,
+ const std::string& s2) {
+ std::string ret_val = format;
+ ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1);
+ ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s2);
+ return UTF8ToUTF16(ret_val);
+}
+
+string16 ErrorUtils::FormatErrorMessageUTF16(const std::string& format,
+ const std::string& s1,
+ const std::string& s2,
+ const std::string& s3) {
+ std::string ret_val = format;
+ ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1);
+ ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s2);
+ ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s3);
+ return UTF8ToUTF16(ret_val);
+}
+
+} // namespace
diff --git a/chromium/extensions/common/error_utils.h b/chromium/extensions/common/error_utils.h
new file mode 100644
index 00000000000..c4d012ae2c0
--- /dev/null
+++ b/chromium/extensions/common/error_utils.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_COMMON_ERROR_UTILS_H_
+#define EXTENSIONS_COMMON_ERROR_UTILS_H_
+
+#include <string>
+
+#include "base/strings/string16.h"
+
+namespace extensions {
+
+class ErrorUtils {
+ public:
+ // Creates an error messages from a pattern.
+ static std::string FormatErrorMessage(const std::string& format,
+ const std::string& s1);
+
+ static std::string FormatErrorMessage(const std::string& format,
+ const std::string& s1,
+ const std::string& s2);
+
+ static std::string FormatErrorMessage(const std::string& format,
+ const std::string& s1,
+ const std::string& s2,
+ const std::string& s3);
+
+ static string16 FormatErrorMessageUTF16(const std::string& format,
+ const std::string& s1);
+
+ static string16 FormatErrorMessageUTF16(const std::string& format,
+ const std::string& s1,
+ const std::string& s2);
+
+ static string16 FormatErrorMessageUTF16(const std::string& format,
+ const std::string& s1,
+ const std::string& s2,
+ const std::string& s3);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_ERROR_UTILS_H_
diff --git a/chromium/extensions/common/event_filter.cc b/chromium/extensions/common/event_filter.cc
new file mode 100644
index 00000000000..70915b976d0
--- /dev/null
+++ b/chromium/extensions/common/event_filter.cc
@@ -0,0 +1,181 @@
+// 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.
+
+#include "extensions/common/event_filter.h"
+
+#include "extensions/common/matcher/url_matcher_factory.h"
+#include "ipc/ipc_message.h"
+
+namespace extensions {
+
+EventFilter::EventMatcherEntry::EventMatcherEntry(
+ scoped_ptr<EventMatcher> event_matcher,
+ URLMatcher* url_matcher,
+ const URLMatcherConditionSet::Vector& condition_sets)
+ : event_matcher_(event_matcher.Pass()),
+ url_matcher_(url_matcher) {
+ for (URLMatcherConditionSet::Vector::const_iterator it =
+ condition_sets.begin(); it != condition_sets.end(); it++)
+ condition_set_ids_.push_back((*it)->id());
+ url_matcher_->AddConditionSets(condition_sets);
+}
+
+EventFilter::EventMatcherEntry::~EventMatcherEntry() {
+ url_matcher_->RemoveConditionSets(condition_set_ids_);
+}
+
+void EventFilter::EventMatcherEntry::DontRemoveConditionSetsInDestructor() {
+ condition_set_ids_.clear();
+}
+
+EventFilter::EventFilter()
+ : next_id_(0),
+ next_condition_set_id_(0) {
+}
+
+EventFilter::~EventFilter() {
+ // Normally when an event matcher entry is removed from event_matchers_ it
+ // will remove its condition sets from url_matcher_, but as url_matcher_ is
+ // being destroyed anyway there is no need to do that step here.
+ for (EventMatcherMultiMap::iterator it = event_matchers_.begin();
+ it != event_matchers_.end(); it++) {
+ for (EventMatcherMap::iterator it2 = it->second.begin();
+ it2 != it->second.end(); it2++) {
+ it2->second->DontRemoveConditionSetsInDestructor();
+ }
+ }
+}
+
+EventFilter::MatcherID
+EventFilter::AddEventMatcher(const std::string& event_name,
+ scoped_ptr<EventMatcher> matcher) {
+ MatcherID id = next_id_++;
+ URLMatcherConditionSet::Vector condition_sets;
+ if (!CreateConditionSets(id, matcher.get(), &condition_sets))
+ return -1;
+
+ for (URLMatcherConditionSet::Vector::iterator it = condition_sets.begin();
+ it != condition_sets.end(); it++) {
+ condition_set_id_to_event_matcher_id_.insert(
+ std::make_pair((*it)->id(), id));
+ }
+ id_to_event_name_[id] = event_name;
+ event_matchers_[event_name][id] = linked_ptr<EventMatcherEntry>(
+ new EventMatcherEntry(matcher.Pass(), &url_matcher_, condition_sets));
+ return id;
+}
+
+EventMatcher* EventFilter::GetEventMatcher(MatcherID id) {
+ DCHECK(id_to_event_name_.find(id) != id_to_event_name_.end());
+ const std::string& event_name = id_to_event_name_[id];
+ return event_matchers_[event_name][id]->event_matcher();
+}
+
+const std::string& EventFilter::GetEventName(MatcherID id) {
+ DCHECK(id_to_event_name_.find(id) != id_to_event_name_.end());
+ return id_to_event_name_[id];
+}
+
+bool EventFilter::CreateConditionSets(
+ MatcherID id,
+ EventMatcher* matcher,
+ URLMatcherConditionSet::Vector* condition_sets) {
+ if (matcher->GetURLFilterCount() == 0) {
+ // If there are no URL filters then we want to match all events, so create a
+ // URLFilter from an empty dictionary.
+ base::DictionaryValue empty_dict;
+ return AddDictionaryAsConditionSet(&empty_dict, condition_sets);
+ }
+ for (int i = 0; i < matcher->GetURLFilterCount(); i++) {
+ base::DictionaryValue* url_filter;
+ if (!matcher->GetURLFilter(i, &url_filter))
+ return false;
+ if (!AddDictionaryAsConditionSet(url_filter, condition_sets))
+ return false;
+ }
+ return true;
+}
+
+bool EventFilter::AddDictionaryAsConditionSet(
+ base::DictionaryValue* url_filter,
+ URLMatcherConditionSet::Vector* condition_sets) {
+ std::string error;
+ URLMatcherConditionSet::ID condition_set_id = next_condition_set_id_++;
+ condition_sets->push_back(URLMatcherFactory::CreateFromURLFilterDictionary(
+ url_matcher_.condition_factory(),
+ url_filter,
+ condition_set_id,
+ &error));
+ if (!error.empty()) {
+ LOG(ERROR) << "CreateFromURLFilterDictionary failed: " << error;
+ url_matcher_.ClearUnusedConditionSets();
+ condition_sets->clear();
+ return false;
+ }
+ return true;
+}
+
+std::string EventFilter::RemoveEventMatcher(MatcherID id) {
+ std::map<MatcherID, std::string>::iterator it = id_to_event_name_.find(id);
+ std::string event_name = it->second;
+ // EventMatcherEntry's destructor causes the condition set ids to be removed
+ // from url_matcher_.
+ event_matchers_[event_name].erase(id);
+ id_to_event_name_.erase(it);
+ return event_name;
+}
+
+std::set<EventFilter::MatcherID> EventFilter::MatchEvent(
+ const std::string& event_name, const EventFilteringInfo& event_info,
+ int routing_id) {
+ std::set<MatcherID> matchers;
+
+ EventMatcherMultiMap::iterator it = event_matchers_.find(event_name);
+ if (it == event_matchers_.end())
+ return matchers;
+
+ EventMatcherMap& matcher_map = it->second;
+ GURL url_to_match_against = event_info.has_url() ? event_info.url() : GURL();
+ std::set<URLMatcherConditionSet::ID> matching_condition_set_ids =
+ url_matcher_.MatchURL(url_to_match_against);
+ for (std::set<URLMatcherConditionSet::ID>::iterator it =
+ matching_condition_set_ids.begin();
+ it != matching_condition_set_ids.end(); it++) {
+ std::map<URLMatcherConditionSet::ID, MatcherID>::iterator matcher_id =
+ condition_set_id_to_event_matcher_id_.find(*it);
+ if (matcher_id == condition_set_id_to_event_matcher_id_.end()) {
+ NOTREACHED() << "id not found in condition set map (" << (*it) << ")";
+ continue;
+ }
+ MatcherID id = matcher_id->second;
+ EventMatcherMap::iterator matcher_entry = matcher_map.find(id);
+ if (matcher_entry == matcher_map.end()) {
+ // Matcher must be for a different event.
+ continue;
+ }
+ const EventMatcher* event_matcher = matcher_entry->second->event_matcher();
+ // The context that installed the event listener should be the same context
+ // as the one where the event listener is called.
+ if ((routing_id != MSG_ROUTING_NONE) &&
+ (event_matcher->GetRoutingID() != routing_id)) {
+ continue;
+ }
+ if (event_matcher->MatchNonURLCriteria(event_info)) {
+ CHECK(!event_matcher->HasURLFilters() || event_info.has_url());
+ matchers.insert(id);
+ }
+ }
+
+ return matchers;
+}
+
+int EventFilter::GetMatcherCountForEvent(const std::string& name) {
+ EventMatcherMultiMap::const_iterator it = event_matchers_.find(name);
+ if (it == event_matchers_.end())
+ return 0;
+
+ return it->second.size();
+}
+
+} // namespace extensions
diff --git a/chromium/extensions/common/event_filter.h b/chromium/extensions/common/event_filter.h
new file mode 100644
index 00000000000..6ef136cb9dc
--- /dev/null
+++ b/chromium/extensions/common/event_filter.h
@@ -0,0 +1,127 @@
+// 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.
+
+#ifndef EXTENSIONS_COMMON_EVENT_FILTER_H_
+#define EXTENSIONS_COMMON_EVENT_FILTER_H_
+
+#include <map>
+#include <set>
+
+#include "base/memory/linked_ptr.h"
+#include "extensions/common/event_filtering_info.h"
+#include "extensions/common/event_matcher.h"
+#include "extensions/common/matcher/url_matcher.h"
+
+namespace extensions {
+
+// Matches incoming events against a collection of EventMatchers. Each added
+// EventMatcher is given an id which is returned by MatchEvent() when it is
+// passed a matching event.
+class EventFilter {
+ public:
+ typedef int MatcherID;
+ EventFilter();
+ ~EventFilter();
+
+ // Adds an event matcher that will be used in calls to MatchEvent(). Returns
+ // the id of the matcher, or -1 if there was an error.
+ MatcherID AddEventMatcher(const std::string& event_name,
+ scoped_ptr<EventMatcher> matcher);
+
+ // Retrieve the EventMatcher with the given id.
+ EventMatcher* GetEventMatcher(MatcherID id);
+
+ // Retrieve the name of the event that the EventMatcher specified by |id| is
+ // referring to.
+ const std::string& GetEventName(MatcherID id);
+
+ // Removes an event matcher, returning the name of the event that it was for.
+ std::string RemoveEventMatcher(MatcherID id);
+
+ // Match an event named |event_name| with filtering info |event_info| against
+ // our set of event matchers. Returns a set of ids that correspond to the
+ // event matchers that matched the event.
+ // TODO(koz): Add a std::string* parameter for retrieving error messages.
+ std::set<MatcherID> MatchEvent(const std::string& event_name,
+ const EventFilteringInfo& event_info,
+ int routing_id);
+
+ int GetMatcherCountForEvent(const std::string& event_name);
+
+ // For testing.
+ bool IsURLMatcherEmpty() const {
+ return url_matcher_.IsEmpty();
+ }
+
+ private:
+ class EventMatcherEntry {
+ public:
+ // Adds |condition_sets| to |url_matcher| on construction and removes them
+ // again on destruction. |condition_sets| should be the
+ // URLMatcherConditionSets that match the URL constraints specified by
+ // |event_matcher|.
+ EventMatcherEntry(scoped_ptr<EventMatcher> event_matcher,
+ URLMatcher* url_matcher,
+ const URLMatcherConditionSet::Vector& condition_sets);
+ ~EventMatcherEntry();
+
+ // Prevents the removal of condition sets when this class is destroyed. We
+ // call this in EventFilter's destructor so that we don't do the costly
+ // removal of condition sets when the URLMatcher is going to be destroyed
+ // and clean them up anyway.
+ void DontRemoveConditionSetsInDestructor();
+
+ EventMatcher* event_matcher() {
+ return event_matcher_.get();
+ }
+
+ private:
+ scoped_ptr<EventMatcher> event_matcher_;
+ // The id sets in url_matcher_ that this EventMatcher owns.
+ std::vector<URLMatcherConditionSet::ID> condition_set_ids_;
+ URLMatcher* url_matcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(EventMatcherEntry);
+ };
+
+ // Maps from a matcher id to an event matcher entry.
+ typedef std::map<MatcherID, linked_ptr<EventMatcherEntry> > EventMatcherMap;
+
+ // Maps from event name to the map of matchers that are registered for it.
+ typedef std::map<std::string, EventMatcherMap> EventMatcherMultiMap;
+
+ // Adds the list of URL filters in |matcher| to the URL matcher, having
+ // matches for those URLs map to |id|.
+ bool CreateConditionSets(MatcherID id,
+ EventMatcher* matcher,
+ URLMatcherConditionSet::Vector* condition_sets);
+
+ bool AddDictionaryAsConditionSet(
+ base::DictionaryValue* url_filter,
+ URLMatcherConditionSet::Vector* condition_sets);
+
+ URLMatcher url_matcher_;
+ EventMatcherMultiMap event_matchers_;
+
+ // The next id to assign to an EventMatcher.
+ MatcherID next_id_;
+
+ // The next id to assign to a condition set passed to URLMatcher.
+ URLMatcherConditionSet::ID next_condition_set_id_;
+
+ // Maps condition set ids, which URLMatcher operates in, to event matcher
+ // ids, which the interface to this class operates in. As each EventFilter
+ // can specify many condition sets this is a many to one relationship.
+ std::map<URLMatcherConditionSet::ID, MatcherID>
+ condition_set_id_to_event_matcher_id_;
+
+ // Maps from event matcher ids to the name of the event they match on.
+ std::map<MatcherID, std::string> id_to_event_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(EventFilter);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_EVENT_FILTER_H_
diff --git a/chromium/extensions/common/event_filter_unittest.cc b/chromium/extensions/common/event_filter_unittest.cc
new file mode 100644
index 00000000000..3f7f12b0501
--- /dev/null
+++ b/chromium/extensions/common/event_filter_unittest.cc
@@ -0,0 +1,253 @@
+// 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.
+
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+#include "extensions/common/event_filter.h"
+#include "extensions/common/event_filtering_info.h"
+#include "extensions/common/event_matcher.h"
+#include "ipc/ipc_message.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+
+class EventFilterUnittest : public testing::Test {
+ public:
+ EventFilterUnittest() {
+ google_event_.SetURL(GURL("http://google.com"));
+ yahoo_event_.SetURL(GURL("http://yahoo.com"));
+ random_url_event_.SetURL(GURL("http://www.something-else.com"));
+ empty_url_event_.SetURL(GURL());
+ }
+
+ protected:
+ scoped_ptr<base::Value> HostSuffixDict(const std::string& host_suffix) {
+ scoped_ptr<base::DictionaryValue> dict(new DictionaryValue());
+ dict->Set("hostSuffix", base::Value::CreateStringValue(host_suffix));
+ return scoped_ptr<base::Value>(dict.release());
+ }
+
+ scoped_ptr<base::ListValue> ValueAsList(scoped_ptr<base::Value> value) {
+ scoped_ptr<base::ListValue> result(new base::ListValue());
+ result->Append(value.release());
+ return result.Pass();
+ }
+
+ scoped_ptr<EventMatcher> AllURLs() {
+ return scoped_ptr<EventMatcher>(new EventMatcher(
+ scoped_ptr<DictionaryValue>(new DictionaryValue), MSG_ROUTING_NONE));
+ }
+
+ scoped_ptr<EventMatcher> HostSuffixMatcher(const std::string& host_suffix) {
+ return MatcherFromURLFilterList(ValueAsList(HostSuffixDict(host_suffix)));
+ }
+
+ scoped_ptr<EventMatcher> MatcherFromURLFilterList(
+ scoped_ptr<ListValue> url_filter_list) {
+ scoped_ptr<DictionaryValue> filter_dict(new DictionaryValue);
+ filter_dict->Set("url", url_filter_list.release());
+ return scoped_ptr<EventMatcher>(
+ new EventMatcher(filter_dict.Pass(), MSG_ROUTING_NONE));
+ }
+
+ EventFilter event_filter_;
+ EventFilteringInfo empty_event_;
+ EventFilteringInfo google_event_;
+ EventFilteringInfo yahoo_event_;
+ EventFilteringInfo random_url_event_;
+ EventFilteringInfo empty_url_event_;
+};
+
+TEST_F(EventFilterUnittest, NoMatchersMatchIfEmpty) {
+ std::set<int> matches = event_filter_.MatchEvent("some-event",
+ empty_event_,
+ MSG_ROUTING_NONE);
+ ASSERT_EQ(0u, matches.size());
+}
+
+TEST_F(EventFilterUnittest, AddingEventMatcherDoesntCrash) {
+ event_filter_.AddEventMatcher("event1", AllURLs());
+}
+
+TEST_F(EventFilterUnittest,
+ DontMatchAgainstMatchersForDifferentEvents) {
+ event_filter_.AddEventMatcher("event1", AllURLs());
+ std::set<int> matches = event_filter_.MatchEvent("event2",
+ empty_event_,
+ MSG_ROUTING_NONE);
+ ASSERT_EQ(0u, matches.size());
+}
+
+TEST_F(EventFilterUnittest, DoMatchAgainstMatchersForSameEvent) {
+ int id = event_filter_.AddEventMatcher("event1", AllURLs());
+ std::set<int> matches = event_filter_.MatchEvent("event1",
+ google_event_, MSG_ROUTING_NONE);
+ ASSERT_EQ(1u, matches.size());
+ ASSERT_EQ(1u, matches.count(id));
+}
+
+TEST_F(EventFilterUnittest, DontMatchUnlessMatcherMatches) {
+ EventFilteringInfo info;
+ info.SetURL(GURL("http://www.yahoo.com"));
+ event_filter_.AddEventMatcher("event1", HostSuffixMatcher("google.com"));
+ std::set<int> matches = event_filter_.MatchEvent(
+ "event1", info, MSG_ROUTING_NONE);
+ ASSERT_TRUE(matches.empty());
+}
+
+TEST_F(EventFilterUnittest, RemovingAnEventMatcherStopsItMatching) {
+ int id = event_filter_.AddEventMatcher("event1", AllURLs());
+ event_filter_.RemoveEventMatcher(id);
+ std::set<int> matches = event_filter_.MatchEvent("event1",
+ empty_event_,
+ MSG_ROUTING_NONE);
+ ASSERT_TRUE(matches.empty());
+}
+
+TEST_F(EventFilterUnittest, MultipleEventMatches) {
+ int id1 = event_filter_.AddEventMatcher("event1", AllURLs());
+ int id2 = event_filter_.AddEventMatcher("event1", AllURLs());
+ std::set<int> matches = event_filter_.MatchEvent("event1",
+ google_event_, MSG_ROUTING_NONE);
+ ASSERT_EQ(2u, matches.size());
+ ASSERT_EQ(1u, matches.count(id1));
+ ASSERT_EQ(1u, matches.count(id2));
+}
+
+TEST_F(EventFilterUnittest, TestURLMatching) {
+ EventFilteringInfo info;
+ info.SetURL(GURL("http://www.google.com"));
+ int id = event_filter_.AddEventMatcher("event1",
+ HostSuffixMatcher("google.com"));
+ std::set<int> matches = event_filter_.MatchEvent(
+ "event1", info, MSG_ROUTING_NONE);
+ ASSERT_EQ(1u, matches.size());
+ ASSERT_EQ(1u, matches.count(id));
+}
+
+TEST_F(EventFilterUnittest, TestMultipleURLFiltersMatchOnAny) {
+ scoped_ptr<base::ListValue> filters(new base::ListValue());
+ filters->Append(HostSuffixDict("google.com").release());
+ filters->Append(HostSuffixDict("yahoo.com").release());
+
+ scoped_ptr<EventMatcher> matcher(MatcherFromURLFilterList(filters.Pass()));
+ int id = event_filter_.AddEventMatcher("event1", matcher.Pass());
+
+ {
+ std::set<int> matches = event_filter_.MatchEvent("event1",
+ google_event_, MSG_ROUTING_NONE);
+ ASSERT_EQ(1u, matches.size());
+ ASSERT_EQ(1u, matches.count(id));
+ }
+ {
+ std::set<int> matches = event_filter_.MatchEvent("event1",
+ yahoo_event_, MSG_ROUTING_NONE);
+ ASSERT_EQ(1u, matches.size());
+ ASSERT_EQ(1u, matches.count(id));
+ }
+ {
+ std::set<int> matches = event_filter_.MatchEvent("event1",
+ random_url_event_, MSG_ROUTING_NONE);
+ ASSERT_EQ(0u, matches.size());
+ }
+}
+
+TEST_F(EventFilterUnittest, TestStillMatchesAfterRemoval) {
+ int id1 = event_filter_.AddEventMatcher("event1", AllURLs());
+ int id2 = event_filter_.AddEventMatcher("event1", AllURLs());
+
+ event_filter_.RemoveEventMatcher(id1);
+ {
+ std::set<int> matches = event_filter_.MatchEvent("event1",
+ google_event_, MSG_ROUTING_NONE);
+ ASSERT_EQ(1u, matches.size());
+ ASSERT_EQ(1u, matches.count(id2));
+ }
+}
+
+TEST_F(EventFilterUnittest, TestMatchesOnlyAgainstPatternsForCorrectEvent) {
+ int id1 = event_filter_.AddEventMatcher("event1", AllURLs());
+ event_filter_.AddEventMatcher("event2", AllURLs());
+
+ {
+ std::set<int> matches = event_filter_.MatchEvent("event1",
+ google_event_, MSG_ROUTING_NONE);
+ ASSERT_EQ(1u, matches.size());
+ ASSERT_EQ(1u, matches.count(id1));
+ }
+}
+
+TEST_F(EventFilterUnittest, TestGetMatcherCountForEvent) {
+ ASSERT_EQ(0, event_filter_.GetMatcherCountForEvent("event1"));
+ int id1 = event_filter_.AddEventMatcher("event1", AllURLs());
+ ASSERT_EQ(1, event_filter_.GetMatcherCountForEvent("event1"));
+ int id2 = event_filter_.AddEventMatcher("event1", AllURLs());
+ ASSERT_EQ(2, event_filter_.GetMatcherCountForEvent("event1"));
+ event_filter_.RemoveEventMatcher(id1);
+ ASSERT_EQ(1, event_filter_.GetMatcherCountForEvent("event1"));
+ event_filter_.RemoveEventMatcher(id2);
+ ASSERT_EQ(0, event_filter_.GetMatcherCountForEvent("event1"));
+}
+
+TEST_F(EventFilterUnittest, RemoveEventMatcherReturnsEventName) {
+ int id1 = event_filter_.AddEventMatcher("event1", AllURLs());
+ int id2 = event_filter_.AddEventMatcher("event1", AllURLs());
+ int id3 = event_filter_.AddEventMatcher("event2", AllURLs());
+
+ ASSERT_EQ("event1", event_filter_.RemoveEventMatcher(id1));
+ ASSERT_EQ("event1", event_filter_.RemoveEventMatcher(id2));
+ ASSERT_EQ("event2", event_filter_.RemoveEventMatcher(id3));
+}
+
+TEST_F(EventFilterUnittest, InvalidURLFilterCantBeAdded) {
+ scoped_ptr<base::ListValue> filter_list(new base::ListValue());
+ filter_list->Append(new base::ListValue()); // Should be a dict.
+ scoped_ptr<EventMatcher> matcher(MatcherFromURLFilterList(
+ filter_list.Pass()));
+ int id1 = event_filter_.AddEventMatcher("event1", matcher.Pass());
+ EXPECT_TRUE(event_filter_.IsURLMatcherEmpty());
+ ASSERT_EQ(-1, id1);
+}
+
+TEST_F(EventFilterUnittest, EmptyListOfURLFiltersMatchesAllURLs) {
+ scoped_ptr<base::ListValue> filter_list(new base::ListValue());
+ scoped_ptr<EventMatcher> matcher(MatcherFromURLFilterList(
+ scoped_ptr<ListValue>(new ListValue)));
+ int id = event_filter_.AddEventMatcher("event1", matcher.Pass());
+ std::set<int> matches = event_filter_.MatchEvent("event1",
+ google_event_, MSG_ROUTING_NONE);
+ ASSERT_EQ(1u, matches.size());
+ ASSERT_EQ(1u, matches.count(id));
+}
+
+TEST_F(EventFilterUnittest,
+ InternalURLMatcherShouldBeEmptyWhenThereAreNoEventMatchers) {
+ ASSERT_TRUE(event_filter_.IsURLMatcherEmpty());
+ int id = event_filter_.AddEventMatcher("event1",
+ HostSuffixMatcher("google.com"));
+ ASSERT_FALSE(event_filter_.IsURLMatcherEmpty());
+ event_filter_.RemoveEventMatcher(id);
+ ASSERT_TRUE(event_filter_.IsURLMatcherEmpty());
+}
+
+TEST_F(EventFilterUnittest, EmptyURLsShouldBeMatchedByEmptyURLFilters) {
+ int id = event_filter_.AddEventMatcher("event1", AllURLs());
+ std::set<int> matches = event_filter_.MatchEvent(
+ "event1", empty_url_event_, MSG_ROUTING_NONE);
+ ASSERT_EQ(1u, matches.size());
+ ASSERT_EQ(1u, matches.count(id));
+}
+
+TEST_F(EventFilterUnittest,
+ EmptyURLsShouldBeMatchedByEmptyURLFiltersWithAnEmptyItem) {
+ scoped_ptr<EventMatcher> matcher(MatcherFromURLFilterList(ValueAsList(
+ scoped_ptr<Value>(new DictionaryValue()))));
+ int id = event_filter_.AddEventMatcher("event1", matcher.Pass());
+ std::set<int> matches = event_filter_.MatchEvent(
+ "event1", empty_url_event_, MSG_ROUTING_NONE);
+ ASSERT_EQ(1u, matches.size());
+ ASSERT_EQ(1u, matches.count(id));
+}
+
+} // namespace extensions
diff --git a/chromium/extensions/common/event_filtering_info.cc b/chromium/extensions/common/event_filtering_info.cc
new file mode 100644
index 00000000000..29566e891be
--- /dev/null
+++ b/chromium/extensions/common/event_filtering_info.cc
@@ -0,0 +1,48 @@
+// 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.
+
+#include "extensions/common/event_filtering_info.h"
+
+#include "base/json/json_writer.h"
+#include "base/values.h"
+
+namespace extensions {
+
+EventFilteringInfo::EventFilteringInfo()
+ : has_url_(false),
+ has_instance_id_(false),
+ instance_id_(0) {
+}
+
+EventFilteringInfo::~EventFilteringInfo() {
+}
+
+void EventFilteringInfo::SetURL(const GURL& url) {
+ url_ = url;
+ has_url_ = true;
+}
+
+void EventFilteringInfo::SetInstanceID(int instance_id) {
+ instance_id_ = instance_id;
+ has_instance_id_ = true;
+}
+
+scoped_ptr<base::Value> EventFilteringInfo::AsValue() const {
+ if (IsEmpty())
+ return scoped_ptr<base::Value>(base::Value::CreateNullValue());
+
+ scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue);
+ if (has_url_)
+ result->SetString("url", url_.spec());
+
+ if (has_instance_id_)
+ result->SetInteger("instanceId", instance_id_);
+ return result.PassAs<base::Value>();
+}
+
+bool EventFilteringInfo::IsEmpty() const {
+ return !has_url_;
+}
+
+} // namespace extensions
diff --git a/chromium/extensions/common/event_filtering_info.h b/chromium/extensions/common/event_filtering_info.h
new file mode 100644
index 00000000000..86309ab2b93
--- /dev/null
+++ b/chromium/extensions/common/event_filtering_info.h
@@ -0,0 +1,53 @@
+// 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.
+
+#ifndef EXTENSIONS_COMMON_EVENT_FILTERING_INFO_H_
+#define EXTENSIONS_COMMON_EVENT_FILTERING_INFO_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "url/gurl.h"
+
+namespace base {
+class Value;
+}
+
+namespace extensions {
+
+// Extra information about an event that is used in event filtering.
+//
+// This is the information that is matched against criteria specified in JS
+// extension event listeners. Eg:
+//
+// chrome.someApi.onSomeEvent.addListener(cb,
+// {url: [{hostSuffix: 'google.com'}],
+// tabId: 1});
+class EventFilteringInfo {
+ public:
+ EventFilteringInfo();
+ ~EventFilteringInfo();
+ void SetURL(const GURL& url);
+ void SetInstanceID(int instance_id);
+
+ bool has_url() const { return has_url_; }
+ const GURL& url() const { return url_; }
+
+ bool has_instance_id() const { return has_instance_id_; }
+ int instance_id() const { return instance_id_; }
+
+ scoped_ptr<base::Value> AsValue() const;
+ bool IsEmpty() const;
+
+ private:
+ bool has_url_;
+ GURL url_;
+
+ bool has_instance_id_;
+ int instance_id_;
+
+ // Allow implicit copy and assignment.
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_EVENT_FILTERING_INFO_H_
diff --git a/chromium/extensions/common/event_matcher.cc b/chromium/extensions/common/event_matcher.cc
new file mode 100644
index 00000000000..ad76c89f4db
--- /dev/null
+++ b/chromium/extensions/common/event_matcher.cc
@@ -0,0 +1,61 @@
+// 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.
+
+#include "extensions/common/event_matcher.h"
+
+#include "extensions/common/event_filtering_info.h"
+
+namespace {
+const char kUrlFiltersKey[] = "url";
+}
+
+namespace extensions {
+
+EventMatcher::EventMatcher(scoped_ptr<base::DictionaryValue> filter,
+ int routing_id)
+ : filter_(filter.Pass()),
+ routing_id_(routing_id) {
+}
+
+EventMatcher::~EventMatcher() {
+}
+
+bool EventMatcher::MatchNonURLCriteria(
+ const EventFilteringInfo& event_info) const {
+ if (!event_info.has_instance_id())
+ return true;
+
+ return event_info.instance_id() == GetInstanceID();
+}
+
+int EventMatcher::GetURLFilterCount() const {
+ base::ListValue* url_filters = NULL;
+ if (filter_->GetList(kUrlFiltersKey, &url_filters))
+ return url_filters->GetSize();
+ return 0;
+}
+
+bool EventMatcher::GetURLFilter(int i, base::DictionaryValue** url_filter_out) {
+ base::ListValue* url_filters = NULL;
+ if (filter_->GetList(kUrlFiltersKey, &url_filters)) {
+ return url_filters->GetDictionary(i, url_filter_out);
+ }
+ return false;
+}
+
+int EventMatcher::HasURLFilters() const {
+ return GetURLFilterCount() != 0;
+}
+
+int EventMatcher::GetInstanceID() const {
+ int instance_id = 0;
+ filter_->GetInteger("instanceId", &instance_id);
+ return instance_id;
+}
+
+int EventMatcher::GetRoutingID() const {
+ return routing_id_;
+}
+
+} // namespace extensions
diff --git a/chromium/extensions/common/event_matcher.h b/chromium/extensions/common/event_matcher.h
new file mode 100644
index 00000000000..7843fced0c3
--- /dev/null
+++ b/chromium/extensions/common/event_matcher.h
@@ -0,0 +1,57 @@
+// 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.
+
+#ifndef EXTENSIONS_COMMON_EVENT_MATCHER_H_
+#define EXTENSIONS_COMMON_EVENT_MATCHER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+
+namespace extensions {
+
+class EventFilteringInfo;
+
+// Matches EventFilteringInfos against a set of criteria. This is intended to
+// be used by EventFilter which performs efficient URL matching across
+// potentially many EventMatchers itself. This is why this class only exposes
+// MatchNonURLCriteria() - URL matching is handled by EventFilter.
+class EventMatcher {
+ public:
+ EventMatcher(scoped_ptr<base::DictionaryValue> filter,
+ int routing_id);
+ ~EventMatcher();
+
+ // Returns true if |event_info| satisfies this matcher's criteria, not taking
+ // into consideration any URL criteria.
+ bool MatchNonURLCriteria(const EventFilteringInfo& event_info) const;
+
+ int GetURLFilterCount() const;
+ bool GetURLFilter(int i, base::DictionaryValue** url_filter_out);
+
+ int HasURLFilters() const;
+
+ int GetInstanceID() const;
+
+ int GetRoutingID() const;
+
+ base::DictionaryValue* value() const {
+ return filter_.get();
+ }
+
+ private:
+ // Contains a dictionary that corresponds to a single event filter, eg:
+ //
+ // {url: [{hostSuffix: 'google.com'}]}
+ //
+ // The valid filter keys are event-specific.
+ scoped_ptr<base::DictionaryValue> filter_;
+
+ int routing_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(EventMatcher);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_EVENT_MATCHER_H_
diff --git a/chromium/extensions/common/extension_paths.cc b/chromium/extensions/common/extension_paths.cc
new file mode 100644
index 00000000000..1e0c461ac0a
--- /dev/null
+++ b/chromium/extensions/common/extension_paths.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/common/extension_paths.h"
+
+#include "base/file_util.h"
+#include "base/path_service.h"
+
+namespace extensions {
+
+bool PathProvider(int key, base::FilePath* result) {
+ switch (key) {
+ case DIR_TEST_DATA: {
+ base::FilePath cur;
+ if (!PathService::Get(base::DIR_SOURCE_ROOT, &cur))
+ return false;
+ cur = cur.Append(FILE_PATH_LITERAL("extensions"));
+ cur = cur.Append(FILE_PATH_LITERAL("test"));
+ cur = cur.Append(FILE_PATH_LITERAL("data"));
+ if (!base::PathExists(cur)) // we don't want to create this
+ return false;
+
+ *result = cur;
+ return true;
+ break;
+ }
+ default:
+ return false;
+ }
+
+ return false;
+}
+
+// This cannot be done as a static initializer sadly since Visual Studio will
+// eliminate this object file if there is no direct entry point into it.
+void RegisterPathProvider() {
+ PathService::RegisterProvider(PathProvider, PATH_START, PATH_END);
+}
+
+} // namespace extensions
diff --git a/chromium/extensions/common/extension_paths.h b/chromium/extensions/common/extension_paths.h
new file mode 100644
index 00000000000..bbeff6acefd
--- /dev/null
+++ b/chromium/extensions/common/extension_paths.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_COMMON_EXTENSION_PATHS_H_
+#define EXTENSIONS_COMMON_EXTENSION_PATHS_H_
+
+// This file declares path keys for extensions. These can be used with
+// the PathService to access various special directories and files.
+
+namespace extensions {
+
+enum {
+ PATH_START = 6000,
+
+ // Valid only in development environment
+ DIR_TEST_DATA,
+
+ PATH_END
+};
+
+// Call once to register the provider for the path keys defined above.
+void RegisterPathProvider();
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_EXTENSION_PATHS_H_
diff --git a/chromium/extensions/common/extension_resource.cc b/chromium/extensions/common/extension_resource.cc
new file mode 100644
index 00000000000..e6605440132
--- /dev/null
+++ b/chromium/extensions/common/extension_resource.cc
@@ -0,0 +1,128 @@
+// 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.
+
+#include "extensions/common/extension_resource.h"
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/threading/thread_restrictions.h"
+
+namespace extensions {
+
+ExtensionResource::ExtensionResource() : follow_symlinks_anywhere_(false) {
+}
+
+ExtensionResource::ExtensionResource(const std::string& extension_id,
+ const base::FilePath& extension_root,
+ const base::FilePath& relative_path)
+ : extension_id_(extension_id),
+ extension_root_(extension_root),
+ relative_path_(relative_path),
+ follow_symlinks_anywhere_(false) {
+}
+
+ExtensionResource::~ExtensionResource() {}
+
+void ExtensionResource::set_follow_symlinks_anywhere() {
+ follow_symlinks_anywhere_ = true;
+}
+
+const base::FilePath& ExtensionResource::GetFilePath() const {
+ if (extension_root_.empty() || relative_path_.empty()) {
+ DCHECK(full_resource_path_.empty());
+ return full_resource_path_;
+ }
+
+ // We've already checked, just return last value.
+ if (!full_resource_path_.empty())
+ return full_resource_path_;
+
+ full_resource_path_ = GetFilePath(
+ extension_root_, relative_path_,
+ follow_symlinks_anywhere_ ?
+ FOLLOW_SYMLINKS_ANYWHERE : SYMLINKS_MUST_RESOLVE_WITHIN_ROOT);
+ return full_resource_path_;
+}
+
+// static
+base::FilePath ExtensionResource::GetFilePath(
+ const base::FilePath& extension_root,
+ const base::FilePath& relative_path,
+ SymlinkPolicy symlink_policy) {
+ // We need to resolve the parent references in the extension_root
+ // path on its own because IsParent doesn't like parent references.
+ base::FilePath clean_extension_root(
+ base::MakeAbsoluteFilePath(extension_root));
+ if (clean_extension_root.empty())
+ return base::FilePath();
+
+ base::FilePath full_path = clean_extension_root.Append(relative_path);
+
+ // If we are allowing the file to be a symlink outside of the root, then the
+ // path before resolving the symlink must still be within it.
+ if (symlink_policy == FOLLOW_SYMLINKS_ANYWHERE) {
+ std::vector<base::FilePath::StringType> components;
+ relative_path.GetComponents(&components);
+ int depth = 0;
+
+ for (std::vector<base::FilePath::StringType>::const_iterator
+ i = components.begin(); i != components.end(); i++) {
+ if (*i == base::FilePath::kParentDirectory) {
+ depth--;
+ } else if (*i != base::FilePath::kCurrentDirectory) {
+ depth++;
+ }
+ if (depth < 0) {
+ return base::FilePath();
+ }
+ }
+ }
+
+ // We must resolve the absolute path of the combined path when
+ // the relative path contains references to a parent folder (i.e., '..').
+ // We also check if the path exists because the posix version of
+ // MakeAbsoluteFilePath will fail if the path doesn't exist, and we want the
+ // same behavior on Windows... So until the posix and Windows version of
+ // MakeAbsoluteFilePath are unified, we need an extra call to PathExists,
+ // unfortunately.
+ // TODO(mad): Fix this once MakeAbsoluteFilePath is unified.
+ full_path = base::MakeAbsoluteFilePath(full_path);
+ if (base::PathExists(full_path) &&
+ (symlink_policy == FOLLOW_SYMLINKS_ANYWHERE ||
+ clean_extension_root.IsParent(full_path))) {
+ return full_path;
+ }
+
+ return base::FilePath();
+}
+
+// Unit-testing helpers.
+base::FilePath::StringType ExtensionResource::NormalizeSeperators(
+ const base::FilePath::StringType& path) const {
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ base::FilePath::StringType win_path = path;
+ for (size_t i = 0; i < win_path.length(); i++) {
+ if (base::FilePath::IsSeparator(win_path[i]))
+ win_path[i] = base::FilePath::kSeparators[0];
+ }
+ return win_path;
+#else
+ return path;
+#endif // FILE_PATH_USES_WIN_SEPARATORS
+}
+
+bool ExtensionResource::ComparePathWithDefault(
+ const base::FilePath& path) const {
+ // Make sure we have a cached value to test against...
+ if (full_resource_path_.empty())
+ GetFilePath();
+ if (NormalizeSeperators(path.value()) ==
+ NormalizeSeperators(full_resource_path_.value())) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+} // namespace extensions
diff --git a/chromium/extensions/common/extension_resource.h b/chromium/extensions/common/extension_resource.h
new file mode 100644
index 00000000000..806889e8d31
--- /dev/null
+++ b/chromium/extensions/common/extension_resource.h
@@ -0,0 +1,89 @@
+// 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.
+
+#ifndef EXTENSIONS_COMMON_EXTENSION_RESOURCE_H_
+#define EXTENSIONS_COMMON_EXTENSION_RESOURCE_H_
+
+#include <string>
+
+#include "base/files/file_path.h"
+
+namespace extensions {
+
+// Represents a resource inside an extension. For example, an image, or a
+// JavaScript file. This is more complicated than just a simple FilePath
+// because extension resources can come from multiple physical file locations
+// depending on locale.
+class ExtensionResource {
+ public:
+ // SymlinkPolicy decides whether we'll allow resources to be a symlink to
+ // anywhere, or whether they must end up within the extension root.
+ enum SymlinkPolicy {
+ SYMLINKS_MUST_RESOLVE_WITHIN_ROOT,
+ FOLLOW_SYMLINKS_ANYWHERE,
+ };
+
+ ExtensionResource();
+
+ ExtensionResource(const std::string& extension_id,
+ const base::FilePath& extension_root,
+ const base::FilePath& relative_path);
+
+ ~ExtensionResource();
+
+ // set_follow_symlinks_anywhere allows the resource to be a symlink to
+ // anywhere in the filesystem. By default, resources have to be within
+ // |extension_root| after resolving symlinks.
+ void set_follow_symlinks_anywhere();
+
+ // Returns actual path to the resource (default or locale specific). In the
+ // browser process, this will DCHECK if not called on the file thread. To
+ // easily load extension images on the UI thread, see ImageLoader.
+ const base::FilePath& GetFilePath() const;
+
+ // Gets the physical file path for the extension resource, taking into account
+ // localization. In the browser process, this will DCHECK if not called on the
+ // file thread. To easily load extension images on the UI thread, see
+ // ImageLoader.
+ //
+ // The relative path must not resolve to a location outside of
+ // |extension_root|. Iff |file_can_symlink_outside_root| is true, then the
+ // file can be a symlink that links outside of |extension_root|.
+ static base::FilePath GetFilePath(const base::FilePath& extension_root,
+ const base::FilePath& relative_path,
+ SymlinkPolicy symlink_policy);
+
+ // Getters
+ const std::string& extension_id() const { return extension_id_; }
+ const base::FilePath& extension_root() const { return extension_root_; }
+ const base::FilePath& relative_path() const { return relative_path_; }
+
+ bool empty() const { return extension_root().empty(); }
+
+ // Unit test helpers.
+ base::FilePath::StringType NormalizeSeperators(
+ const base::FilePath::StringType& path) const;
+ bool ComparePathWithDefault(const base::FilePath& path) const;
+
+ private:
+ // The id of the extension that this resource is associated with.
+ std::string extension_id_;
+
+ // Extension root.
+ base::FilePath extension_root_;
+
+ // Relative path to resource.
+ base::FilePath relative_path_;
+
+ // If |follow_symlinks_anywhere_| is true then the resource itself must be
+ // within |extension_root|, but it can be a symlink to a file that is not.
+ bool follow_symlinks_anywhere_;
+
+ // Full path to extension resource. Starts empty.
+ mutable base::FilePath full_resource_path_;
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_EXTENSION_RESOURCE_H_
diff --git a/chromium/extensions/common/extension_resource_unittest.cc b/chromium/extensions/common/extension_resource_unittest.cc
new file mode 100644
index 00000000000..8ec44bb33da
--- /dev/null
+++ b/chromium/extensions/common/extension_resource_unittest.cc
@@ -0,0 +1,164 @@
+// 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.
+
+#include <algorithm>
+
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/path_service.h"
+#include "extensions/common/constants.h"
+#include "extensions/common/extension_paths.h"
+#include "extensions/common/extension_resource.h"
+#include "extensions/common/id_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace extensions {
+
+TEST(ExtensionResourceTest, CreateEmptyResource) {
+ ExtensionResource resource;
+
+ EXPECT_TRUE(resource.extension_root().empty());
+ EXPECT_TRUE(resource.relative_path().empty());
+ EXPECT_TRUE(resource.GetFilePath().empty());
+}
+
+const base::FilePath::StringType ToLower(
+ const base::FilePath::StringType& in_str) {
+ base::FilePath::StringType str(in_str);
+ std::transform(str.begin(), str.end(), str.begin(), tolower);
+ return str;
+}
+
+TEST(ExtensionResourceTest, CreateWithMissingResourceOnDisk) {
+ base::FilePath root_path;
+ ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &root_path));
+ base::FilePath relative_path;
+ relative_path = relative_path.AppendASCII("cira.js");
+ std::string extension_id = id_util::GenerateId("test");
+ ExtensionResource resource(extension_id, root_path, relative_path);
+
+ // The path doesn't exist on disk, we will be returned an empty path.
+ EXPECT_EQ(root_path.value(), resource.extension_root().value());
+ EXPECT_EQ(relative_path.value(), resource.relative_path().value());
+ EXPECT_TRUE(resource.GetFilePath().empty());
+}
+
+TEST(ExtensionResourceTest, ResourcesOutsideOfPath) {
+ base::ScopedTempDir temp;
+ ASSERT_TRUE(temp.CreateUniqueTempDir());
+
+ base::FilePath inner_dir = temp.path().AppendASCII("directory");
+ ASSERT_TRUE(file_util::CreateDirectory(inner_dir));
+ base::FilePath sub_dir = inner_dir.AppendASCII("subdir");
+ ASSERT_TRUE(file_util::CreateDirectory(sub_dir));
+ base::FilePath inner_file = inner_dir.AppendASCII("inner");
+ base::FilePath outer_file = temp.path().AppendASCII("outer");
+ ASSERT_TRUE(file_util::WriteFile(outer_file, "X", 1));
+ ASSERT_TRUE(file_util::WriteFile(inner_file, "X", 1));
+ std::string extension_id = id_util::GenerateId("test");
+
+#if defined(OS_POSIX)
+ base::FilePath symlink_file = inner_dir.AppendASCII("symlink");
+ file_util::CreateSymbolicLink(
+ base::FilePath().AppendASCII("..").AppendASCII("outer"),
+ symlink_file);
+#endif
+
+ // A non-packing extension should be able to access the file within the
+ // directory.
+ ExtensionResource r1(extension_id, inner_dir,
+ base::FilePath().AppendASCII("inner"));
+ EXPECT_FALSE(r1.GetFilePath().empty());
+
+ // ... but not a relative path that walks out of |inner_dir|.
+ ExtensionResource r2(extension_id, inner_dir,
+ base::FilePath().AppendASCII("..").AppendASCII("outer"));
+ EXPECT_TRUE(r2.GetFilePath().empty());
+
+ // A packing extension should also be able to access the file within the
+ // directory.
+ ExtensionResource r3(extension_id, inner_dir,
+ base::FilePath().AppendASCII("inner"));
+ r3.set_follow_symlinks_anywhere();
+ EXPECT_FALSE(r3.GetFilePath().empty());
+
+ // ... but, again, not a relative path that walks out of |inner_dir|.
+ ExtensionResource r4(extension_id, inner_dir,
+ base::FilePath().AppendASCII("..").AppendASCII("outer"));
+ r4.set_follow_symlinks_anywhere();
+ EXPECT_TRUE(r4.GetFilePath().empty());
+
+ // ... and not even when clever current-directory syntax is present. Note
+ // that the path for this test case can't start with the current directory
+ // component due to quirks in FilePath::Append(), and the path must exist.
+ ExtensionResource r4a(
+ extension_id, inner_dir,
+ base::FilePath().AppendASCII("subdir").AppendASCII(".").AppendASCII("..").
+ AppendASCII("..").AppendASCII("outer"));
+ r4a.set_follow_symlinks_anywhere();
+ EXPECT_TRUE(r4a.GetFilePath().empty());
+
+#if defined(OS_POSIX)
+ // The non-packing extension should also not be able to access a resource that
+ // symlinks out of the directory.
+ ExtensionResource r5(extension_id, inner_dir,
+ base::FilePath().AppendASCII("symlink"));
+ EXPECT_TRUE(r5.GetFilePath().empty());
+
+ // ... but a packing extension can.
+ ExtensionResource r6(extension_id, inner_dir,
+ base::FilePath().AppendASCII("symlink"));
+ r6.set_follow_symlinks_anywhere();
+ EXPECT_FALSE(r6.GetFilePath().empty());
+#endif
+}
+
+TEST(ExtensionResourceTest, CreateWithAllResourcesOnDisk) {
+ base::ScopedTempDir temp;
+ ASSERT_TRUE(temp.CreateUniqueTempDir());
+
+ // Create resource in the extension root.
+ const char* filename = "res.ico";
+ base::FilePath root_resource = temp.path().AppendASCII(filename);
+ std::string data = "some foo";
+ ASSERT_TRUE(file_util::WriteFile(root_resource, data.c_str(), data.length()));
+
+ // Create l10n resources (for current locale and its parents).
+ base::FilePath l10n_path =
+ temp.path().Append(kLocaleFolder);
+ ASSERT_TRUE(file_util::CreateDirectory(l10n_path));
+
+ std::vector<std::string> locales;
+ l10n_util::GetParentLocales(l10n_util::GetApplicationLocale(std::string()),
+ &locales);
+ ASSERT_FALSE(locales.empty());
+ for (size_t i = 0; i < locales.size(); i++) {
+ base::FilePath make_path;
+ make_path = l10n_path.AppendASCII(locales[i]);
+ ASSERT_TRUE(file_util::CreateDirectory(make_path));
+ ASSERT_TRUE(file_util::WriteFile(make_path.AppendASCII(filename),
+ data.c_str(), data.length()));
+ }
+
+ base::FilePath path;
+ std::string extension_id = id_util::GenerateId("test");
+ ExtensionResource resource(extension_id, temp.path(),
+ base::FilePath().AppendASCII(filename));
+ base::FilePath resolved_path = resource.GetFilePath();
+
+ base::FilePath expected_path;
+ // Expect default path only, since fallback logic is disabled.
+ // See http://crbug.com/27359.
+ expected_path = base::MakeAbsoluteFilePath(root_resource);
+ ASSERT_FALSE(expected_path.empty());
+
+ EXPECT_EQ(ToLower(expected_path.value()), ToLower(resolved_path.value()));
+ EXPECT_EQ(ToLower(temp.path().value()),
+ ToLower(resource.extension_root().value()));
+ EXPECT_EQ(ToLower(base::FilePath().AppendASCII(filename).value()),
+ ToLower(resource.relative_path().value()));
+}
+
+} // namespace extensions
diff --git a/chromium/extensions/common/extensions_client.cc b/chromium/extensions/common/extensions_client.cc
new file mode 100644
index 00000000000..e7f936dfbdf
--- /dev/null
+++ b/chromium/extensions/common/extensions_client.cc
@@ -0,0 +1,32 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "extensions/common/extensions_client.h"
+
+namespace extensions {
+
+namespace {
+
+ExtensionsClient* g_client = NULL;
+
+void Initialize(ExtensionsClient* client) {
+ client->RegisterManifestHandlers();
+}
+
+} // namespace
+
+ExtensionsClient* ExtensionsClient::Get() {
+ return g_client;
+}
+
+void ExtensionsClient::Set(ExtensionsClient* client) {
+ // This can happen in unit tests, where the utility thread runs in-process.
+ if (g_client)
+ return;
+ g_client = client;
+ Initialize(g_client);
+}
+
+} // namespace extensions
diff --git a/chromium/extensions/common/extensions_client.h b/chromium/extensions/common/extensions_client.h
new file mode 100644
index 00000000000..83550a9a1aa
--- /dev/null
+++ b/chromium/extensions/common/extensions_client.h
@@ -0,0 +1,38 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_COMMON_EXTENSIONS_CLIENT_H_
+#define EXTENSIONS_COMMON_EXTENSIONS_CLIENT_H_
+
+#include <string>
+
+namespace extensions {
+
+class FeatureProvider;
+class PermissionsProvider;
+
+// Sets up global state for the extensions system. Should be Set() once in each
+// process. This should be implemented by the client of the extensions system.
+class ExtensionsClient {
+ public:
+ // Returns a PermissionsProvider to initialize the permissions system.
+ virtual const PermissionsProvider& GetPermissionsProvider() const = 0;
+
+ // Gets a feature provider for a specific feature type.
+ virtual FeatureProvider* GetFeatureProviderByName(const std::string& name)
+ const = 0;
+
+ // Called at startup. Registers the handlers for parsing manifests.
+ virtual void RegisterManifestHandlers() const = 0;
+
+ // Return the extensions client.
+ static ExtensionsClient* Get();
+
+ // Initialize the extensions system with this extensions client.
+ static void Set(ExtensionsClient* client);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_EXTENSIONS_CLIENT_H_
diff --git a/chromium/extensions/common/features/feature_provider.cc b/chromium/extensions/common/features/feature_provider.cc
new file mode 100644
index 00000000000..8e15d4b5366
--- /dev/null
+++ b/chromium/extensions/common/features/feature_provider.cc
@@ -0,0 +1,16 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/common/features/feature_provider.h"
+
+#include "base/basictypes.h"
+#include "extensions/common/extensions_client.h"
+
+namespace extensions {
+
+FeatureProvider* FeatureProvider::GetByName(const std::string& name) {
+ return ExtensionsClient::Get()->GetFeatureProviderByName(name);
+}
+
+} // namespace extensions
diff --git a/chromium/extensions/common/features/feature_provider.h b/chromium/extensions/common/features/feature_provider.h
new file mode 100644
index 00000000000..c20e5d1c3b8
--- /dev/null
+++ b/chromium/extensions/common/features/feature_provider.h
@@ -0,0 +1,36 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_COMMON_FEATURES_FEATURE_PROVIDER_H_
+#define EXTENSIONS_COMMON_FEATURES_FEATURE_PROVIDER_H_
+
+#include <string>
+#include <vector>
+
+namespace extensions {
+
+class Feature;
+
+// Implemented by classes that can vend features.
+class FeatureProvider {
+ public:
+ FeatureProvider() {}
+ virtual ~FeatureProvider() {}
+
+ // Returns the feature with the specified name.
+ virtual Feature* GetFeature(const std::string& name) = 0;
+
+ // Returns the parent feature of |feature|, or NULL if there isn't one.
+ virtual Feature* GetParent(Feature* feature) = 0;
+
+ // Returns all features described by this instance.
+ virtual const std::vector<std::string>& GetAllFeatureNames() = 0;
+
+ // Gets a feature provider for a specific feature type, like "permission".
+ static FeatureProvider* GetByName(const std::string& name);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_FEATURES_FEATURE_PROVIDER_H_
diff --git a/chromium/extensions/common/id_util.cc b/chromium/extensions/common/id_util.cc
new file mode 100644
index 00000000000..3b8628ee04c
--- /dev/null
+++ b/chromium/extensions/common/id_util.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/common/id_util.h"
+
+#include "base/files/file_path.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "crypto/sha2.h"
+
+namespace {
+
+// Converts a normal hexadecimal string into the alphabet used by extensions.
+// We use the characters 'a'-'p' instead of '0'-'f' to avoid ever having a
+// completely numeric host, since some software interprets that as an IP
+// address.
+static void ConvertHexadecimalToIDAlphabet(std::string* id) {
+ for (size_t i = 0; i < id->size(); ++i) {
+ int val;
+ if (base::HexStringToInt(base::StringPiece(id->begin() + i,
+ id->begin() + i + 1),
+ &val)) {
+ (*id)[i] = val + 'a';
+ } else {
+ (*id)[i] = 'a';
+ }
+ }
+}
+
+} // namespace
+
+namespace extensions {
+namespace id_util {
+
+// First 16 bytes of SHA256 hashed public key.
+const size_t kIdSize = 16;
+
+std::string GenerateId(const std::string& input) {
+ uint8 hash[kIdSize];
+ crypto::SHA256HashString(input, hash, sizeof(hash));
+ std::string output = StringToLowerASCII(base::HexEncode(hash, sizeof(hash)));
+ ConvertHexadecimalToIDAlphabet(&output);
+
+ return output;
+}
+
+std::string GenerateIdForPath(const base::FilePath& path) {
+ base::FilePath new_path = MaybeNormalizePath(path);
+ std::string path_bytes =
+ std::string(reinterpret_cast<const char*>(new_path.value().data()),
+ new_path.value().size() * sizeof(base::FilePath::CharType));
+ return GenerateId(path_bytes);
+}
+
+base::FilePath MaybeNormalizePath(const base::FilePath& path) {
+#if defined(OS_WIN)
+ // Normalize any drive letter to upper-case. We do this for consistency with
+ // net_utils::FilePathToFileURL(), which does the same thing, to make string
+ // comparisons simpler.
+ base::FilePath::StringType path_str = path.value();
+ if (path_str.size() >= 2 && path_str[0] >= L'a' && path_str[0] <= L'z' &&
+ path_str[1] == ':')
+ path_str[0] += ('A' - 'a');
+
+ return base::FilePath(path_str);
+#else
+ return path;
+#endif
+}
+
+} // namespace id_util
+} // namespace extensions
diff --git a/chromium/extensions/common/id_util.h b/chromium/extensions/common/id_util.h
new file mode 100644
index 00000000000..74e690dbf60
--- /dev/null
+++ b/chromium/extensions/common/id_util.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_COMMON_ID_UTIL_H_
+#define EXTENSIONS_COMMON_ID_UTIL_H_
+
+#include <string>
+
+namespace base {
+class FilePath;
+}
+
+namespace extensions {
+namespace id_util {
+
+// The number of bytes in a legal id.
+extern const size_t kIdSize;
+
+// Generates an extension ID from arbitrary input. The same input string will
+// always generate the same output ID.
+std::string GenerateId(const std::string& input);
+
+// Generate an ID for an extension in the given path.
+// Used while developing extensions, before they have a key.
+std::string GenerateIdForPath(const base::FilePath& path);
+
+// Normalize the path for use by the extension. On Windows, this will make
+// sure the drive letter is uppercase.
+base::FilePath MaybeNormalizePath(const base::FilePath& path);
+
+} // namespace id_util
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_ID_UTIL_H_
diff --git a/chromium/extensions/common/id_util_unittest.cc b/chromium/extensions/common/id_util_unittest.cc
new file mode 100644
index 00000000000..f43d206f1d9
--- /dev/null
+++ b/chromium/extensions/common/id_util_unittest.cc
@@ -0,0 +1,44 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "extensions/common/id_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+namespace id_util {
+
+TEST(IDUtilTest, GenerateID) {
+ const uint8 public_key_info[] = {
+ 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81,
+ 0x89, 0x02, 0x81, 0x81, 0x00, 0xb8, 0x7f, 0x2b, 0x20, 0xdc, 0x7c, 0x9b,
+ 0x0c, 0xdc, 0x51, 0x61, 0x99, 0x0d, 0x36, 0x0f, 0xd4, 0x66, 0x88, 0x08,
+ 0x55, 0x84, 0xd5, 0x3a, 0xbf, 0x2b, 0xa4, 0x64, 0x85, 0x7b, 0x0c, 0x04,
+ 0x13, 0x3f, 0x8d, 0xf4, 0xbc, 0x38, 0x0d, 0x49, 0xfe, 0x6b, 0xc4, 0x5a,
+ 0xb0, 0x40, 0x53, 0x3a, 0xd7, 0x66, 0x09, 0x0f, 0x9e, 0x36, 0x74, 0x30,
+ 0xda, 0x8a, 0x31, 0x4f, 0x1f, 0x14, 0x50, 0xd7, 0xc7, 0x20, 0x94, 0x17,
+ 0xde, 0x4e, 0xb9, 0x57, 0x5e, 0x7e, 0x0a, 0xe5, 0xb2, 0x65, 0x7a, 0x89,
+ 0x4e, 0xb6, 0x47, 0xff, 0x1c, 0xbd, 0xb7, 0x38, 0x13, 0xaf, 0x47, 0x85,
+ 0x84, 0x32, 0x33, 0xf3, 0x17, 0x49, 0xbf, 0xe9, 0x96, 0xd0, 0xd6, 0x14,
+ 0x6f, 0x13, 0x8d, 0xc5, 0xfc, 0x2c, 0x72, 0xba, 0xac, 0xea, 0x7e, 0x18,
+ 0x53, 0x56, 0xa6, 0x83, 0xa2, 0xce, 0x93, 0x93, 0xe7, 0x1f, 0x0f, 0xe6,
+ 0x0f, 0x02, 0x03, 0x01, 0x00, 0x01
+ };
+ std::string extension_id = GenerateId(
+ std::string(reinterpret_cast<const char*>(&public_key_info[0]),
+ arraysize(public_key_info)));
+ EXPECT_EQ("melddjfinppjdikinhbgehiennejpfhp", extension_id);
+
+ EXPECT_EQ("jpignaibiiemhngfjkcpokkamffknabf", GenerateId("test"));
+
+ EXPECT_EQ("ncocknphbhhlhkikpnnlmbcnbgdempcd", GenerateId("_"));
+
+ EXPECT_EQ("jimneklojkjdibfkgiiophfhjhbdgcfi",
+ GenerateId(
+ "this_string_is_longer_than_a_single_sha256_hash_digest"));
+}
+
+} // namespace id_util
+} // namespace extensions
diff --git a/chromium/extensions/common/install_warning.cc b/chromium/extensions/common/install_warning.cc
new file mode 100644
index 00000000000..c632a71ae08
--- /dev/null
+++ b/chromium/extensions/common/install_warning.cc
@@ -0,0 +1,24 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/common/install_warning.h"
+
+namespace extensions {
+
+void PrintTo(const InstallWarning& warning, ::std::ostream* os) {
+ *os << "InstallWarning(";
+ switch (warning.format) {
+ case InstallWarning::FORMAT_TEXT:
+ *os << "FORMAT_TEXT, \"";
+ break;
+ case InstallWarning::FORMAT_HTML:
+ *os << "FORMAT_HTML, \"";
+ break;
+ }
+ // This is just for test error messages, so no need to escape '"'
+ // characters inside the message.
+ *os << warning.message << "\")";
+}
+
+} // namespace extensions
diff --git a/chromium/extensions/common/install_warning.h b/chromium/extensions/common/install_warning.h
new file mode 100644
index 00000000000..fb3fccb29b6
--- /dev/null
+++ b/chromium/extensions/common/install_warning.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_COMMON_INSTALL_WARNING_H_
+#define EXTENSIONS_COMMON_INSTALL_WARNING_H_
+
+#include <ostream>
+#include <string>
+
+namespace extensions {
+
+struct InstallWarning {
+ enum Format {
+ // IMPORTANT: Do not build HTML strings from user or developer-supplied
+ // input.
+ FORMAT_TEXT,
+ FORMAT_HTML,
+ };
+ static InstallWarning Text(const std::string& message) {
+ return InstallWarning(FORMAT_TEXT, message);
+ }
+ InstallWarning(Format format, const std::string& message)
+ : format(format), message(message) {
+ }
+ bool operator==(const InstallWarning& other) const {
+ return format == other.format && message == other.message;
+ }
+ Format format;
+ std::string message;
+};
+
+// Let gtest print InstallWarnings.
+void PrintTo(const InstallWarning&, ::std::ostream* os);
+
+} // namespace
+
+#endif // EXTENSIONS_COMMON_INSTALL_WARNING_H_
diff --git a/chromium/extensions/common/manifest_constants.cc b/chromium/extensions/common/manifest_constants.cc
new file mode 100644
index 00000000000..fcdf6e99fc8
--- /dev/null
+++ b/chromium/extensions/common/manifest_constants.cc
@@ -0,0 +1,151 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/common/manifest_constants.h"
+
+namespace extensions {
+
+namespace manifest_keys {
+
+const char kAllFrames[] = "all_frames";
+const char kAltKey[] = "altKey";
+const char kApp[] = "app";
+const char kAudio[] = "audio";
+const char kBackgroundAllowJsAccess[] = "background.allow_js_access";
+const char kBackgroundPage[] = "background.page";
+const char kBackgroundPageLegacy[] = "background_page";
+const char kBackgroundPersistent[] = "background.persistent";
+const char kBackgroundScripts[] = "background.scripts";
+const char kBrowserAction[] = "browser_action";
+const char kChromeURLOverrides[] = "chrome_url_overrides";
+const char kCommands[] = "commands";
+const char kContentPack[] = "content_pack";
+const char kContentPackSites[] = "sites";
+const char kContentScripts[] = "content_scripts";
+const char kContentSecurityPolicy[] = "content_security_policy";
+const char kConvertedFromUserScript[] = "converted_from_user_script";
+const char kCss[] = "css";
+const char kCtrlKey[] = "ctrlKey";
+const char kCurrentLocale[] = "current_locale";
+const char kDefaultLocale[] = "default_locale";
+const char kDescription[] = "description";
+const char kDevToolsPage[] = "devtools_page";
+const char kDisplayInLauncher[] = "display_in_launcher";
+const char kDisplayInNewTabPage[] = "display_in_new_tab_page";
+const char kEventName[] = "event_name";
+const char kExcludeGlobs[] = "exclude_globs";
+const char kExcludeMatches[] = "exclude_matches";
+const char kExport[] = "export";
+const char kExternallyConnectable[] = "externally_connectable";
+const char kFileAccessList[] = "file_access";
+const char kFileFilters[] = "file_filters";
+const char kFileBrowserHandlers[] = "file_browser_handlers";
+const char kMediaGalleriesHandlers[] = "media_galleries_handlers";
+const char kFileHandlers[] = "file_handlers";
+const char kFileHandlerExtensions[] = "extensions";
+const char kFileHandlerTitle[] = "title";
+const char kFileHandlerTypes[] = "types";
+const char kHomepageURL[] = "homepage_url";
+const char kIcons[] = "icons";
+const char kId[] = "id";
+const char kImport[] = "import";
+const char kIncognito[] = "incognito";
+const char kIncludeGlobs[] = "include_globs";
+const char kInputComponents[] = "input_components";
+const char kIsolation[] = "app.isolation";
+const char kJs[] = "js";
+const char kKey[] = "key";
+const char kKeycode[] = "keyCode";
+const char kKioskEnabled[] = "kiosk_enabled";
+const char kLanguage[] = "language";
+const char kLaunch[] = "app.launch";
+const char kLaunchContainer[] = "app.launch.container";
+const char kLaunchHeight[] = "app.launch.height";
+const char kLaunchLocalPath[] = "app.launch.local_path";
+const char kLaunchWebURL[] = "app.launch.web_url";
+const char kLaunchWidth[] = "app.launch.width";
+const char kLayouts[] = "layouts";
+const char kManifestVersion[] = "manifest_version";
+const char kMatches[] = "matches";
+const char kMinimumChromeVersion[] = "minimum_chrome_version";
+const char kMinimumVersion[] = "minimum_version";
+const char kMIMETypes[] = "mime_types";
+const char kMimeTypesHandler[] = "mime_types_handler";
+const char kName[] = "name";
+const char kNaClModules[] = "nacl_modules";
+const char kNaClModulesMIMEType[] = "mime_type";
+const char kNaClModulesPath[] = "path";
+const char kOAuth2[] = "oauth2";
+const char kOAuth2AutoApprove[] = "oauth2.auto_approve";
+const char kOAuth2ClientId[] = "oauth2.client_id";
+const char kOAuth2Scopes[] = "oauth2.scopes";
+const char kOfflineEnabled[] = "offline_enabled";
+const char kOmnibox[] = "omnibox";
+const char kOmniboxKeyword[] = "omnibox.keyword";
+const char kOptionalPermissions[] = "optional_permissions";
+const char kOptionsPage[] = "options_page";
+const char kPageAction[] = "page_action";
+const char kPageActionDefaultIcon[] = "default_icon";
+const char kPageActionDefaultPopup[] = "default_popup";
+const char kPageActionDefaultTitle[] = "default_title";
+const char kPageActionIcons[] = "icons";
+const char kPageActionId[] = "id";
+const char kPageActionPopup[] = "popup";
+const char kPageActionPopupPath[] = "path";
+const char kPageActions[] = "page_actions";
+const char kPermissions[] = "permissions";
+const char kPlatformAppBackground[] = "app.background";
+const char kPlatformAppBackgroundPage[] = "app.background.page";
+const char kPlatformAppBackgroundScripts[] = "app.background.scripts";
+const char kPlatformAppContentSecurityPolicy[] = "app.content_security_policy";
+const char kPlugins[] = "plugins";
+const char kPluginsPath[] = "path";
+const char kPluginsPublic[] = "public";
+const char kPublicKey[] = "key";
+const char kResources[] = "resources";
+const char kRequirements[] = "requirements";
+const char kRunAt[] = "run_at";
+const char kSandboxedPages[] = "sandbox.pages";
+const char kSandboxedPagesCSP[] = "sandbox.content_security_policy";
+const char kScriptBadge[] = "script_badge";
+const char kShiftKey[] = "shiftKey";
+const char kShortcutKey[] = "shortcutKey";
+const char kSignature[] = "signature";
+const char kSpellcheck[] = "spellcheck";
+const char kSpellcheckDictionaryFormat[] = "dictionary_format";
+const char kSpellcheckDictionaryLanguage[] = "dictionary_language";
+const char kSpellcheckDictionaryLocale[] = "dictionary_locale";
+const char kSpellcheckDictionaryPath[] = "dictionary_path";
+const char kStorageManagedSchema[] = "storage.managed_schema";
+const char kSuggestedKey[] = "suggested_key";
+const char kSystemIndicator[] = "system_indicator";
+const char kSystemInfoDisplay[] = "systemInfo.display";
+const char kTheme[] = "theme";
+const char kThemeColors[] = "colors";
+const char kThemeDisplayProperties[] = "properties";
+const char kThemeImages[] = "images";
+const char kThemeTints[] = "tints";
+const char kTtsEngine[] = "tts_engine";
+const char kTtsGenderFemale[] = "female";
+const char kTtsGenderMale[] = "male";
+const char kTtsVoices[] = "voices";
+const char kTtsVoicesEventTypeEnd[] = "end";
+const char kTtsVoicesEventTypeError[] = "error";
+const char kTtsVoicesEventTypeMarker[] = "marker";
+const char kTtsVoicesEventTypeSentence[] = "sentence";
+const char kTtsVoicesEventTypeStart[] = "start";
+const char kTtsVoicesEventTypeWord[] = "word";
+const char kTtsVoicesEventTypes[] = "event_types";
+const char kTtsVoicesGender[] = "gender";
+const char kTtsVoicesLang[] = "lang";
+const char kTtsVoicesVoiceName[] = "voice_name";
+const char kType[] = "type";
+const char kUpdateURL[] = "update_url";
+const char kVersion[] = "version";
+const char kWebAccessibleResources[] = "web_accessible_resources";
+const char kWebURLs[] = "app.urls";
+
+} // namespace manifest_keys
+
+} // namespace extensions
diff --git a/chromium/extensions/common/manifest_constants.h b/chromium/extensions/common/manifest_constants.h
new file mode 100644
index 00000000000..ce31f7c6881
--- /dev/null
+++ b/chromium/extensions/common/manifest_constants.h
@@ -0,0 +1,157 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_COMMON_MANIFEST_CONSTANTS_H_
+#define EXTENSIONS_COMMON_MANIFEST_CONSTANTS_H_
+
+// Keys used in JSON representation of extensions.
+namespace extensions {
+namespace manifest_keys {
+ extern const char kAllFrames[];
+ extern const char kAltKey[];
+ extern const char kApp[];
+ extern const char kBackgroundAllowJsAccess[];
+ extern const char kBackgroundPage[];
+ extern const char kBackgroundPageLegacy[];
+ extern const char kBackgroundPersistent[];
+ extern const char kBackgroundScripts[];
+ extern const char kBrowserAction[];
+ extern const char kBrowseURLs[];
+ extern const char kChromeURLOverrides[];
+ extern const char kCommands[];
+ extern const char kContentPack[];
+ extern const char kContentPackSites[];
+ extern const char kContentScripts[];
+ extern const char kContentSecurityPolicy[];
+ extern const char kConvertedFromUserScript[];
+ extern const char kCss[];
+ extern const char kCtrlKey[];
+ extern const char kCurrentLocale[];
+ extern const char kDefaultLocale[];
+ extern const char kDescription[];
+ extern const char kDevToolsPage[];
+ extern const char kDisplayInLauncher[];
+ extern const char kDisplayInNewTabPage[];
+ extern const char kEventName[];
+ extern const char kExcludeGlobs[];
+ extern const char kExcludeMatches[];
+ extern const char kExport[];
+ extern const char kExternallyConnectable[];
+ extern const char kFileAccessList[];
+ extern const char kFileHandlers[];
+ extern const char kFileHandlerExtensions[];
+ extern const char kFileHandlerTitle[];
+ extern const char kFileHandlerTypes[];
+ extern const char kFileFilters[];
+ extern const char kFileBrowserHandlers[];
+ extern const char kMediaGalleriesHandlers[];
+ extern const char kHomepageURL[];
+ extern const char kIcons[];
+ extern const char kId[];
+ extern const char kImport[];
+ extern const char kIncognito[];
+ extern const char kIncludeGlobs[];
+ extern const char kInputComponents[];
+ extern const char kIntentDisposition[];
+ extern const char kIntentHref[];
+ extern const char kIntentPath[];
+ extern const char kIntents[];
+ extern const char kIntentTitle[];
+ extern const char kIntentType[];
+ extern const char kIsolation[];
+ extern const char kJs[];
+ extern const char kKey[];
+ extern const char kKeycode[];
+ extern const char kKioskEnabled[];
+ extern const char kLanguage[];
+ extern const char kLaunch[];
+ extern const char kLaunchContainer[];
+ extern const char kLaunchHeight[];
+ extern const char kLaunchLocalPath[];
+ extern const char kLaunchWebURL[];
+ extern const char kLaunchWidth[];
+ extern const char kLayouts[];
+ extern const char kManifestVersion[];
+ extern const char kMatches[];
+ extern const char kMIMETypes[];
+ extern const char kMimeTypesHandler[];
+ extern const char kMinimumChromeVersion[];
+ extern const char kMinimumVersion[];
+ extern const char kNaClModules[];
+ extern const char kNaClModulesMIMEType[];
+ extern const char kNaClModulesPath[];
+ extern const char kName[];
+ extern const char kOAuth2[];
+ extern const char kOAuth2AutoApprove[];
+ extern const char kOAuth2ClientId[];
+ extern const char kOAuth2Scopes[];
+ extern const char kOfflineEnabled[];
+ extern const char kOmnibox[];
+ extern const char kOmniboxKeyword[];
+ extern const char kOptionalPermissions[];
+ extern const char kOptionsPage[];
+ extern const char kPageAction[];
+ extern const char kPageActionDefaultIcon[];
+ extern const char kPageActionDefaultPopup[];
+ extern const char kPageActionDefaultTitle[];
+ extern const char kPageActionIcons[];
+ extern const char kPageActionId[];
+ extern const char kPageActionPopup[];
+ extern const char kPageActionPopupPath[];
+ extern const char kPageActions[];
+ extern const char kPermissions[];
+ extern const char kPlatformAppBackground[];
+ extern const char kPlatformAppBackgroundPage[];
+ extern const char kPlatformAppBackgroundScripts[];
+ extern const char kPlatformAppContentSecurityPolicy[];
+ extern const char kPlugins[];
+ extern const char kPluginsPath[];
+ extern const char kPluginsPublic[];
+ extern const char kPublicKey[];
+ extern const char kResources[];
+ extern const char kRequirements[];
+ extern const char kRunAt[];
+ extern const char kSandboxedPages[];
+ extern const char kSandboxedPagesCSP[];
+ extern const char kScriptBadge[];
+ extern const char kShiftKey[];
+ extern const char kShortcutKey[];
+ extern const char kSignature[];
+ extern const char kSpellcheck[];
+ extern const char kSpellcheckDictionaryFormat[];
+ extern const char kSpellcheckDictionaryLanguage[];
+ extern const char kSpellcheckDictionaryLocale[];
+ extern const char kSpellcheckDictionaryPath[];
+ extern const char kStorageManagedSchema[];
+ extern const char kSuggestedKey[];
+ extern const char kSystemIndicator[];
+ extern const char kTheme[];
+ extern const char kThemeColors[];
+ extern const char kThemeDisplayProperties[];
+ extern const char kThemeImages[];
+ extern const char kThemeTints[];
+ extern const char kTtsEngine[];
+ extern const char kTtsGenderFemale[];
+ extern const char kTtsGenderMale[];
+ extern const char kTtsVoices[];
+ extern const char kTtsVoicesEventTypeEnd[];
+ extern const char kTtsVoicesEventTypeError[];
+ extern const char kTtsVoicesEventTypeMarker[];
+ extern const char kTtsVoicesEventTypeSentence[];
+ extern const char kTtsVoicesEventTypeStart[];
+ extern const char kTtsVoicesEventTypeWord[];
+ extern const char kTtsVoicesEventTypes[];
+ extern const char kTtsVoicesGender[];
+ extern const char kTtsVoicesLang[];
+ extern const char kTtsVoicesVoiceName[];
+ extern const char kType[];
+ extern const char kUpdateURL[];
+ extern const char kVersion[];
+ extern const char kWebAccessibleResources[];
+ extern const char kWebURLs[];
+} // namespace manifest_keys
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_MANIFEST_CONSTANTS_H_
diff --git a/chromium/extensions/common/matcher/DEPS b/chromium/extensions/common/matcher/DEPS
new file mode 100644
index 00000000000..972b087be35
--- /dev/null
+++ b/chromium/extensions/common/matcher/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+third_party/re2"
+]
diff --git a/chromium/extensions/common/matcher/OWNERS b/chromium/extensions/common/matcher/OWNERS
new file mode 100644
index 00000000000..d5f125f04e3
--- /dev/null
+++ b/chromium/extensions/common/matcher/OWNERS
@@ -0,0 +1 @@
+battre@chromium.org
diff --git a/chromium/extensions/common/matcher/regex_set_matcher.cc b/chromium/extensions/common/matcher/regex_set_matcher.cc
new file mode 100644
index 00000000000..24d99efa3ff
--- /dev/null
+++ b/chromium/extensions/common/matcher/regex_set_matcher.cc
@@ -0,0 +1,113 @@
+// 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.
+
+#include "extensions/common/matcher/regex_set_matcher.h"
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "extensions/common/matcher/substring_set_matcher.h"
+#include "third_party/re2/re2/filtered_re2.h"
+#include "third_party/re2/re2/re2.h"
+
+namespace extensions {
+
+RegexSetMatcher::RegexSetMatcher() {}
+
+RegexSetMatcher::~RegexSetMatcher() {
+ DeleteSubstringPatterns();
+}
+
+void RegexSetMatcher::AddPatterns(
+ const std::vector<const StringPattern*>& regex_list) {
+ if (regex_list.empty())
+ return;
+ for (size_t i = 0; i < regex_list.size(); ++i) {
+ regexes_[regex_list[i]->id()] = regex_list[i];
+ }
+
+ RebuildMatcher();
+}
+
+void RegexSetMatcher::ClearPatterns() {
+ regexes_.clear();
+ RebuildMatcher();
+}
+
+bool RegexSetMatcher::Match(const std::string& text,
+ std::set<StringPattern::ID>* matches) const {
+ size_t old_number_of_matches = matches->size();
+ if (regexes_.empty())
+ return false;
+ if (!filtered_re2_.get()) {
+ LOG(ERROR) << "RegexSetMatcher was not initialized";
+ return false;
+ }
+
+ // FilteredRE2 expects lowercase for prefiltering, but we still
+ // match case-sensitively.
+ std::vector<RE2ID> atoms(FindSubstringMatches(
+ StringToLowerASCII(text)));
+
+ std::vector<RE2ID> re2_ids;
+ filtered_re2_->AllMatches(text, atoms, &re2_ids);
+
+ for (size_t i = 0; i < re2_ids.size(); ++i) {
+ StringPattern::ID id = re2_id_map_[re2_ids[i]];
+ matches->insert(id);
+ }
+ return old_number_of_matches != matches->size();
+}
+
+bool RegexSetMatcher::IsEmpty() const {
+ return regexes_.empty();
+}
+
+std::vector<RegexSetMatcher::RE2ID> RegexSetMatcher::FindSubstringMatches(
+ const std::string& text) const {
+ std::set<int> atoms_set;
+ substring_matcher_->Match(text, &atoms_set);
+ return std::vector<RE2ID>(atoms_set.begin(), atoms_set.end());
+}
+
+void RegexSetMatcher::RebuildMatcher() {
+ re2_id_map_.clear();
+ filtered_re2_.reset(new re2::FilteredRE2());
+ if (regexes_.empty())
+ return;
+
+ for (RegexMap::iterator it = regexes_.begin(); it != regexes_.end(); ++it) {
+ RE2ID re2_id;
+ RE2::ErrorCode error = filtered_re2_->Add(
+ it->second->pattern(), RE2::DefaultOptions, &re2_id);
+ if (error == RE2::NoError) {
+ DCHECK_EQ(static_cast<RE2ID>(re2_id_map_.size()), re2_id);
+ re2_id_map_.push_back(it->first);
+ } else {
+ // Unparseable regexes should have been rejected already in
+ // URLMatcherFactory::CreateURLMatchesCondition.
+ LOG(ERROR) << "Could not parse regex (id=" << it->first << ", "
+ << it->second->pattern() << ")";
+ }
+ }
+
+ std::vector<std::string> strings_to_match;
+ filtered_re2_->Compile(&strings_to_match);
+
+ substring_matcher_.reset(new SubstringSetMatcher);
+ DeleteSubstringPatterns();
+ // Build SubstringSetMatcher from |strings_to_match|.
+ // SubstringSetMatcher doesn't own its strings.
+ for (size_t i = 0; i < strings_to_match.size(); ++i) {
+ substring_patterns_.push_back(
+ new StringPattern(strings_to_match[i], i));
+ }
+ substring_matcher_->RegisterPatterns(substring_patterns_);
+}
+
+void RegexSetMatcher::DeleteSubstringPatterns() {
+ STLDeleteElements(&substring_patterns_);
+}
+
+} // namespace extensions
diff --git a/chromium/extensions/common/matcher/regex_set_matcher.h b/chromium/extensions/common/matcher/regex_set_matcher.h
new file mode 100644
index 00000000000..906ab9ca3ee
--- /dev/null
+++ b/chromium/extensions/common/matcher/regex_set_matcher.h
@@ -0,0 +1,82 @@
+// 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.
+
+#ifndef EXTENSIONS_COMMON_MATCHER_REGEX_SET_MATCHER_H_
+#define EXTENSIONS_COMMON_MATCHER_REGEX_SET_MATCHER_H_
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "extensions/common/matcher/string_pattern.h"
+#include "extensions/common/matcher/substring_set_matcher.h"
+
+namespace re2 {
+class FilteredRE2;
+}
+
+namespace extensions {
+
+// Efficiently matches URLs against a collection of regular expressions,
+// using FilteredRE2 to reduce the number of regexes that must be matched
+// by pre-filtering with substring matching. See:
+// http://swtch.com/~rsc/regexp/regexp3.html#analysis
+class RegexSetMatcher {
+ public:
+ RegexSetMatcher();
+ virtual ~RegexSetMatcher();
+
+ // Adds the regex patterns in |regex_list| to the matcher. Also rebuilds
+ // the FilteredRE2 matcher; thus, for efficiency, prefer adding multiple
+ // patterns at once.
+ // Ownership of the patterns remains with the caller.
+ void AddPatterns(const std::vector<const StringPattern*>& regex_list);
+
+ // Removes all regex patterns.
+ void ClearPatterns();
+
+ // Appends the IDs of regular expressions in our set that match the |text|
+ // to |matches|.
+ bool Match(const std::string& text,
+ std::set<StringPattern::ID>* matches) const;
+
+ bool IsEmpty() const;
+
+ private:
+ typedef int RE2ID;
+ typedef std::map<StringPattern::ID, const StringPattern*> RegexMap;
+ typedef std::vector<StringPattern::ID> RE2IDMap;
+
+ // Use Aho-Corasick SubstringSetMatcher to find which literal patterns
+ // match the |text|.
+ std::vector<RE2ID> FindSubstringMatches(const std::string& text) const;
+
+ // Rebuild FilteredRE2 from scratch. Needs to be called whenever
+ // our set of regexes changes.
+ // TODO(yoz): investigate if it could be done incrementally;
+ // apparently not supported by FilteredRE2.
+ void RebuildMatcher();
+
+ // Clean up StringPatterns in |substring_patterns_|.
+ void DeleteSubstringPatterns();
+
+ // Mapping of regex StringPattern::IDs to regexes.
+ RegexMap regexes_;
+ // Mapping of RE2IDs from FilteredRE2 (which are assigned in order)
+ // to regex StringPattern::IDs.
+ RE2IDMap re2_id_map_;
+
+ scoped_ptr<re2::FilteredRE2> filtered_re2_;
+ scoped_ptr<SubstringSetMatcher> substring_matcher_;
+
+ // The substring patterns from FilteredRE2, which are used in
+ // |substring_matcher_| but whose lifetime is managed here.
+ std::vector<const StringPattern*> substring_patterns_;
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_MATCHER_REGEX_SET_MATCHER_H_
diff --git a/chromium/extensions/common/matcher/regex_set_matcher_unittest.cc b/chromium/extensions/common/matcher/regex_set_matcher_unittest.cc
new file mode 100644
index 00000000000..49d312a1401
--- /dev/null
+++ b/chromium/extensions/common/matcher/regex_set_matcher_unittest.cc
@@ -0,0 +1,61 @@
+// 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.
+
+#include "extensions/common/matcher/regex_set_matcher.h"
+
+#include <set>
+
+#include "base/stl_util.h"
+#include "url/gurl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using extensions::StringPattern;
+using extensions::RegexSetMatcher;
+
+TEST(RegexSetMatcherTest, MatchRegexes) {
+ StringPattern pattern_1("ab.*c", 42);
+ StringPattern pattern_2("f*f", 17);
+ StringPattern pattern_3("c(ar|ra)b|brac", 239);
+ std::vector<const StringPattern*> regexes;
+ regexes.push_back(&pattern_1);
+ regexes.push_back(&pattern_2);
+ regexes.push_back(&pattern_3);
+ RegexSetMatcher matcher;
+ matcher.AddPatterns(regexes);
+
+ std::set<StringPattern::ID> result1;
+ matcher.Match("http://abracadabra.com", &result1);
+ EXPECT_EQ(2U, result1.size());
+ EXPECT_TRUE(ContainsKey(result1, 42));
+ EXPECT_TRUE(ContainsKey(result1, 239));
+
+ std::set<StringPattern::ID> result2;
+ matcher.Match("https://abfffffffffffffffffffffffffffffff.fi/cf", &result2);
+ EXPECT_EQ(2U, result2.size());
+ EXPECT_TRUE(ContainsKey(result2, 17));
+ EXPECT_TRUE(ContainsKey(result2, 42));
+
+ std::set<StringPattern::ID> result3;
+ matcher.Match("http://nothing.com/", &result3);
+ EXPECT_EQ(0U, result3.size());
+}
+
+TEST(RegexSetMatcherTest, CaseSensitivity) {
+ StringPattern pattern_1("AAA", 51);
+ StringPattern pattern_2("aaA", 57);
+ std::vector<const StringPattern*> regexes;
+ regexes.push_back(&pattern_1);
+ regexes.push_back(&pattern_2);
+ RegexSetMatcher matcher;
+ matcher.AddPatterns(regexes);
+
+ std::set<StringPattern::ID> result1;
+ matcher.Match("http://aaa.net/", &result1);
+ EXPECT_EQ(0U, result1.size());
+
+ std::set<StringPattern::ID> result2;
+ matcher.Match("http://aaa.net/quaaACK", &result2);
+ EXPECT_EQ(1U, result2.size());
+ EXPECT_TRUE(ContainsKey(result2, 57));
+}
diff --git a/chromium/extensions/common/matcher/string_pattern.cc b/chromium/extensions/common/matcher/string_pattern.cc
new file mode 100644
index 00000000000..4e5c350c45a
--- /dev/null
+++ b/chromium/extensions/common/matcher/string_pattern.cc
@@ -0,0 +1,20 @@
+// 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.
+
+#include "extensions/common/matcher/string_pattern.h"
+
+namespace extensions {
+
+StringPattern::StringPattern(const std::string& pattern,
+ StringPattern::ID id)
+ : pattern_(pattern), id_(id) {}
+
+StringPattern::~StringPattern() {}
+
+bool StringPattern::operator<(const StringPattern& rhs) const {
+ if (id_ != rhs.id_) return id_ < rhs.id_;
+ return pattern_ < rhs.pattern_;
+}
+
+} // namespace extensions
diff --git a/chromium/extensions/common/matcher/string_pattern.h b/chromium/extensions/common/matcher/string_pattern.h
new file mode 100644
index 00000000000..1781b032833
--- /dev/null
+++ b/chromium/extensions/common/matcher/string_pattern.h
@@ -0,0 +1,42 @@
+// 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.
+
+#ifndef EXTENSIONS_COMMON_MATCHER_STRING_PATTERN_H_
+#define EXTENSIONS_COMMON_MATCHER_STRING_PATTERN_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+
+namespace extensions {
+
+// An individual pattern of a substring or regex matcher. A pattern consists of
+// a string (interpreted as individual bytes, no character encoding) and an
+// identifier.
+// IDs are returned to the caller of SubstringSetMatcher::Match() or
+// RegexMatcher::MatchURL() to help the caller to figure out what
+// patterns matched a string. All patterns registered to a matcher
+// need to contain unique IDs.
+class StringPattern {
+ public:
+ typedef int ID;
+
+ StringPattern(const std::string& pattern, ID id);
+ ~StringPattern();
+ const std::string& pattern() const { return pattern_; }
+ ID id() const { return id_; }
+
+ bool operator<(const StringPattern& rhs) const;
+
+ private:
+ std::string pattern_;
+ ID id_;
+
+ DISALLOW_COPY_AND_ASSIGN(StringPattern);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_MATCHER_STRING_PATTERN_H_
diff --git a/chromium/extensions/common/matcher/string_pattern_unittest.cc b/chromium/extensions/common/matcher/string_pattern_unittest.cc
new file mode 100644
index 00000000000..6e7e4bfbcfe
--- /dev/null
+++ b/chromium/extensions/common/matcher/string_pattern_unittest.cc
@@ -0,0 +1,23 @@
+// 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.
+
+#include "extensions/common/matcher/string_pattern.h"
+
+#include <string>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+using extensions::StringPattern;
+
+TEST(StringPatternTest, StringPattern) {
+ StringPattern r1("Test", 2);
+ EXPECT_EQ("Test", r1.pattern());
+ EXPECT_EQ(2, r1.id());
+
+ EXPECT_FALSE(r1 < r1);
+ StringPattern r2("Test", 3);
+ EXPECT_TRUE(r1 < r2);
+ StringPattern r3("ZZZZ", 2);
+ EXPECT_TRUE(r1 < r3);
+}
diff --git a/chromium/extensions/common/matcher/substring_set_matcher.cc b/chromium/extensions/common/matcher/substring_set_matcher.cc
new file mode 100644
index 00000000000..91ac718b04c
--- /dev/null
+++ b/chromium/extensions/common/matcher/substring_set_matcher.cc
@@ -0,0 +1,272 @@
+// 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.
+
+#include "extensions/common/matcher/substring_set_matcher.h"
+
+#include <algorithm>
+#include <queue>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+
+namespace extensions {
+
+namespace {
+
+// Compare StringPattern instances based on their string patterns.
+bool ComparePatterns(const StringPattern* a, const StringPattern* b) {
+ return a->pattern() < b->pattern();
+}
+
+// Given the set of patterns, compute how many nodes will the corresponding
+// Aho-Corasick tree have. Note that |patterns| need to be sorted.
+uint32 TreeSize(const std::vector<const StringPattern*>& patterns) {
+ uint32 result = 1u; // 1 for the root node.
+ if (patterns.empty())
+ return result;
+
+ std::vector<const StringPattern*>::const_iterator last = patterns.begin();
+ std::vector<const StringPattern*>::const_iterator current = last + 1;
+ // For the first pattern, each letter is a label of an edge to a new node.
+ result += (*last)->pattern().size();
+
+ // For the subsequent patterns, only count the edges which were not counted
+ // yet. For this it suffices to test against the previous pattern, because the
+ // patterns are sorted.
+ for (; current != patterns.end(); ++last, ++current) {
+ const std::string& last_pattern = (*last)->pattern();
+ const std::string& current_pattern = (*current)->pattern();
+ const uint32 prefix_bound =
+ std::min(last_pattern.size(), current_pattern.size());
+
+ uint32 common_prefix = 0;
+ while (common_prefix < prefix_bound &&
+ last_pattern[common_prefix] == current_pattern[common_prefix])
+ ++common_prefix;
+ result += current_pattern.size() - common_prefix;
+ }
+ return result;
+}
+
+} // namespace
+
+//
+// SubstringSetMatcher
+//
+
+SubstringSetMatcher::SubstringSetMatcher() {
+ RebuildAhoCorasickTree(SubstringPatternVector());
+}
+
+SubstringSetMatcher::~SubstringSetMatcher() {}
+
+void SubstringSetMatcher::RegisterPatterns(
+ const std::vector<const StringPattern*>& patterns) {
+ RegisterAndUnregisterPatterns(patterns,
+ std::vector<const StringPattern*>());
+}
+
+void SubstringSetMatcher::UnregisterPatterns(
+ const std::vector<const StringPattern*>& patterns) {
+ RegisterAndUnregisterPatterns(std::vector<const StringPattern*>(),
+ patterns);
+}
+
+void SubstringSetMatcher::RegisterAndUnregisterPatterns(
+ const std::vector<const StringPattern*>& to_register,
+ const std::vector<const StringPattern*>& to_unregister) {
+ // Register patterns.
+ for (std::vector<const StringPattern*>::const_iterator i =
+ to_register.begin(); i != to_register.end(); ++i) {
+ DCHECK(patterns_.find((*i)->id()) == patterns_.end());
+ patterns_[(*i)->id()] = *i;
+ }
+
+ // Unregister patterns
+ for (std::vector<const StringPattern*>::const_iterator i =
+ to_unregister.begin(); i != to_unregister.end(); ++i) {
+ patterns_.erase((*i)->id());
+ }
+
+ // Now we compute the total number of tree nodes needed.
+ SubstringPatternVector sorted_patterns;
+ sorted_patterns.resize(patterns_.size());
+
+ size_t next = 0;
+ for (SubstringPatternMap::const_iterator i = patterns_.begin();
+ i != patterns_.end();
+ ++i, ++next) {
+ sorted_patterns[next] = i->second;
+ }
+
+ std::sort(sorted_patterns.begin(), sorted_patterns.end(), ComparePatterns);
+ tree_.reserve(TreeSize(sorted_patterns));
+
+ RebuildAhoCorasickTree(sorted_patterns);
+}
+
+bool SubstringSetMatcher::Match(const std::string& text,
+ std::set<StringPattern::ID>* matches) const {
+ const size_t old_number_of_matches = matches->size();
+
+ // Handle patterns matching the empty string.
+ matches->insert(tree_[0].matches().begin(), tree_[0].matches().end());
+
+ uint32 current_node = 0;
+ for (std::string::const_iterator i = text.begin(); i != text.end(); ++i) {
+ uint32 edge_from_current = tree_[current_node].GetEdge(*i);
+ while (edge_from_current == AhoCorasickNode::kNoSuchEdge &&
+ current_node != 0) {
+ current_node = tree_[current_node].failure();
+ edge_from_current = tree_[current_node].GetEdge(*i);
+ }
+ if (edge_from_current != AhoCorasickNode::kNoSuchEdge) {
+ current_node = edge_from_current;
+ matches->insert(tree_[current_node].matches().begin(),
+ tree_[current_node].matches().end());
+ } else {
+ DCHECK_EQ(0u, current_node);
+ }
+ }
+
+ return old_number_of_matches != matches->size();
+}
+
+bool SubstringSetMatcher::IsEmpty() const {
+ // An empty tree consists of only the root node.
+ return patterns_.empty() && tree_.size() == 1u;
+}
+
+void SubstringSetMatcher::RebuildAhoCorasickTree(
+ const SubstringPatternVector& sorted_patterns) {
+ tree_.clear();
+
+ // Initialize root note of tree.
+ AhoCorasickNode root;
+ root.set_failure(0);
+ tree_.push_back(root);
+
+ // Insert all patterns.
+ for (SubstringPatternVector::const_iterator i = sorted_patterns.begin();
+ i != sorted_patterns.end();
+ ++i) {
+ InsertPatternIntoAhoCorasickTree(*i);
+ }
+
+ CreateFailureEdges();
+}
+
+void SubstringSetMatcher::InsertPatternIntoAhoCorasickTree(
+ const StringPattern* pattern) {
+ const std::string& text = pattern->pattern();
+ const std::string::const_iterator text_end = text.end();
+
+ // Iterators on the tree and the text.
+ uint32 current_node = 0;
+ std::string::const_iterator i = text.begin();
+
+ // Follow existing paths for as long as possible.
+ while (i != text_end) {
+ uint32 edge_from_current = tree_[current_node].GetEdge(*i);
+ if (edge_from_current == AhoCorasickNode::kNoSuchEdge)
+ break;
+ current_node = edge_from_current;
+ ++i;
+ }
+
+ // Create new nodes if necessary.
+ while (i != text_end) {
+ tree_.push_back(AhoCorasickNode());
+ tree_[current_node].SetEdge(*i, tree_.size() - 1);
+ current_node = tree_.size() - 1;
+ ++i;
+ }
+
+ // Register match.
+ tree_[current_node].AddMatch(pattern->id());
+}
+
+void SubstringSetMatcher::CreateFailureEdges() {
+ typedef AhoCorasickNode::Edges Edges;
+
+ std::queue<uint32> queue;
+
+ AhoCorasickNode& root = tree_[0];
+ root.set_failure(0);
+ const Edges& root_edges = root.edges();
+ for (Edges::const_iterator e = root_edges.begin(); e != root_edges.end();
+ ++e) {
+ const uint32& leads_to = e->second;
+ tree_[leads_to].set_failure(0);
+ queue.push(leads_to);
+ }
+
+ while (!queue.empty()) {
+ AhoCorasickNode& current_node = tree_[queue.front()];
+ queue.pop();
+ for (Edges::const_iterator e = current_node.edges().begin();
+ e != current_node.edges().end(); ++e) {
+ const char& edge_label = e->first;
+ const uint32& leads_to = e->second;
+ queue.push(leads_to);
+
+ uint32 failure = current_node.failure();
+ uint32 edge_from_failure = tree_[failure].GetEdge(edge_label);
+ while (edge_from_failure == AhoCorasickNode::kNoSuchEdge &&
+ failure != 0) {
+ failure = tree_[failure].failure();
+ edge_from_failure = tree_[failure].GetEdge(edge_label);
+ }
+
+ const uint32 follow_in_case_of_failure =
+ edge_from_failure != AhoCorasickNode::kNoSuchEdge
+ ? edge_from_failure
+ : 0;
+ tree_[leads_to].set_failure(follow_in_case_of_failure);
+ tree_[leads_to].AddMatches(tree_[follow_in_case_of_failure].matches());
+ }
+ }
+}
+
+const uint32 SubstringSetMatcher::AhoCorasickNode::kNoSuchEdge = ~0;
+
+SubstringSetMatcher::AhoCorasickNode::AhoCorasickNode()
+ : failure_(kNoSuchEdge) {}
+
+SubstringSetMatcher::AhoCorasickNode::~AhoCorasickNode() {}
+
+SubstringSetMatcher::AhoCorasickNode::AhoCorasickNode(
+ const SubstringSetMatcher::AhoCorasickNode& other)
+ : edges_(other.edges_),
+ failure_(other.failure_),
+ matches_(other.matches_) {}
+
+SubstringSetMatcher::AhoCorasickNode&
+SubstringSetMatcher::AhoCorasickNode::operator=(
+ const SubstringSetMatcher::AhoCorasickNode& other) {
+ edges_ = other.edges_;
+ failure_ = other.failure_;
+ matches_ = other.matches_;
+ return *this;
+}
+
+uint32 SubstringSetMatcher::AhoCorasickNode::GetEdge(char c) const {
+ Edges::const_iterator i = edges_.find(c);
+ return i == edges_.end() ? kNoSuchEdge : i->second;
+}
+
+void SubstringSetMatcher::AhoCorasickNode::SetEdge(char c, uint32 node) {
+ edges_[c] = node;
+}
+
+void SubstringSetMatcher::AhoCorasickNode::AddMatch(StringPattern::ID id) {
+ matches_.insert(id);
+}
+
+void SubstringSetMatcher::AhoCorasickNode::AddMatches(
+ const SubstringSetMatcher::AhoCorasickNode::Matches& matches) {
+ matches_.insert(matches.begin(), matches.end());
+}
+
+} // namespace extensions
diff --git a/chromium/extensions/common/matcher/substring_set_matcher.h b/chromium/extensions/common/matcher/substring_set_matcher.h
new file mode 100644
index 00000000000..610efc0fc22
--- /dev/null
+++ b/chromium/extensions/common/matcher/substring_set_matcher.h
@@ -0,0 +1,140 @@
+// 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.
+
+#ifndef EXTENSIONS_COMMON_MATCHER_SUBSTRING_SET_MATCHER_H_
+#define EXTENSIONS_COMMON_MATCHER_SUBSTRING_SET_MATCHER_H_
+
+#include <limits>
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "extensions/common/matcher/string_pattern.h"
+
+namespace extensions {
+
+// Class that store a set of string patterns and can find for a string S,
+// which string patterns occur in S.
+class SubstringSetMatcher {
+ public:
+ SubstringSetMatcher();
+ ~SubstringSetMatcher();
+
+ // Registers all |patterns|. The ownership remains with the caller.
+ // The same pattern cannot be registered twice and each pattern needs to have
+ // a unique ID.
+ // Ownership of the patterns remains with the caller.
+ void RegisterPatterns(const std::vector<const StringPattern*>& patterns);
+
+ // Unregisters the passed |patterns|.
+ void UnregisterPatterns(const std::vector<const StringPattern*>& patterns);
+
+ // Analogous to RegisterPatterns and UnregisterPatterns but executes both
+ // operations in one step, which is cheaper in the execution.
+ void RegisterAndUnregisterPatterns(
+ const std::vector<const StringPattern*>& to_register,
+ const std::vector<const StringPattern*>& to_unregister);
+
+ // Matches |text| against all registered StringPatterns. Stores the IDs
+ // of matching patterns in |matches|. |matches| is not cleared before adding
+ // to it.
+ bool Match(const std::string& text,
+ std::set<StringPattern::ID>* matches) const;
+
+ // Returns true if this object retains no allocated data. Only for debugging.
+ bool IsEmpty() const;
+
+ private:
+ // A node of an Aho Corasick Tree. This is implemented according to
+ // http://www.cs.uku.fi/~kilpelai/BSA05/lectures/slides04.pdf
+ //
+ // The algorithm is based on the idea of building a trie of all registered
+ // patterns. Each node of the tree is annotated with a set of pattern
+ // IDs that are used to report matches.
+ //
+ // The root of the trie represents an empty match. If we were looking whether
+ // any registered pattern matches a text at the beginning of the text (i.e.
+ // whether any pattern is a prefix of the text), we could just follow
+ // nodes in the trie according to the matching characters in the text.
+ // E.g., if text == "foobar", we would follow the trie from the root node
+ // to its child labeled 'f', from there to child 'o', etc. In this process we
+ // would report all pattern IDs associated with the trie nodes as matches.
+ //
+ // As we are not looking for all prefix matches but all substring matches,
+ // this algorithm would need to compare text.substr(0), text.substr(1), ...
+ // against the trie, which is in O(|text|^2).
+ //
+ // The Aho Corasick algorithm improves this runtime by using failure edges.
+ // In case we have found a partial match of length k in the text
+ // (text[i, ..., i + k - 1]) in the trie starting at the root and ending at
+ // a node at depth k, but cannot find a match in the trie for character
+ // text[i + k] at depth k + 1, we follow a failure edge. This edge
+ // corresponds to the longest proper suffix of text[i, ..., i + k - 1] that
+ // is a prefix of any registered pattern.
+ //
+ // If your brain thinks "Forget it, let's go shopping.", don't worry.
+ // Take a nap and read an introductory text on the Aho Corasick algorithm.
+ // It will make sense. Eventually.
+ class AhoCorasickNode {
+ public:
+ // Key: label of the edge, value: node index in |tree_| of parent class.
+ typedef std::map<char, uint32> Edges;
+ typedef std::set<StringPattern::ID> Matches;
+
+ static const uint32 kNoSuchEdge; // Represents an invalid node index.
+
+ AhoCorasickNode();
+ ~AhoCorasickNode();
+ AhoCorasickNode(const AhoCorasickNode& other);
+ AhoCorasickNode& operator=(const AhoCorasickNode& other);
+
+ uint32 GetEdge(char c) const;
+ void SetEdge(char c, uint32 node);
+ const Edges& edges() const { return edges_; }
+
+ uint32 failure() const { return failure_; }
+ void set_failure(uint32 failure) { failure_ = failure; }
+
+ void AddMatch(StringPattern::ID id);
+ void AddMatches(const Matches& matches);
+ const Matches& matches() const { return matches_; }
+
+ private:
+ // Outgoing edges of current node.
+ Edges edges_;
+
+ // Node index that failure edge leads to.
+ uint32 failure_;
+
+ // Identifiers of matches.
+ Matches matches_;
+ };
+
+ typedef std::map<StringPattern::ID, const StringPattern*> SubstringPatternMap;
+ typedef std::vector<const StringPattern*> SubstringPatternVector;
+
+ // |sorted_patterns| is a copy of |patterns_| sorted by the pattern string.
+ void RebuildAhoCorasickTree(const SubstringPatternVector& sorted_patterns);
+
+ // Inserts a path for |pattern->pattern()| into the tree and adds
+ // |pattern->id()| to the set of matches. Ownership of |pattern| remains with
+ // the caller.
+ void InsertPatternIntoAhoCorasickTree(const StringPattern* pattern);
+ void CreateFailureEdges();
+
+ // Set of all registered StringPatterns. Used to regenerate the
+ // Aho-Corasick tree in case patterns are registered or unregistered.
+ SubstringPatternMap patterns_;
+
+ // The nodes of a Aho-Corasick tree.
+ std::vector<AhoCorasickNode> tree_;
+
+ DISALLOW_COPY_AND_ASSIGN(SubstringSetMatcher);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_MATCHER_SUBSTRING_SET_MATCHER_H_
diff --git a/chromium/extensions/common/matcher/substring_set_matcher_unittest.cc b/chromium/extensions/common/matcher/substring_set_matcher_unittest.cc
new file mode 100644
index 00000000000..fde65bf2dca
--- /dev/null
+++ b/chromium/extensions/common/matcher/substring_set_matcher_unittest.cc
@@ -0,0 +1,167 @@
+// 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.
+
+#include "extensions/common/matcher/substring_set_matcher.h"
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+using extensions::StringPattern;
+using extensions::SubstringSetMatcher;
+
+namespace {
+void TestOnePattern(const std::string& test_string,
+ const std::string& pattern,
+ bool is_match) {
+ std::string test =
+ "TestOnePattern(" + test_string + ", " + pattern + ", " +
+ (is_match ? "1" : "0") + ")";
+ std::vector<const StringPattern*> patterns;
+ StringPattern substring_pattern(pattern, 1);
+ patterns.push_back(&substring_pattern);
+ SubstringSetMatcher matcher;
+ matcher.RegisterPatterns(patterns);
+ std::set<int> matches;
+ matcher.Match(test_string, &matches);
+
+ size_t expected_matches = (is_match ? 1 : 0);
+ EXPECT_EQ(expected_matches, matches.size()) << test;
+ EXPECT_EQ(is_match, matches.find(1) != matches.end()) << test;
+}
+
+void TestTwoPatterns(const std::string& test_string,
+ const std::string& pattern_1,
+ const std::string& pattern_2,
+ bool is_match_1,
+ bool is_match_2) {
+ std::string test =
+ "TestTwoPatterns(" + test_string + ", " + pattern_1 + ", " + pattern_2 +
+ ", " + (is_match_1 ? "1" : "0") + ", " + (is_match_2 ? "1" : "0") + ")";
+ StringPattern substring_pattern_1(pattern_1, 1);
+ StringPattern substring_pattern_2(pattern_2, 2);
+ // In order to make sure that the order in which patterns are registered
+ // does not make any difference we try both permutations.
+ for (int permutation = 0; permutation < 2; ++permutation) {
+ std::vector<const StringPattern*> patterns;
+ if (permutation == 0) {
+ patterns.push_back(&substring_pattern_1);
+ patterns.push_back(&substring_pattern_2);
+ } else {
+ patterns.push_back(&substring_pattern_2);
+ patterns.push_back(&substring_pattern_1);
+ }
+ SubstringSetMatcher matcher;
+ matcher.RegisterPatterns(patterns);
+ std::set<int> matches;
+ matcher.Match(test_string, &matches);
+
+ size_t expected_matches = (is_match_1 ? 1 : 0) + (is_match_2 ? 1 : 0);
+ EXPECT_EQ(expected_matches, matches.size()) << test;
+ EXPECT_EQ(is_match_1, matches.find(1) != matches.end()) << test;
+ EXPECT_EQ(is_match_2, matches.find(2) != matches.end()) << test;
+ }
+}
+}
+
+TEST(SubstringSetMatcherTest, TestMatcher) {
+ // Test overlapping patterns
+ // String abcde
+ // Pattern 1 bc
+ // Pattern 2 cd
+ TestTwoPatterns("abcde", "bc", "cd", true, true);
+
+ // Test subpatterns - part 1
+ // String abcde
+ // Pattern 1 bc
+ // Pattern 2 b
+ TestTwoPatterns("abcde", "bc", "b", true, true);
+
+ // Test subpatterns - part 2
+ // String abcde
+ // Pattern 1 bc
+ // Pattern 2 c
+ TestTwoPatterns("abcde", "bc", "c", true, true);
+
+ // Test identical matches
+ // String abcde
+ // Pattern 1 abcde
+ TestOnePattern("abcde", "abcde", true);
+
+ // Test multiple matches
+ // String aaaaa
+ // Pattern 1 a
+ TestOnePattern("abcde", "a", true);
+
+ // Test matches at beginning and end
+ // String abcde
+ // Pattern 1 ab
+ // Pattern 2 de
+ TestTwoPatterns("abcde", "ab", "de", true, true);
+
+ // Test duplicate patterns with different IDs
+ // String abcde
+ // Pattern 1 bc
+ // Pattern 2 bc
+ TestTwoPatterns("abcde", "bc", "bc", true, true);
+
+ // Test non-match
+ // String abcde
+ // Pattern 1 fg
+ TestOnePattern("abcde", "fg", false);
+
+ // Test empty pattern and too long pattern
+ // String abcde
+ // Pattern 1
+ // Pattern 2 abcdef
+ TestTwoPatterns("abcde", std::string(), "abcdef", true, false);
+}
+
+TEST(SubstringSetMatcherTest, RegisterAndRemove) {
+ SubstringSetMatcher matcher;
+
+ StringPattern pattern_1("a", 1);
+ StringPattern pattern_2("b", 2);
+ StringPattern pattern_3("c", 3);
+
+ std::vector<const StringPattern*> patterns;
+ patterns.push_back(&pattern_1);
+ matcher.RegisterPatterns(patterns);
+
+ patterns.clear();
+ patterns.push_back(&pattern_2);
+ patterns.push_back(&pattern_3);
+ matcher.RegisterPatterns(patterns);
+
+ std::set<int> matches;
+ matcher.Match("abd", &matches);
+ EXPECT_EQ(2u, matches.size());
+ EXPECT_TRUE(matches.end() != matches.find(1));
+ EXPECT_TRUE(matches.end() != matches.find(2));
+
+ patterns.clear();
+ patterns.push_back(&pattern_2);
+ matcher.UnregisterPatterns(patterns);
+
+ matches.clear();
+ matcher.Match("abd", &matches);
+ EXPECT_EQ(1u, matches.size());
+ EXPECT_TRUE(matches.end() != matches.find(1));
+ EXPECT_TRUE(matches.end() == matches.find(2));
+
+ patterns.clear();
+ patterns.push_back(&pattern_1);
+ patterns.push_back(&pattern_3);
+ matcher.UnregisterPatterns(patterns);
+ EXPECT_TRUE(matcher.IsEmpty());
+}
+
+TEST(SubstringSetMatcherTest, TestEmptyMatcher) {
+ SubstringSetMatcher matcher;
+ std::set<int> matches;
+ matcher.Match("abd", &matches);
+ EXPECT_TRUE(matches.empty());
+}
diff --git a/chromium/extensions/common/matcher/url_matcher.cc b/chromium/extensions/common/matcher/url_matcher.cc
new file mode 100644
index 00000000000..be3057a26b5
--- /dev/null
+++ b/chromium/extensions/common/matcher/url_matcher.cc
@@ -0,0 +1,885 @@
+// 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.
+
+#include "extensions/common/matcher/url_matcher.h"
+
+#include <algorithm>
+#include <iterator>
+
+#include "base/logging.h"
+#include "content/public/common/url_constants.h"
+#include "url/gurl.h"
+#include "url/url_canon.h"
+
+namespace extensions {
+
+// This set of classes implement a mapping of URL Component Patterns, such as
+// host_prefix, host_suffix, host_equals, ..., etc., to StringPatterns
+// for use in substring comparisons.
+//
+// The idea of this mapping is to reduce the problem of comparing many
+// URL Component Patterns against one URL to the problem of searching many
+// substrings in one string:
+//
+// ---------------------- -----------------
+// | URL Query operator | ----translate----> | StringPattern |
+// ---------------------- -----------------
+// ^
+// |
+// compare
+// |
+// v
+// ---------------------- -----------------
+// | URL to compare | | |
+// | to all URL Query | ----translate----> | String |
+// | operators | | |
+// ---------------------- -----------------
+//
+// The reason for this problem reduction is that there are efficient algorithms
+// for searching many substrings in one string (see Aho-Corasick algorithm).
+//
+// Additionally, some of the same pieces are reused to implement regular
+// expression comparisons. The FilteredRE2 implementation for matching many
+// regular expressions against one string uses prefiltering, in which a set
+// of substrings (derived from the regexes) are first searched for, to reduce
+// the number of regular expressions to test; the prefiltering step also
+// uses Aho-Corasick.
+//
+// Case 1: {host,path,query}_{prefix,suffix,equals} searches.
+// ==========================================================
+//
+// For searches in this class, we normalize URLs as follows:
+//
+// Step 1:
+// Remove scheme, port and segment from URL:
+// -> http://www.example.com:8080/index.html?search=foo#first_match becomes
+// www.example.com/index.html?search=foo
+//
+// We remove the scheme and port number because they can be checked later
+// in a secondary filter step. We remove the segment (the #... part) because
+// this is not guaranteed to be ASCII-7 encoded.
+//
+// Step 2:
+// Translate URL to String and add the following position markers:
+// - BU = Beginning of URL
+// - ED = End of Domain
+// - EP = End of Path
+// - EU = End of URL
+// Furthermore, the hostname is canonicalized to start with a ".".
+//
+// Position markers are represented as characters >127, which are therefore
+// guaranteed not to be part of the ASCII-7 encoded URL character set.
+//
+// -> www.example.com/index.html?search=foo becomes
+// BU .www.example.com ED /index.html EP ?search=foo EU
+//
+// -> www.example.com/index.html becomes
+// BU .www.example.com ED /index.html EP EU
+//
+// Step 3:
+// Translate URL Component Patterns as follows:
+//
+// host_prefix(prefix) = BU add_missing_dot_prefix(prefix)
+// -> host_prefix("www.example") = BU .www.example
+//
+// host_suffix(suffix) = suffix ED
+// -> host_suffix("example.com") = example.com ED
+// -> host_suffix(".example.com") = .example.com ED
+//
+// host_equals(domain) = BU add_missing_dot_prefix(domain) ED
+// -> host_equals("www.example.com") = BU .www.example.com ED
+//
+// Similarly for path query parameters ({path, query}_{prefix, suffix, equals}).
+//
+// With this, we can search the StringPatterns in the normalized URL.
+//
+//
+// Case 2: url_{prefix,suffix,equals,contains} searches.
+// =====================================================
+//
+// Step 1: as above, except that
+// - the scheme is not removed
+// - the port is not removed if it is specified and does not match the default
+// port for the given scheme.
+//
+// Step 2:
+// Translate URL to String and add the following position markers:
+// - BU = Beginning of URL
+// - EU = End of URL
+//
+// -> http://www.example.com:8080/index.html?search=foo#first_match becomes
+// BU http://www.example.com:8080/index.html?search=foo EU
+// -> http://www.example.com:80/index.html?search=foo#first_match becomes
+// BU http://www.example.com/index.html?search=foo EU
+//
+// url_prefix(prefix) = BU prefix
+// -> url_prefix("http://www.example") = BU http://www.example
+//
+// url_contains(substring) = substring
+// -> url_contains("index") = index
+//
+//
+// Case 3: {host,path,query}_contains searches.
+// ============================================
+//
+// These kinds of searches are not supported directly but can be derived
+// by a combination of a url_contains() query followed by an explicit test:
+//
+// host_contains(str) = url_contains(str) followed by test whether str occurs
+// in host component of original URL.
+// -> host_contains("example.co") = example.co
+// followed by gurl.host().find("example.co");
+//
+// [similarly for path_contains and query_contains].
+//
+//
+// Regular expression matching (url_matches searches)
+// ==================================================
+//
+// This class also supports matching regular expressions (RE2 syntax)
+// against full URLs, which are transformed as in case 2.
+
+namespace {
+
+bool IsRegexCriterion(URLMatcherCondition::Criterion criterion) {
+ return criterion == URLMatcherCondition::URL_MATCHES;
+}
+
+bool IsOriginAndPathRegexCriterion(URLMatcherCondition::Criterion criterion) {
+ return criterion == URLMatcherCondition::ORIGIN_AND_PATH_MATCHES;
+}
+
+} // namespace
+
+//
+// URLMatcherCondition
+//
+
+URLMatcherCondition::URLMatcherCondition()
+ : criterion_(HOST_PREFIX),
+ string_pattern_(NULL) {}
+
+URLMatcherCondition::~URLMatcherCondition() {}
+
+URLMatcherCondition::URLMatcherCondition(
+ Criterion criterion,
+ const StringPattern* string_pattern)
+ : criterion_(criterion),
+ string_pattern_(string_pattern) {}
+
+URLMatcherCondition::URLMatcherCondition(const URLMatcherCondition& rhs)
+ : criterion_(rhs.criterion_),
+ string_pattern_(rhs.string_pattern_) {}
+
+URLMatcherCondition& URLMatcherCondition::operator=(
+ const URLMatcherCondition& rhs) {
+ criterion_ = rhs.criterion_;
+ string_pattern_ = rhs.string_pattern_;
+ return *this;
+}
+
+bool URLMatcherCondition::operator<(const URLMatcherCondition& rhs) const {
+ if (criterion_ < rhs.criterion_) return true;
+ if (criterion_ > rhs.criterion_) return false;
+ if (string_pattern_ != NULL && rhs.string_pattern_ != NULL)
+ return *string_pattern_ < *rhs.string_pattern_;
+ if (string_pattern_ == NULL && rhs.string_pattern_ != NULL) return true;
+ // Either string_pattern_ != NULL && rhs.string_pattern_ == NULL,
+ // or both are NULL.
+ return false;
+}
+
+bool URLMatcherCondition::IsFullURLCondition() const {
+ // For these criteria the SubstringMatcher needs to be executed on the
+ // GURL that is canonicalized with
+ // URLMatcherConditionFactory::CanonicalizeURLForFullSearches.
+ switch (criterion_) {
+ case HOST_CONTAINS:
+ case PATH_CONTAINS:
+ case QUERY_CONTAINS:
+ case URL_PREFIX:
+ case URL_SUFFIX:
+ case URL_CONTAINS:
+ case URL_EQUALS:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+bool URLMatcherCondition::IsRegexCondition() const {
+ return IsRegexCriterion(criterion_);
+}
+
+bool URLMatcherCondition::IsOriginAndPathRegexCondition() const {
+ return IsOriginAndPathRegexCriterion(criterion_);
+}
+
+bool URLMatcherCondition::IsMatch(
+ const std::set<StringPattern::ID>& matching_patterns,
+ const GURL& url) const {
+ DCHECK(string_pattern_);
+ if (!ContainsKey(matching_patterns, string_pattern_->id()))
+ return false;
+ // The criteria HOST_CONTAINS, PATH_CONTAINS, QUERY_CONTAINS are based on
+ // a substring match on the raw URL. In case of a match, we need to verify
+ // that the match was found in the correct component of the URL.
+ switch (criterion_) {
+ case HOST_CONTAINS:
+ return url.host().find(string_pattern_->pattern()) !=
+ std::string::npos;
+ case PATH_CONTAINS:
+ return url.path().find(string_pattern_->pattern()) !=
+ std::string::npos;
+ case QUERY_CONTAINS:
+ return url.query().find(string_pattern_->pattern()) !=
+ std::string::npos;
+ default:
+ break;
+ }
+ return true;
+}
+
+//
+// URLMatcherConditionFactory
+//
+
+namespace {
+// These are symbols that are not contained in 7-bit ASCII used in GURLs.
+const char kBeginningOfURL[] = {static_cast<char>(-1), 0};
+const char kEndOfDomain[] = {static_cast<char>(-2), 0};
+const char kEndOfPath[] = {static_cast<char>(-3), 0};
+const char kEndOfURL[] = {static_cast<char>(-4), 0};
+} // namespace
+
+URLMatcherConditionFactory::URLMatcherConditionFactory() : id_counter_(0) {}
+
+URLMatcherConditionFactory::~URLMatcherConditionFactory() {
+ STLDeleteElements(&substring_pattern_singletons_);
+ STLDeleteElements(&regex_pattern_singletons_);
+ STLDeleteElements(&origin_and_path_regex_pattern_singletons_);
+}
+
+std::string URLMatcherConditionFactory::CanonicalizeURLForComponentSearches(
+ const GURL& url) const {
+ return kBeginningOfURL + CanonicalizeHostname(url.host()) + kEndOfDomain +
+ url.path() + kEndOfPath +
+ (url.has_query() ? "?" + url.query() : std::string()) + kEndOfURL;
+}
+
+URLMatcherCondition URLMatcherConditionFactory::CreateHostPrefixCondition(
+ const std::string& prefix) {
+ return CreateCondition(URLMatcherCondition::HOST_PREFIX,
+ kBeginningOfURL + CanonicalizeHostname(prefix));
+}
+
+URLMatcherCondition URLMatcherConditionFactory::CreateHostSuffixCondition(
+ const std::string& suffix) {
+ return CreateCondition(URLMatcherCondition::HOST_SUFFIX,
+ suffix + kEndOfDomain);
+}
+
+URLMatcherCondition URLMatcherConditionFactory::CreateHostContainsCondition(
+ const std::string& str) {
+ return CreateCondition(URLMatcherCondition::HOST_CONTAINS, str);
+}
+
+URLMatcherCondition URLMatcherConditionFactory::CreateHostEqualsCondition(
+ const std::string& str) {
+ return CreateCondition(URLMatcherCondition::HOST_EQUALS,
+ kBeginningOfURL + CanonicalizeHostname(str) + kEndOfDomain);
+}
+
+URLMatcherCondition URLMatcherConditionFactory::CreatePathPrefixCondition(
+ const std::string& prefix) {
+ return CreateCondition(URLMatcherCondition::PATH_PREFIX,
+ kEndOfDomain + prefix);
+}
+
+URLMatcherCondition URLMatcherConditionFactory::CreatePathSuffixCondition(
+ const std::string& suffix) {
+ return CreateCondition(URLMatcherCondition::PATH_SUFFIX, suffix + kEndOfPath);
+}
+
+URLMatcherCondition URLMatcherConditionFactory::CreatePathContainsCondition(
+ const std::string& str) {
+ return CreateCondition(URLMatcherCondition::PATH_CONTAINS, str);
+}
+
+URLMatcherCondition URLMatcherConditionFactory::CreatePathEqualsCondition(
+ const std::string& str) {
+ return CreateCondition(URLMatcherCondition::PATH_EQUALS,
+ kEndOfDomain + str + kEndOfPath);
+}
+
+URLMatcherCondition URLMatcherConditionFactory::CreateQueryPrefixCondition(
+ const std::string& prefix) {
+ std::string pattern;
+ if (!prefix.empty() && prefix[0] == '?')
+ pattern = kEndOfPath + prefix;
+ else
+ pattern = kEndOfPath + ('?' + prefix);
+
+ return CreateCondition(URLMatcherCondition::QUERY_PREFIX, pattern);
+}
+
+URLMatcherCondition URLMatcherConditionFactory::CreateQuerySuffixCondition(
+ const std::string& suffix) {
+ if (!suffix.empty() && suffix[0] == '?') {
+ return CreateQueryEqualsCondition(suffix);
+ } else {
+ return CreateCondition(URLMatcherCondition::QUERY_SUFFIX,
+ suffix + kEndOfURL);
+ }
+}
+
+URLMatcherCondition URLMatcherConditionFactory::CreateQueryContainsCondition(
+ const std::string& str) {
+ if (!str.empty() && str[0] == '?')
+ return CreateQueryPrefixCondition(str);
+ else
+ return CreateCondition(URLMatcherCondition::QUERY_CONTAINS, str);
+}
+
+URLMatcherCondition URLMatcherConditionFactory::CreateQueryEqualsCondition(
+ const std::string& str) {
+ std::string pattern;
+ if (!str.empty() && str[0] == '?')
+ pattern = kEndOfPath + str + kEndOfURL;
+ else
+ pattern = kEndOfPath + ('?' + str) + kEndOfURL;
+
+ return CreateCondition(URLMatcherCondition::QUERY_EQUALS, pattern);
+}
+
+URLMatcherCondition
+ URLMatcherConditionFactory::CreateHostSuffixPathPrefixCondition(
+ const std::string& host_suffix,
+ const std::string& path_prefix) {
+ return CreateCondition(URLMatcherCondition::HOST_SUFFIX_PATH_PREFIX,
+ host_suffix + kEndOfDomain + path_prefix);
+}
+
+URLMatcherCondition
+URLMatcherConditionFactory::CreateHostEqualsPathPrefixCondition(
+ const std::string& host,
+ const std::string& path_prefix) {
+ return CreateCondition(URLMatcherCondition::HOST_EQUALS_PATH_PREFIX,
+ kBeginningOfURL + CanonicalizeHostname(host) + kEndOfDomain +
+ path_prefix);
+}
+
+std::string URLMatcherConditionFactory::CanonicalizeURLForFullSearches(
+ const GURL& url) const {
+ GURL::Replacements replacements;
+ replacements.ClearPassword();
+ replacements.ClearUsername();
+ replacements.ClearRef();
+ // Clear port if it is implicit from scheme.
+ if (url.has_port()) {
+ const std::string& port = url.scheme();
+ if (url_canon::DefaultPortForScheme(port.c_str(), port.size()) ==
+ url.EffectiveIntPort()) {
+ replacements.ClearPort();
+ }
+ }
+ return kBeginningOfURL + url.ReplaceComponents(replacements).spec() +
+ kEndOfURL;
+}
+
+static std::string CanonicalizeURLForRegexSearchesHelper(
+ const GURL& url,
+ bool clear_query) {
+ GURL::Replacements replacements;
+ replacements.ClearPassword();
+ replacements.ClearUsername();
+ replacements.ClearRef();
+ if (clear_query)
+ replacements.ClearQuery();
+ // Clear port if it is implicit from scheme.
+ if (url.has_port()) {
+ const std::string& port = url.scheme();
+ if (url_canon::DefaultPortForScheme(port.c_str(), port.size()) ==
+ url.EffectiveIntPort()) {
+ replacements.ClearPort();
+ }
+ }
+ return url.ReplaceComponents(replacements).spec();
+}
+
+std::string URLMatcherConditionFactory::CanonicalizeURLForRegexSearches(
+ const GURL& url) const {
+ return CanonicalizeURLForRegexSearchesHelper(url, false);
+}
+
+std::string
+URLMatcherConditionFactory::CanonicalizeURLForOriginAndPathRegexSearches(
+ const GURL& url) const {
+ return CanonicalizeURLForRegexSearchesHelper(url, true);
+}
+
+URLMatcherCondition URLMatcherConditionFactory::CreateURLPrefixCondition(
+ const std::string& prefix) {
+ return CreateCondition(URLMatcherCondition::URL_PREFIX,
+ kBeginningOfURL + prefix);
+}
+
+URLMatcherCondition URLMatcherConditionFactory::CreateURLSuffixCondition(
+ const std::string& suffix) {
+ return CreateCondition(URLMatcherCondition::URL_SUFFIX, suffix + kEndOfURL);
+}
+
+URLMatcherCondition URLMatcherConditionFactory::CreateURLContainsCondition(
+ const std::string& str) {
+ return CreateCondition(URLMatcherCondition::URL_CONTAINS, str);
+}
+
+URLMatcherCondition URLMatcherConditionFactory::CreateURLEqualsCondition(
+ const std::string& str) {
+ return CreateCondition(URLMatcherCondition::URL_EQUALS,
+ kBeginningOfURL + str + kEndOfURL);
+}
+
+URLMatcherCondition URLMatcherConditionFactory::CreateURLMatchesCondition(
+ const std::string& regex) {
+ return CreateCondition(URLMatcherCondition::URL_MATCHES, regex);
+}
+
+URLMatcherCondition
+URLMatcherConditionFactory::CreateOriginAndPathMatchesCondition(
+ const std::string& regex) {
+ return CreateCondition(URLMatcherCondition::ORIGIN_AND_PATH_MATCHES, regex);
+}
+
+void URLMatcherConditionFactory::ForgetUnusedPatterns(
+ const std::set<StringPattern::ID>& used_patterns) {
+ PatternSingletons::iterator i = substring_pattern_singletons_.begin();
+ while (i != substring_pattern_singletons_.end()) {
+ if (ContainsKey(used_patterns, (*i)->id())) {
+ ++i;
+ } else {
+ delete *i;
+ substring_pattern_singletons_.erase(i++);
+ }
+ }
+ i = regex_pattern_singletons_.begin();
+ while (i != regex_pattern_singletons_.end()) {
+ if (ContainsKey(used_patterns, (*i)->id())) {
+ ++i;
+ } else {
+ delete *i;
+ regex_pattern_singletons_.erase(i++);
+ }
+ }
+ i = origin_and_path_regex_pattern_singletons_.begin();
+ while (i != origin_and_path_regex_pattern_singletons_.end()) {
+ if (ContainsKey(used_patterns, (*i)->id())) {
+ ++i;
+ } else {
+ delete *i;
+ origin_and_path_regex_pattern_singletons_.erase(i++);
+ }
+ }
+}
+
+bool URLMatcherConditionFactory::IsEmpty() const {
+ return substring_pattern_singletons_.empty() &&
+ regex_pattern_singletons_.empty() &&
+ origin_and_path_regex_pattern_singletons_.empty();
+}
+
+URLMatcherCondition URLMatcherConditionFactory::CreateCondition(
+ URLMatcherCondition::Criterion criterion,
+ const std::string& pattern) {
+ StringPattern search_pattern(pattern, 0);
+ PatternSingletons* pattern_singletons = NULL;
+ if (IsRegexCriterion(criterion))
+ pattern_singletons = &regex_pattern_singletons_;
+ else if (IsOriginAndPathRegexCriterion(criterion))
+ pattern_singletons = &origin_and_path_regex_pattern_singletons_;
+ else
+ pattern_singletons = &substring_pattern_singletons_;
+
+ PatternSingletons::const_iterator iter =
+ pattern_singletons->find(&search_pattern);
+
+ if (iter != pattern_singletons->end()) {
+ return URLMatcherCondition(criterion, *iter);
+ } else {
+ StringPattern* new_pattern =
+ new StringPattern(pattern, id_counter_++);
+ pattern_singletons->insert(new_pattern);
+ return URLMatcherCondition(criterion, new_pattern);
+ }
+}
+
+std::string URLMatcherConditionFactory::CanonicalizeHostname(
+ const std::string& hostname) const {
+ if (!hostname.empty() && hostname[0] == '.')
+ return hostname;
+ else
+ return "." + hostname;
+}
+
+bool URLMatcherConditionFactory::StringPatternPointerCompare::operator()(
+ StringPattern* lhs,
+ StringPattern* rhs) const {
+ if (lhs == NULL && rhs != NULL) return true;
+ if (lhs != NULL && rhs != NULL)
+ return lhs->pattern() < rhs->pattern();
+ // Either both are NULL or only rhs is NULL.
+ return false;
+}
+
+//
+// URLMatcherSchemeFilter
+//
+
+URLMatcherSchemeFilter::URLMatcherSchemeFilter(const std::string& filter)
+ : filters_(1) {
+ filters_.push_back(filter);
+}
+
+URLMatcherSchemeFilter::URLMatcherSchemeFilter(
+ const std::vector<std::string>& filters)
+ : filters_(filters) {}
+
+URLMatcherSchemeFilter::~URLMatcherSchemeFilter() {}
+
+bool URLMatcherSchemeFilter::IsMatch(const GURL& url) const {
+ return std::find(filters_.begin(), filters_.end(), url.scheme()) !=
+ filters_.end();
+}
+
+//
+// URLMatcherPortFilter
+//
+
+URLMatcherPortFilter::URLMatcherPortFilter(
+ const std::vector<URLMatcherPortFilter::Range>& ranges)
+ : ranges_(ranges) {}
+
+URLMatcherPortFilter::~URLMatcherPortFilter() {}
+
+bool URLMatcherPortFilter::IsMatch(const GURL& url) const {
+ int port = url.EffectiveIntPort();
+ for (std::vector<Range>::const_iterator i = ranges_.begin();
+ i != ranges_.end(); ++i) {
+ if (i->first <= port && port <= i->second)
+ return true;
+ }
+ return false;
+}
+
+// static
+URLMatcherPortFilter::Range URLMatcherPortFilter::CreateRange(int from,
+ int to) {
+ return Range(from, to);
+}
+
+// static
+URLMatcherPortFilter::Range URLMatcherPortFilter::CreateRange(int port) {
+ return Range(port, port);
+}
+
+//
+// URLMatcherConditionSet
+//
+
+URLMatcherConditionSet::~URLMatcherConditionSet() {}
+
+URLMatcherConditionSet::URLMatcherConditionSet(
+ ID id,
+ const Conditions& conditions)
+ : id_(id),
+ conditions_(conditions) {}
+
+URLMatcherConditionSet::URLMatcherConditionSet(
+ ID id,
+ const Conditions& conditions,
+ scoped_ptr<URLMatcherSchemeFilter> scheme_filter,
+ scoped_ptr<URLMatcherPortFilter> port_filter)
+ : id_(id),
+ conditions_(conditions),
+ scheme_filter_(scheme_filter.Pass()),
+ port_filter_(port_filter.Pass()) {}
+
+bool URLMatcherConditionSet::IsMatch(
+ const std::set<StringPattern::ID>& matching_patterns,
+ const GURL& url) const {
+ for (Conditions::const_iterator i = conditions_.begin();
+ i != conditions_.end(); ++i) {
+ if (!i->IsMatch(matching_patterns, url))
+ return false;
+ }
+ if (scheme_filter_.get() && !scheme_filter_->IsMatch(url))
+ return false;
+ if (port_filter_.get() && !port_filter_->IsMatch(url))
+ return false;
+ return true;
+}
+
+//
+// URLMatcher
+//
+
+URLMatcher::URLMatcher() {}
+
+URLMatcher::~URLMatcher() {}
+
+void URLMatcher::AddConditionSets(
+ const URLMatcherConditionSet::Vector& condition_sets) {
+ for (URLMatcherConditionSet::Vector::const_iterator i =
+ condition_sets.begin(); i != condition_sets.end(); ++i) {
+ DCHECK(url_matcher_condition_sets_.find((*i)->id()) ==
+ url_matcher_condition_sets_.end());
+ url_matcher_condition_sets_[(*i)->id()] = *i;
+ }
+ UpdateInternalDatastructures();
+}
+
+void URLMatcher::RemoveConditionSets(
+ const std::vector<URLMatcherConditionSet::ID>& condition_set_ids) {
+ for (std::vector<URLMatcherConditionSet::ID>::const_iterator i =
+ condition_set_ids.begin(); i != condition_set_ids.end(); ++i) {
+ DCHECK(url_matcher_condition_sets_.find(*i) !=
+ url_matcher_condition_sets_.end());
+ url_matcher_condition_sets_.erase(*i);
+ }
+ UpdateInternalDatastructures();
+}
+
+void URLMatcher::ClearUnusedConditionSets() {
+ UpdateConditionFactory();
+}
+
+std::set<URLMatcherConditionSet::ID> URLMatcher::MatchURL(
+ const GURL& url) const {
+ // Find all IDs of StringPatterns that match |url|.
+ // See URLMatcherConditionFactory for the canonicalization of URLs and the
+ // distinction between full url searches and url component searches.
+ std::set<StringPattern::ID> matches;
+ if (!full_url_matcher_.IsEmpty()) {
+ full_url_matcher_.Match(
+ condition_factory_.CanonicalizeURLForFullSearches(url), &matches);
+ }
+ if (!url_component_matcher_.IsEmpty()) {
+ url_component_matcher_.Match(
+ condition_factory_.CanonicalizeURLForComponentSearches(url), &matches);
+ }
+ if (!regex_set_matcher_.IsEmpty()) {
+ regex_set_matcher_.Match(
+ condition_factory_.CanonicalizeURLForRegexSearches(url), &matches);
+ }
+ if (!origin_and_path_regex_set_matcher_.IsEmpty()) {
+ origin_and_path_regex_set_matcher_.Match(
+ condition_factory_.CanonicalizeURLForOriginAndPathRegexSearches(url),
+ &matches);
+ }
+
+ // Calculate all URLMatcherConditionSets for which all URLMatcherConditions
+ // were fulfilled.
+ std::set<URLMatcherConditionSet::ID> result;
+ for (std::set<StringPattern::ID>::const_iterator i = matches.begin();
+ i != matches.end(); ++i) {
+ // For each URLMatcherConditionSet there is exactly one condition
+ // registered in substring_match_triggers_. This means that the following
+ // logic tests each URLMatcherConditionSet exactly once if it can be
+ // completely fulfilled.
+ StringPatternTriggers::const_iterator triggered_condition_sets_iter =
+ substring_match_triggers_.find(*i);
+ if (triggered_condition_sets_iter == substring_match_triggers_.end())
+ continue; // Not all substring matches are triggers for a condition set.
+ const std::set<URLMatcherConditionSet::ID>& condition_sets =
+ triggered_condition_sets_iter->second;
+ for (std::set<URLMatcherConditionSet::ID>::const_iterator j =
+ condition_sets.begin(); j != condition_sets.end(); ++j) {
+ URLMatcherConditionSets::const_iterator condition_set_iter =
+ url_matcher_condition_sets_.find(*j);
+ DCHECK(condition_set_iter != url_matcher_condition_sets_.end());
+ if (condition_set_iter->second->IsMatch(matches, url))
+ result.insert(*j);
+ }
+ }
+
+ return result;
+}
+
+bool URLMatcher::IsEmpty() const {
+ return condition_factory_.IsEmpty() &&
+ url_matcher_condition_sets_.empty() &&
+ substring_match_triggers_.empty() &&
+ full_url_matcher_.IsEmpty() &&
+ url_component_matcher_.IsEmpty() &&
+ regex_set_matcher_.IsEmpty() &&
+ origin_and_path_regex_set_matcher_.IsEmpty() &&
+ registered_full_url_patterns_.empty() &&
+ registered_url_component_patterns_.empty();
+}
+
+void URLMatcher::UpdateSubstringSetMatcher(bool full_url_conditions) {
+ // The purpose of |full_url_conditions| is just that we need to execute
+ // the same logic once for Full URL searches and once for URL Component
+ // searches (see URLMatcherConditionFactory).
+
+ // Determine which patterns need to be registered when this function
+ // terminates.
+ std::set<const StringPattern*> new_patterns;
+ for (URLMatcherConditionSets::const_iterator condition_set_iter =
+ url_matcher_condition_sets_.begin();
+ condition_set_iter != url_matcher_condition_sets_.end();
+ ++condition_set_iter) {
+ const URLMatcherConditionSet::Conditions& conditions =
+ condition_set_iter->second->conditions();
+ for (URLMatcherConditionSet::Conditions::const_iterator condition_iter =
+ conditions.begin(); condition_iter != conditions.end();
+ ++condition_iter) {
+ // If we are called to process Full URL searches, ignore others, and
+ // vice versa. (Regex conditions are updated in UpdateRegexSetMatcher.)
+ if (!condition_iter->IsRegexCondition() &&
+ !condition_iter->IsOriginAndPathRegexCondition() &&
+ full_url_conditions == condition_iter->IsFullURLCondition())
+ new_patterns.insert(condition_iter->string_pattern());
+ }
+ }
+
+ // This is the set of patterns that were registered before this function
+ // is called.
+ std::set<const StringPattern*>& registered_patterns =
+ full_url_conditions ? registered_full_url_patterns_
+ : registered_url_component_patterns_;
+
+ // Add all patterns that are in new_patterns but not in registered_patterns.
+ std::vector<const StringPattern*> patterns_to_register;
+ std::set_difference(
+ new_patterns.begin(), new_patterns.end(),
+ registered_patterns.begin(), registered_patterns.end(),
+ std::back_inserter(patterns_to_register));
+
+ // Remove all patterns that are in registered_patterns but not in
+ // new_patterns.
+ std::vector<const StringPattern*> patterns_to_unregister;
+ std::set_difference(
+ registered_patterns.begin(), registered_patterns.end(),
+ new_patterns.begin(), new_patterns.end(),
+ std::back_inserter(patterns_to_unregister));
+
+ // Update the SubstringSetMatcher.
+ SubstringSetMatcher& url_matcher =
+ full_url_conditions ? full_url_matcher_ : url_component_matcher_;
+ url_matcher.RegisterAndUnregisterPatterns(patterns_to_register,
+ patterns_to_unregister);
+
+ // Update the set of registered_patterns for the next time this function
+ // is being called.
+ registered_patterns.swap(new_patterns);
+}
+
+void URLMatcher::UpdateRegexSetMatcher() {
+ std::vector<const StringPattern*> new_patterns;
+ std::vector<const StringPattern*> new_origin_and_path_patterns;
+
+ for (URLMatcherConditionSets::const_iterator condition_set_iter =
+ url_matcher_condition_sets_.begin();
+ condition_set_iter != url_matcher_condition_sets_.end();
+ ++condition_set_iter) {
+ const URLMatcherConditionSet::Conditions& conditions =
+ condition_set_iter->second->conditions();
+ for (URLMatcherConditionSet::Conditions::const_iterator condition_iter =
+ conditions.begin(); condition_iter != conditions.end();
+ ++condition_iter) {
+ if (condition_iter->IsRegexCondition()) {
+ new_patterns.push_back(condition_iter->string_pattern());
+ } else if (condition_iter->IsOriginAndPathRegexCondition()) {
+ new_origin_and_path_patterns.push_back(
+ condition_iter->string_pattern());
+ }
+ }
+ }
+
+ // Start over from scratch. We can't really do better than this, since the
+ // FilteredRE2 backend doesn't support incremental updates.
+ regex_set_matcher_.ClearPatterns();
+ regex_set_matcher_.AddPatterns(new_patterns);
+ origin_and_path_regex_set_matcher_.ClearPatterns();
+ origin_and_path_regex_set_matcher_.AddPatterns(new_origin_and_path_patterns);
+}
+
+void URLMatcher::UpdateTriggers() {
+ // Count substring pattern frequencies.
+ std::map<StringPattern::ID, size_t> substring_pattern_frequencies;
+ for (URLMatcherConditionSets::const_iterator condition_set_iter =
+ url_matcher_condition_sets_.begin();
+ condition_set_iter != url_matcher_condition_sets_.end();
+ ++condition_set_iter) {
+ const URLMatcherConditionSet::Conditions& conditions =
+ condition_set_iter->second->conditions();
+ for (URLMatcherConditionSet::Conditions::const_iterator condition_iter =
+ conditions.begin(); condition_iter != conditions.end();
+ ++condition_iter) {
+ const StringPattern* pattern = condition_iter->string_pattern();
+ substring_pattern_frequencies[pattern->id()]++;
+ }
+ }
+
+ // Update trigger conditions: Determine for each URLMatcherConditionSet which
+ // URLMatcherCondition contains a StringPattern that occurs least
+ // frequently in this URLMatcher. We assume that this condition is very
+ // specific and occurs rarely in URLs. If a match occurs for this
+ // URLMatcherCondition, we want to test all other URLMatcherCondition in the
+ // respective URLMatcherConditionSet as well to see whether the entire
+ // URLMatcherConditionSet is considered matching.
+ substring_match_triggers_.clear();
+ for (URLMatcherConditionSets::const_iterator condition_set_iter =
+ url_matcher_condition_sets_.begin();
+ condition_set_iter != url_matcher_condition_sets_.end();
+ ++condition_set_iter) {
+ const URLMatcherConditionSet::Conditions& conditions =
+ condition_set_iter->second->conditions();
+ if (conditions.empty())
+ continue;
+ URLMatcherConditionSet::Conditions::const_iterator condition_iter =
+ conditions.begin();
+ StringPattern::ID trigger = condition_iter->string_pattern()->id();
+ // We skip the first element in the following loop.
+ ++condition_iter;
+ for (; condition_iter != conditions.end(); ++condition_iter) {
+ StringPattern::ID current_id =
+ condition_iter->string_pattern()->id();
+ if (substring_pattern_frequencies[trigger] >
+ substring_pattern_frequencies[current_id]) {
+ trigger = current_id;
+ }
+ }
+ substring_match_triggers_[trigger].insert(condition_set_iter->second->id());
+ }
+}
+
+void URLMatcher::UpdateConditionFactory() {
+ std::set<StringPattern::ID> used_patterns;
+ for (URLMatcherConditionSets::const_iterator condition_set_iter =
+ url_matcher_condition_sets_.begin();
+ condition_set_iter != url_matcher_condition_sets_.end();
+ ++condition_set_iter) {
+ const URLMatcherConditionSet::Conditions& conditions =
+ condition_set_iter->second->conditions();
+ for (URLMatcherConditionSet::Conditions::const_iterator condition_iter =
+ conditions.begin(); condition_iter != conditions.end();
+ ++condition_iter) {
+ used_patterns.insert(condition_iter->string_pattern()->id());
+ }
+ }
+ condition_factory_.ForgetUnusedPatterns(used_patterns);
+}
+
+void URLMatcher::UpdateInternalDatastructures() {
+ UpdateSubstringSetMatcher(false);
+ UpdateSubstringSetMatcher(true);
+ UpdateRegexSetMatcher();
+ UpdateTriggers();
+ UpdateConditionFactory();
+}
+
+} // namespace extensions
diff --git a/chromium/extensions/common/matcher/url_matcher.h b/chromium/extensions/common/matcher/url_matcher.h
new file mode 100644
index 00000000000..d93a6067013
--- /dev/null
+++ b/chromium/extensions/common/matcher/url_matcher.h
@@ -0,0 +1,355 @@
+// 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.
+
+#ifndef EXTENSIONS_COMMON_MATCHER_URL_MATCHER_H_
+#define EXTENSIONS_COMMON_MATCHER_URL_MATCHER_H_
+
+#include <set>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "extensions/common/matcher/regex_set_matcher.h"
+#include "extensions/common/matcher/substring_set_matcher.h"
+
+class GURL;
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace extensions {
+
+// This class represents a single URL matching condition, e.g. a match on the
+// host suffix or the containment of a string in the query component of a GURL.
+//
+// The difference from a simple StringPattern is that this also supports
+// checking whether the {Host, Path, Query} of a URL contains a string. The
+// reduction of URL matching conditions to StringPatterns conducted by
+// URLMatcherConditionFactory is not capable of expressing that alone.
+//
+// Also supported is matching regular expressions against the URL (URL_MATCHES).
+class URLMatcherCondition {
+ public:
+ enum Criterion {
+ HOST_PREFIX,
+ HOST_SUFFIX,
+ HOST_CONTAINS,
+ HOST_EQUALS,
+ PATH_PREFIX,
+ PATH_SUFFIX,
+ PATH_CONTAINS,
+ PATH_EQUALS,
+ QUERY_PREFIX,
+ QUERY_SUFFIX,
+ QUERY_CONTAINS,
+ QUERY_EQUALS,
+ HOST_SUFFIX_PATH_PREFIX,
+ HOST_EQUALS_PATH_PREFIX,
+ URL_PREFIX,
+ URL_SUFFIX,
+ URL_CONTAINS,
+ URL_EQUALS,
+ URL_MATCHES,
+ ORIGIN_AND_PATH_MATCHES, // Matches the URL minus its query string.
+ };
+
+ URLMatcherCondition();
+ ~URLMatcherCondition();
+ URLMatcherCondition(Criterion criterion,
+ const StringPattern* substring_pattern);
+ URLMatcherCondition(const URLMatcherCondition& rhs);
+ URLMatcherCondition& operator=(const URLMatcherCondition& rhs);
+ bool operator<(const URLMatcherCondition& rhs) const;
+
+ Criterion criterion() const { return criterion_; }
+ const StringPattern* string_pattern() const {
+ return string_pattern_;
+ }
+
+ // Returns whether this URLMatcherCondition needs to be executed on a
+ // full URL rather than the individual components (see
+ // URLMatcherConditionFactory).
+ bool IsFullURLCondition() const;
+
+ // Returns whether this URLMatcherCondition is a regular expression to be
+ // handled by a regex matcher instead of a substring matcher.
+ bool IsRegexCondition() const;
+
+ // Returns whether this URLMatcherCondition is a regular expression that shall
+ // be evaluated on the URL without the query parameter.
+ bool IsOriginAndPathRegexCondition() const;
+
+ // Returns whether this condition is fulfilled according to
+ // |matching_patterns| and |url|.
+ bool IsMatch(const std::set<StringPattern::ID>& matching_patterns,
+ const GURL& url) const;
+
+ private:
+ // |criterion_| and |string_pattern_| describe together what property a URL
+ // needs to fulfill to be considered a match.
+ Criterion criterion_;
+
+ // This is the StringPattern that is used in a SubstringSetMatcher.
+ const StringPattern* string_pattern_;
+};
+
+// Class to map the problem of finding {host, path, query} {prefixes, suffixes,
+// containments, and equality} in GURLs to the substring matching problem.
+//
+// Say, you want to check whether the path of a URL starts with "/index.html".
+// This class preprocesses a URL like "www.google.com/index.html" into something
+// like "www.google.com|/index.html". After preprocessing, you can search for
+// "|/index.html" in the string and see that this candidate URL actually has
+// a path that starts with "/index.html". On the contrary,
+// "www.google.com/images/index.html" would be normalized to
+// "www.google.com|/images/index.html". It is easy to see that it contains
+// "/index.html" but the path of the URL does not start with "/index.html".
+//
+// This preprocessing is important if you want to match a URL against many
+// patterns because it reduces the matching to a "discover all substrings
+// of a dictionary in a text" problem, which can be solved very efficiently
+// by the Aho-Corasick algorithm.
+//
+// IMPORTANT: The URLMatcherConditionFactory owns the StringPattern
+// referenced by created URLMatcherConditions. Therefore, it must outlive
+// all created URLMatcherCondition and the SubstringSetMatcher.
+class URLMatcherConditionFactory {
+ public:
+ URLMatcherConditionFactory();
+ ~URLMatcherConditionFactory();
+
+ // Canonicalizes a URL for "Create{Host,Path,Query}*Condition" searches.
+ std::string CanonicalizeURLForComponentSearches(const GURL& url) const;
+
+ // Factory methods for various condition types.
+ //
+ // Note that these methods fill the pattern_singletons_. If you create
+ // conditions and don't register them to a URLMatcher, they will continue to
+ // consume memory. You need to call ForgetUnusedPatterns() or
+ // URLMatcher::ClearUnusedConditionSets() in this case.
+ URLMatcherCondition CreateHostPrefixCondition(const std::string& prefix);
+ URLMatcherCondition CreateHostSuffixCondition(const std::string& suffix);
+ URLMatcherCondition CreateHostContainsCondition(const std::string& str);
+ URLMatcherCondition CreateHostEqualsCondition(const std::string& str);
+
+ URLMatcherCondition CreatePathPrefixCondition(const std::string& prefix);
+ URLMatcherCondition CreatePathSuffixCondition(const std::string& suffix);
+ URLMatcherCondition CreatePathContainsCondition(const std::string& str);
+ URLMatcherCondition CreatePathEqualsCondition(const std::string& str);
+
+ URLMatcherCondition CreateQueryPrefixCondition(const std::string& prefix);
+ URLMatcherCondition CreateQuerySuffixCondition(const std::string& suffix);
+ URLMatcherCondition CreateQueryContainsCondition(const std::string& str);
+ URLMatcherCondition CreateQueryEqualsCondition(const std::string& str);
+
+ // This covers the common case, where you don't care whether a domain
+ // "foobar.com" is expressed as "foobar.com" or "www.foobar.com", and it
+ // should be followed by a given |path_prefix|.
+ URLMatcherCondition CreateHostSuffixPathPrefixCondition(
+ const std::string& host_suffix,
+ const std::string& path_prefix);
+ URLMatcherCondition CreateHostEqualsPathPrefixCondition(
+ const std::string& host,
+ const std::string& path_prefix);
+
+ // Canonicalizes a URL for "CreateURL*Condition" searches.
+ std::string CanonicalizeURLForFullSearches(const GURL& url) const;
+
+ // Canonicalizes a URL for "CreateURLMatchesCondition" searches.
+ std::string CanonicalizeURLForRegexSearches(const GURL& url) const;
+ // Canonicalizes a URL for "CreateOriginAndPathMatchesCondition" searches.
+ std::string CanonicalizeURLForOriginAndPathRegexSearches(
+ const GURL& url) const;
+
+ URLMatcherCondition CreateURLPrefixCondition(const std::string& prefix);
+ URLMatcherCondition CreateURLSuffixCondition(const std::string& suffix);
+ URLMatcherCondition CreateURLContainsCondition(const std::string& str);
+ URLMatcherCondition CreateURLEqualsCondition(const std::string& str);
+
+ URLMatcherCondition CreateURLMatchesCondition(const std::string& regex);
+ URLMatcherCondition CreateOriginAndPathMatchesCondition(
+ const std::string& regex);
+
+ // Removes all patterns from |pattern_singletons_| that are not listed in
+ // |used_patterns|. These patterns are not referenced any more and get
+ // freed.
+ void ForgetUnusedPatterns(
+ const std::set<StringPattern::ID>& used_patterns);
+
+ // Returns true if this object retains no allocated data. Only for debugging.
+ bool IsEmpty() const;
+
+ private:
+ // Creates a URLMatcherCondition according to the parameters passed.
+ // The URLMatcherCondition will refer to a StringPattern that is
+ // owned by |pattern_singletons_|.
+ URLMatcherCondition CreateCondition(URLMatcherCondition::Criterion criterion,
+ const std::string& pattern);
+
+ // Prepends a "." to the hostname if it does not start with one.
+ std::string CanonicalizeHostname(const std::string& hostname) const;
+
+ // Counter that ensures that all created StringPatterns have unique IDs.
+ // Note that substring patterns and regex patterns will use different IDs.
+ int id_counter_;
+
+ // This comparison considers only the pattern() value of the
+ // StringPatterns.
+ struct StringPatternPointerCompare {
+ bool operator()(StringPattern* lhs, StringPattern* rhs) const;
+ };
+ // Set to ensure that we generate only one StringPattern for each content
+ // of StringPattern::pattern().
+ typedef std::set<StringPattern*, StringPatternPointerCompare>
+ PatternSingletons;
+ PatternSingletons substring_pattern_singletons_;
+ PatternSingletons regex_pattern_singletons_;
+ PatternSingletons origin_and_path_regex_pattern_singletons_;
+
+ DISALLOW_COPY_AND_ASSIGN(URLMatcherConditionFactory);
+};
+
+// This class represents a filter for the URL scheme to be hooked up into a
+// URLMatcherConditionSet.
+class URLMatcherSchemeFilter {
+ public:
+ explicit URLMatcherSchemeFilter(const std::string& filter);
+ explicit URLMatcherSchemeFilter(const std::vector<std::string>& filters);
+ ~URLMatcherSchemeFilter();
+ bool IsMatch(const GURL& url) const;
+
+ private:
+ std::vector<std::string> filters_;
+
+ DISALLOW_COPY_AND_ASSIGN(URLMatcherSchemeFilter);
+};
+
+// This class represents a filter for port numbers to be hooked up into a
+// URLMatcherConditionSet.
+class URLMatcherPortFilter {
+ public:
+ // Boundaries of a port range (both ends are included).
+ typedef std::pair<int, int> Range;
+ explicit URLMatcherPortFilter(const std::vector<Range>& ranges);
+ ~URLMatcherPortFilter();
+ bool IsMatch(const GURL& url) const;
+
+ // Creates a port range [from, to]; both ends are included.
+ static Range CreateRange(int from, int to);
+ // Creates a port range containing a single port.
+ static Range CreateRange(int port);
+
+ private:
+ std::vector<Range> ranges_;
+
+ DISALLOW_COPY_AND_ASSIGN(URLMatcherPortFilter);
+};
+
+// This class represents a set of conditions that all need to match on a
+// given URL in order to be considered a match.
+class URLMatcherConditionSet : public base::RefCounted<URLMatcherConditionSet> {
+ public:
+ typedef int ID;
+ typedef std::set<URLMatcherCondition> Conditions;
+ typedef std::vector<scoped_refptr<URLMatcherConditionSet> > Vector;
+
+ // Matches if all conditions in |conditions| are fulfilled.
+ URLMatcherConditionSet(ID id, const Conditions& conditions);
+
+ // Matches if all conditions in |conditions|, |scheme_filter| and
+ // |port_filter| are fulfilled. |scheme_filter| and |port_filter| may be NULL,
+ // in which case, no restrictions are imposed on the scheme/port of a URL.
+ URLMatcherConditionSet(ID id, const Conditions& conditions,
+ scoped_ptr<URLMatcherSchemeFilter> scheme_filter,
+ scoped_ptr<URLMatcherPortFilter> port_filter);
+
+ ID id() const { return id_; }
+ const Conditions& conditions() const { return conditions_; }
+
+ bool IsMatch(const std::set<StringPattern::ID>& matching_patterns,
+ const GURL& url) const;
+
+ private:
+ friend class base::RefCounted<URLMatcherConditionSet>;
+ ~URLMatcherConditionSet();
+ ID id_;
+ Conditions conditions_;
+ scoped_ptr<URLMatcherSchemeFilter> scheme_filter_;
+ scoped_ptr<URLMatcherPortFilter> port_filter_;
+
+ DISALLOW_COPY_AND_ASSIGN(URLMatcherConditionSet);
+};
+
+// This class allows matching one URL against a large set of
+// URLMatcherConditionSets at the same time.
+class URLMatcher {
+ public:
+ URLMatcher();
+ ~URLMatcher();
+
+ // Adds new URLMatcherConditionSet to this URL Matcher. Each condition set
+ // must have a unique ID.
+ // This is an expensive operation as it triggers pre-calculations on the
+ // currently registered condition sets. Do not call this operation many
+ // times with a single condition set in each call.
+ void AddConditionSets(const URLMatcherConditionSet::Vector& condition_sets);
+
+ // Removes the listed condition sets. All |condition_set_ids| must be
+ // currently registered. This function should be called with large batches
+ // of |condition_set_ids| at a time to improve performance.
+ void RemoveConditionSets(
+ const std::vector<URLMatcherConditionSet::ID>& condition_set_ids);
+
+ // Removes all unused condition sets from the ConditionFactory.
+ void ClearUnusedConditionSets();
+
+ // Returns the IDs of all URLMatcherConditionSet that match to this |url|.
+ std::set<URLMatcherConditionSet::ID> MatchURL(const GURL& url) const;
+
+ // Returns the URLMatcherConditionFactory that must be used to create
+ // URLMatcherConditionSets for this URLMatcher.
+ URLMatcherConditionFactory* condition_factory() {
+ return &condition_factory_;
+ }
+
+ // Returns true if this object retains no allocated data. Only for debugging.
+ bool IsEmpty() const;
+
+ private:
+ void UpdateSubstringSetMatcher(bool full_url_conditions);
+ void UpdateRegexSetMatcher();
+ void UpdateTriggers();
+ void UpdateConditionFactory();
+ void UpdateInternalDatastructures();
+
+ URLMatcherConditionFactory condition_factory_;
+
+ // Maps the ID of a URLMatcherConditionSet to the respective
+ // URLMatcherConditionSet.
+ typedef std::map<URLMatcherConditionSet::ID,
+ scoped_refptr<URLMatcherConditionSet> >
+ URLMatcherConditionSets;
+ URLMatcherConditionSets url_matcher_condition_sets_;
+
+ // Maps a StringPattern ID to the URLMatcherConditions that need to
+ // be triggered in case of a StringPattern match.
+ typedef std::map<StringPattern::ID, std::set<URLMatcherConditionSet::ID> >
+ StringPatternTriggers;
+ StringPatternTriggers substring_match_triggers_;
+
+ SubstringSetMatcher full_url_matcher_;
+ SubstringSetMatcher url_component_matcher_;
+ RegexSetMatcher regex_set_matcher_;
+ RegexSetMatcher origin_and_path_regex_set_matcher_;
+ std::set<const StringPattern*> registered_full_url_patterns_;
+ std::set<const StringPattern*> registered_url_component_patterns_;
+
+ DISALLOW_COPY_AND_ASSIGN(URLMatcher);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_MATCHER_URL_MATCHER_H_
diff --git a/chromium/extensions/common/matcher/url_matcher_constants.cc b/chromium/extensions/common/matcher/url_matcher_constants.cc
new file mode 100644
index 00000000000..5b23dea09ae
--- /dev/null
+++ b/chromium/extensions/common/matcher/url_matcher_constants.cc
@@ -0,0 +1,34 @@
+// 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.
+
+#include "extensions/common/matcher/url_matcher_constants.h"
+
+namespace extensions {
+namespace url_matcher_constants {
+
+// Keys of dictionaries for URL constraints
+const char kPortsKey[] = "ports";
+const char kSchemesKey[] = "schemes";
+const char kHostContainsKey[] = "hostContains";
+const char kHostEqualsKey[] = "hostEquals";
+const char kHostPrefixKey[] = "hostPrefix";
+const char kHostSuffixKey[] = "hostSuffix";
+const char kHostSuffixPathPrefixKey[] = "hostSuffixPathPrefix";
+const char kOriginAndPathMatchesKey[] = "originAndPathMatches";
+const char kPathContainsKey[] = "pathContains";
+const char kPathEqualsKey[] = "pathEquals";
+const char kPathPrefixKey[] = "pathPrefix";
+const char kPathSuffixKey[] = "pathSuffix";
+const char kQueryContainsKey[] = "queryContains";
+const char kQueryEqualsKey[] = "queryEquals";
+const char kQueryPrefixKey[] = "queryPrefix";
+const char kQuerySuffixKey[] = "querySuffix";
+const char kURLContainsKey[] = "urlContains";
+const char kURLEqualsKey[] = "urlEquals";
+const char kURLMatchesKey[] = "urlMatches";
+const char kURLPrefixKey[] = "urlPrefix";
+const char kURLSuffixKey[] = "urlSuffix";
+
+} // namespace url_matcher_constants
+} // namespace extensions
diff --git a/chromium/extensions/common/matcher/url_matcher_constants.h b/chromium/extensions/common/matcher/url_matcher_constants.h
new file mode 100644
index 00000000000..0618a521577
--- /dev/null
+++ b/chromium/extensions/common/matcher/url_matcher_constants.h
@@ -0,0 +1,39 @@
+// 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.
+
+// Constants used for the URLMatcher component of the Declarative API.
+
+#ifndef EXTENSIONS_COMMON_MATCHER_URL_MATCHER_CONSTANTS_H_
+#define EXTENSIONS_COMMON_MATCHER_URL_MATCHER_CONSTANTS_H_
+
+namespace extensions {
+namespace url_matcher_constants {
+
+// Keys of dictionaries for URL constraints
+extern const char kPortsKey[];
+extern const char kSchemesKey[];
+extern const char kHostContainsKey[];
+extern const char kHostEqualsKey[];
+extern const char kHostPrefixKey[];
+extern const char kHostSuffixKey[];
+extern const char kHostSuffixPathPrefixKey[];
+extern const char kOriginAndPathMatchesKey[];
+extern const char kPathContainsKey[];
+extern const char kPathEqualsKey[];
+extern const char kPathPrefixKey[];
+extern const char kPathSuffixKey[];
+extern const char kQueryContainsKey[];
+extern const char kQueryEqualsKey[];
+extern const char kQueryPrefixKey[];
+extern const char kQuerySuffixKey[];
+extern const char kURLContainsKey[];
+extern const char kURLEqualsKey[];
+extern const char kURLMatchesKey[];
+extern const char kURLPrefixKey[];
+extern const char kURLSuffixKey[];
+
+} // namespace url_matcher_constants
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_MATCHER_URL_MATCHER_CONSTANTS_H_
diff --git a/chromium/extensions/common/matcher/url_matcher_factory.cc b/chromium/extensions/common/matcher/url_matcher_factory.cc
new file mode 100644
index 00000000000..6eec2e6ab12
--- /dev/null
+++ b/chromium/extensions/common/matcher/url_matcher_factory.cc
@@ -0,0 +1,277 @@
+// 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.
+
+#include "extensions/common/matcher/url_matcher_factory.h"
+
+#include <algorithm>
+#include <cctype>
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "base/values.h"
+#include "extensions/common/error_utils.h"
+#include "extensions/common/matcher/url_matcher_constants.h"
+#include "extensions/common/matcher/url_matcher_helpers.h"
+#include "third_party/re2/re2/re2.h"
+
+namespace helpers = extensions::url_matcher_helpers;
+namespace keys = extensions::url_matcher_constants;
+
+namespace {
+// Error messages:
+const char kInvalidPortRanges[] = "Invalid port ranges in UrlFilter.";
+const char kVectorOfStringsExpected[] =
+ "UrlFilter attribute '*' expected a vector of strings as parameter.";
+const char kUnknownURLFilterAttribute[] =
+ "Unknown attribute '*' in UrlFilter.";
+const char kAttributeExpectedString[] =
+ "UrlFilter attribute '*' expected a string value.";
+const char kUnparseableRegexString[] =
+ "Could not parse regular expression '*': *";
+const char kLowerCaseExpected[] = "* values need to be in lower case.";
+
+// Registry for all factory methods of extensions::URLMatcherConditionFactory
+// that allows translating string literals from the extension API into
+// the corresponding factory method to be called.
+class URLMatcherConditionFactoryMethods {
+ public:
+ URLMatcherConditionFactoryMethods() {
+ typedef extensions::URLMatcherConditionFactory F;
+ factory_methods_[keys::kHostContainsKey] = &F::CreateHostContainsCondition;
+ factory_methods_[keys::kHostEqualsKey] = &F::CreateHostEqualsCondition;
+ factory_methods_[keys::kHostPrefixKey] = &F::CreateHostPrefixCondition;
+ factory_methods_[keys::kHostSuffixKey] = &F::CreateHostSuffixCondition;
+ factory_methods_[keys::kOriginAndPathMatchesKey] =
+ &F::CreateOriginAndPathMatchesCondition;
+ factory_methods_[keys::kPathContainsKey] = &F::CreatePathContainsCondition;
+ factory_methods_[keys::kPathEqualsKey] = &F::CreatePathEqualsCondition;
+ factory_methods_[keys::kPathPrefixKey] = &F::CreatePathPrefixCondition;
+ factory_methods_[keys::kPathSuffixKey] = &F::CreatePathSuffixCondition;
+ factory_methods_[keys::kQueryContainsKey] =
+ &F::CreateQueryContainsCondition;
+ factory_methods_[keys::kQueryEqualsKey] = &F::CreateQueryEqualsCondition;
+ factory_methods_[keys::kQueryPrefixKey] = &F::CreateQueryPrefixCondition;
+ factory_methods_[keys::kQuerySuffixKey] = &F::CreateQuerySuffixCondition;
+ factory_methods_[keys::kURLContainsKey] = &F::CreateURLContainsCondition;
+ factory_methods_[keys::kURLEqualsKey] = &F::CreateURLEqualsCondition;
+ factory_methods_[keys::kURLPrefixKey] = &F::CreateURLPrefixCondition;
+ factory_methods_[keys::kURLSuffixKey] = &F::CreateURLSuffixCondition;
+ factory_methods_[keys::kURLMatchesKey] = &F::CreateURLMatchesCondition;
+ }
+
+ // Returns whether a factory method for the specified |pattern_type| (e.g.
+ // "host_suffix") is known.
+ bool Contains(const std::string& pattern_type) const {
+ return factory_methods_.find(pattern_type) != factory_methods_.end();
+ }
+
+ // Creates a URLMatcherCondition instance from |url_matcher_condition_factory|
+ // of the given |pattern_type| (e.g. "host_suffix") for the given
+ // |pattern_value| (e.g. "example.com").
+ // The |pattern_type| needs to be known to this class (see Contains()) or
+ // a CHECK is triggered.
+ extensions::URLMatcherCondition Call(
+ extensions::URLMatcherConditionFactory* url_matcher_condition_factory,
+ const std::string& pattern_type,
+ const std::string& pattern_value) const {
+ FactoryMethods::const_iterator i = factory_methods_.find(pattern_type);
+ CHECK(i != factory_methods_.end());
+ const FactoryMethod& method = i->second;
+ return (url_matcher_condition_factory->*method)(pattern_value);
+ }
+
+ private:
+ typedef extensions::URLMatcherCondition
+ (extensions::URLMatcherConditionFactory::* FactoryMethod)
+ (const std::string& prefix);
+ typedef std::map<std::string, FactoryMethod> FactoryMethods;
+
+ FactoryMethods factory_methods_;
+
+ DISALLOW_COPY_AND_ASSIGN(URLMatcherConditionFactoryMethods);
+};
+
+static base::LazyInstance<URLMatcherConditionFactoryMethods>
+ g_url_matcher_condition_factory_methods = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+namespace extensions {
+
+// static
+scoped_refptr<URLMatcherConditionSet>
+URLMatcherFactory::CreateFromURLFilterDictionary(
+ URLMatcherConditionFactory* url_matcher_condition_factory,
+ const base::DictionaryValue* url_filter_dict,
+ URLMatcherConditionSet::ID id,
+ std::string* error) {
+ scoped_ptr<URLMatcherSchemeFilter> url_matcher_schema_filter;
+ scoped_ptr<URLMatcherPortFilter> url_matcher_port_filter;
+ URLMatcherConditionSet::Conditions url_matcher_conditions;
+
+ for (base::DictionaryValue::Iterator iter(*url_filter_dict);
+ !iter.IsAtEnd(); iter.Advance()) {
+ const std::string& condition_attribute_name = iter.key();
+ const Value& condition_attribute_value = iter.value();
+ if (IsURLMatcherConditionAttribute(condition_attribute_name)) {
+ // Handle {host, path, ...}{Prefix, Suffix, Contains, Equals}.
+ URLMatcherCondition url_matcher_condition =
+ CreateURLMatcherCondition(
+ url_matcher_condition_factory,
+ condition_attribute_name,
+ &condition_attribute_value,
+ error);
+ if (!error->empty())
+ return scoped_refptr<URLMatcherConditionSet>(NULL);
+ url_matcher_conditions.insert(url_matcher_condition);
+ } else if (condition_attribute_name == keys::kSchemesKey) {
+ // Handle scheme.
+ url_matcher_schema_filter = CreateURLMatcherScheme(
+ &condition_attribute_value, error);
+ if (!error->empty())
+ return scoped_refptr<URLMatcherConditionSet>(NULL);
+ } else if (condition_attribute_name == keys::kPortsKey) {
+ // Handle ports.
+ url_matcher_port_filter = CreateURLMatcherPorts(
+ &condition_attribute_value, error);
+ if (!error->empty())
+ return scoped_refptr<URLMatcherConditionSet>(NULL);
+ } else {
+ // Handle unknown attributes.
+ *error = ErrorUtils::FormatErrorMessage(
+ kUnknownURLFilterAttribute,
+ condition_attribute_name);
+ return scoped_refptr<URLMatcherConditionSet>(NULL);
+ }
+ }
+
+ // As the URL is the preliminary matching criterion that triggers the tests
+ // for the remaining condition attributes, we insert an empty URL match if
+ // no other url match conditions were specified. Such an empty URL is always
+ // matched.
+ if (url_matcher_conditions.empty()) {
+ url_matcher_conditions.insert(
+ url_matcher_condition_factory->CreateHostPrefixCondition(
+ std::string()));
+ }
+
+ scoped_refptr<URLMatcherConditionSet> url_matcher_condition_set(
+ new URLMatcherConditionSet(id, url_matcher_conditions,
+ url_matcher_schema_filter.Pass(), url_matcher_port_filter.Pass()));
+ return url_matcher_condition_set;
+}
+
+// static
+bool URLMatcherFactory::IsURLMatcherConditionAttribute(
+ const std::string& condition_attribute_name) {
+ return g_url_matcher_condition_factory_methods.Get().Contains(
+ condition_attribute_name);
+}
+
+namespace {
+
+// Returns true if some alphabetic characters in this string are upper case.
+bool ContainsUpperCase(const std::string& str) {
+ return std::find_if(str.begin(), str.end(), ::isupper) != str.end();
+}
+
+} // namespace
+
+// static
+URLMatcherCondition URLMatcherFactory::CreateURLMatcherCondition(
+ URLMatcherConditionFactory* url_matcher_condition_factory,
+ const std::string& condition_attribute_name,
+ const base::Value* value,
+ std::string* error) {
+ std::string str_value;
+ if (!value->GetAsString(&str_value)) {
+ *error = ErrorUtils::FormatErrorMessage(kAttributeExpectedString,
+ condition_attribute_name);
+ return URLMatcherCondition();
+ }
+ if (condition_attribute_name == keys::kHostContainsKey ||
+ condition_attribute_name == keys::kHostPrefixKey ||
+ condition_attribute_name == keys::kHostSuffixKey ||
+ condition_attribute_name == keys::kHostEqualsKey) {
+ if (ContainsUpperCase(str_value)) {
+ *error = ErrorUtils::FormatErrorMessage(kLowerCaseExpected,
+ "Host");
+ return URLMatcherCondition();
+ }
+ }
+
+ // Test regular expressions for validity.
+ if (condition_attribute_name == keys::kURLMatchesKey ||
+ condition_attribute_name == keys::kOriginAndPathMatchesKey) {
+ re2::RE2 regex(str_value);
+ if (!regex.ok()) {
+ *error = ErrorUtils::FormatErrorMessage(kUnparseableRegexString,
+ str_value, regex.error());
+ return URLMatcherCondition();
+ }
+ }
+ return g_url_matcher_condition_factory_methods.Get().Call(
+ url_matcher_condition_factory, condition_attribute_name, str_value);
+}
+
+// static
+scoped_ptr<URLMatcherSchemeFilter> URLMatcherFactory::CreateURLMatcherScheme(
+ const base::Value* value,
+ std::string* error) {
+ std::vector<std::string> schemas;
+ if (!helpers::GetAsStringVector(value, &schemas)) {
+ *error = ErrorUtils::FormatErrorMessage(kVectorOfStringsExpected,
+ keys::kSchemesKey);
+ return scoped_ptr<URLMatcherSchemeFilter>();
+ }
+ for (std::vector<std::string>::const_iterator it = schemas.begin();
+ it != schemas.end(); ++it) {
+ if (ContainsUpperCase(*it)) {
+ *error = ErrorUtils::FormatErrorMessage(kLowerCaseExpected,
+ "Scheme");
+ return scoped_ptr<URLMatcherSchemeFilter>();
+ }
+ }
+ return scoped_ptr<URLMatcherSchemeFilter>(
+ new URLMatcherSchemeFilter(schemas));
+}
+
+// static
+scoped_ptr<URLMatcherPortFilter> URLMatcherFactory::CreateURLMatcherPorts(
+ const base::Value* value,
+ std::string* error) {
+ std::vector<URLMatcherPortFilter::Range> ranges;
+ const base::ListValue* value_list = NULL;
+ if (!value->GetAsList(&value_list)) {
+ *error = kInvalidPortRanges;
+ return scoped_ptr<URLMatcherPortFilter>();
+ }
+
+ for (ListValue::const_iterator i = value_list->begin();
+ i != value_list->end(); ++i) {
+ Value* entry = *i;
+ int port = 0;
+ base::ListValue* range = NULL;
+ if (entry->GetAsInteger(&port)) {
+ ranges.push_back(URLMatcherPortFilter::CreateRange(port));
+ } else if (entry->GetAsList(&range)) {
+ int from = 0, to = 0;
+ if (range->GetSize() != 2u ||
+ !range->GetInteger(0, &from) ||
+ !range->GetInteger(1, &to)) {
+ *error = kInvalidPortRanges;
+ return scoped_ptr<URLMatcherPortFilter>();
+ }
+ ranges.push_back(URLMatcherPortFilter::CreateRange(from, to));
+ } else {
+ *error = kInvalidPortRanges;
+ return scoped_ptr<URLMatcherPortFilter>();
+ }
+ }
+
+ return scoped_ptr<URLMatcherPortFilter>(new URLMatcherPortFilter(ranges));
+}
+
+} // namespace extensions
diff --git a/chromium/extensions/common/matcher/url_matcher_factory.h b/chromium/extensions/common/matcher/url_matcher_factory.h
new file mode 100644
index 00000000000..7c5be6832d8
--- /dev/null
+++ b/chromium/extensions/common/matcher/url_matcher_factory.h
@@ -0,0 +1,62 @@
+// 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.
+
+#ifndef EXTENSIONS_COMMON_MATCHER_URL_MATCHER_FACTORY_H_
+#define EXTENSIONS_COMMON_MATCHER_URL_MATCHER_FACTORY_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "extensions/common/matcher/url_matcher.h"
+
+namespace base {
+class DictionaryValue;
+class Value;
+}
+
+namespace extensions {
+
+class URLMatcherFactory {
+ public:
+ // Creates a URLMatcherConditionSet from a UrlFilter dictionary as defined in
+ // the declarative API. |url_fetcher_dict| contains the dictionary passed
+ // by the extension, |id| is the identifier assigned to the created
+ // URLMatcherConditionSet. In case of an error, |error| is set to contain
+ // an error message.
+ //
+ // Note: In case this function fails or if you don't register the
+ // URLMatcherConditionSet to the URLMatcher, you need to call
+ // URLMatcher::ClearUnusedConditionSets() on the URLMatcher that owns this
+ // URLMatcherFactory. Otherwise you leak memory.
+ static scoped_refptr<URLMatcherConditionSet> CreateFromURLFilterDictionary(
+ URLMatcherConditionFactory* url_matcher_condition_factory,
+ const base::DictionaryValue* url_filter_dict,
+ URLMatcherConditionSet::ID id,
+ std::string* error);
+
+ private:
+ // Returns whether a condition attribute with name |condition_attribute_name|
+ // needs to be handled by the URLMatcher.
+ static bool IsURLMatcherConditionAttribute(
+ const std::string& condition_attribute_name);
+
+ // Factory method of for URLMatcherConditions.
+ static URLMatcherCondition CreateURLMatcherCondition(
+ URLMatcherConditionFactory* url_matcher_condition_factory,
+ const std::string& condition_attribute_name,
+ const base::Value* value,
+ std::string* error);
+
+ static scoped_ptr<URLMatcherSchemeFilter> CreateURLMatcherScheme(
+ const base::Value* value, std::string* error);
+
+ static scoped_ptr<URLMatcherPortFilter> CreateURLMatcherPorts(
+ const base::Value* value, std::string* error);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(URLMatcherFactory);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_MATCHER_URL_MATCHER_FACTORY_H_
diff --git a/chromium/extensions/common/matcher/url_matcher_factory_unittest.cc b/chromium/extensions/common/matcher/url_matcher_factory_unittest.cc
new file mode 100644
index 00000000000..733e1b8a941
--- /dev/null
+++ b/chromium/extensions/common/matcher/url_matcher_factory_unittest.cc
@@ -0,0 +1,339 @@
+// 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.
+
+#include "extensions/common/matcher/url_matcher_factory.h"
+
+#include "base/basictypes.h"
+#include "base/format_macros.h"
+#include "base/strings/stringprintf.h"
+#include "base/values.h"
+#include "extensions/common/matcher/url_matcher_constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace extensions {
+
+namespace keys = url_matcher_constants;
+
+TEST(URLMatcherFactoryTest, CreateFromURLFilterDictionary) {
+ URLMatcher matcher;
+
+ std::string error;
+ scoped_refptr<URLMatcherConditionSet> result;
+
+ // Invalid key: {"invalid": "foobar"}
+ DictionaryValue invalid_condition;
+ invalid_condition.SetString("invalid", "foobar");
+
+ // Invalid value type: {"hostSuffix": []}
+ DictionaryValue invalid_condition2;
+ invalid_condition2.Set(keys::kHostSuffixKey, new ListValue);
+
+ // Invalid regex value: {"urlMatches": "*"}
+ DictionaryValue invalid_condition3;
+ invalid_condition3.SetString(keys::kURLMatchesKey, "*");
+
+ // Invalid regex value: {"originAndPathMatches": "*"}
+ DictionaryValue invalid_condition4;
+ invalid_condition4.SetString(keys::kOriginAndPathMatchesKey, "*");
+
+ // Valid values:
+ // {
+ // "port_range": [80, [1000, 1010]],
+ // "schemes": ["http"],
+ // "hostSuffix": "example.com"
+ // "hostPrefix": "www"
+ // }
+
+ // Port range: Allow 80;1000-1010.
+ ListValue* port_range = new ListValue();
+ port_range->Append(Value::CreateIntegerValue(1000));
+ port_range->Append(Value::CreateIntegerValue(1010));
+ ListValue* port_ranges = new ListValue();
+ port_ranges->Append(Value::CreateIntegerValue(80));
+ port_ranges->Append(port_range);
+
+ ListValue* scheme_list = new ListValue();
+ scheme_list->Append(Value::CreateStringValue("http"));
+
+ DictionaryValue valid_condition;
+ valid_condition.SetString(keys::kHostSuffixKey, "example.com");
+ valid_condition.SetString(keys::kHostPrefixKey, "www");
+ valid_condition.Set(keys::kPortsKey, port_ranges);
+ valid_condition.Set(keys::kSchemesKey, scheme_list);
+
+ // Test wrong condition name passed.
+ error.clear();
+ result = URLMatcherFactory::CreateFromURLFilterDictionary(
+ matcher.condition_factory(), &invalid_condition, 1, &error);
+ EXPECT_FALSE(error.empty());
+ EXPECT_FALSE(result.get());
+
+ // Test wrong datatype in hostSuffix.
+ error.clear();
+ result = URLMatcherFactory::CreateFromURLFilterDictionary(
+ matcher.condition_factory(), &invalid_condition2, 2, &error);
+ EXPECT_FALSE(error.empty());
+ EXPECT_FALSE(result.get());
+
+ // Test invalid regex in urlMatches.
+ error.clear();
+ result = URLMatcherFactory::CreateFromURLFilterDictionary(
+ matcher.condition_factory(), &invalid_condition3, 3, &error);
+ EXPECT_FALSE(error.empty());
+ EXPECT_FALSE(result.get());
+
+ error.clear();
+ result = URLMatcherFactory::CreateFromURLFilterDictionary(
+ matcher.condition_factory(), &invalid_condition4, 4, &error);
+ EXPECT_FALSE(error.empty());
+ EXPECT_FALSE(result.get());
+
+ // Test success.
+ error.clear();
+ result = URLMatcherFactory::CreateFromURLFilterDictionary(
+ matcher.condition_factory(), &valid_condition, 100, &error);
+ EXPECT_EQ("", error);
+ ASSERT_TRUE(result.get());
+
+ URLMatcherConditionSet::Vector conditions;
+ conditions.push_back(result);
+ matcher.AddConditionSets(conditions);
+
+ EXPECT_EQ(1u, matcher.MatchURL(GURL("http://www.example.com")).size());
+ EXPECT_EQ(1u, matcher.MatchURL(GURL("http://www.example.com:80")).size());
+ EXPECT_EQ(1u, matcher.MatchURL(GURL("http://www.example.com:1000")).size());
+ // Wrong scheme.
+ EXPECT_EQ(0u, matcher.MatchURL(GURL("https://www.example.com:80")).size());
+ // Wrong port.
+ EXPECT_EQ(0u, matcher.MatchURL(GURL("http://www.example.com:81")).size());
+ // Unfulfilled host prefix.
+ EXPECT_EQ(0u, matcher.MatchURL(GURL("http://mail.example.com:81")).size());
+}
+
+// Using upper case letters for scheme and host values is currently an error.
+// See more context at http://crbug.com/160702#c6 .
+TEST(URLMatcherFactoryTest, UpperCase) {
+ URLMatcher matcher;
+ std::string error;
+ scoped_refptr<URLMatcherConditionSet> result;
+
+ // {"hostContains": "exaMple"}
+ DictionaryValue invalid_condition1;
+ invalid_condition1.SetString(keys::kHostContainsKey, "exaMple");
+
+ // {"hostSuffix": ".Com"}
+ DictionaryValue invalid_condition2;
+ invalid_condition2.SetString(keys::kHostSuffixKey, ".Com");
+
+ // {"hostPrefix": "WWw."}
+ DictionaryValue invalid_condition3;
+ invalid_condition3.SetString(keys::kHostPrefixKey, "WWw.");
+
+ // {"hostEquals": "WWW.example.Com"}
+ DictionaryValue invalid_condition4;
+ invalid_condition4.SetString(keys::kHostEqualsKey, "WWW.example.Com");
+
+ // {"scheme": ["HTTP"]}
+ ListValue* scheme_list = new ListValue();
+ scheme_list->Append(Value::CreateStringValue("HTTP"));
+ DictionaryValue invalid_condition5;
+ invalid_condition5.Set(keys::kSchemesKey, scheme_list);
+
+ const DictionaryValue* invalid_conditions[] = {
+ &invalid_condition1,
+ &invalid_condition2,
+ &invalid_condition3,
+ &invalid_condition4,
+ &invalid_condition5
+ };
+
+ for (size_t i = 0; i < arraysize(invalid_conditions); ++i) {
+ error.clear();
+ result = URLMatcherFactory::CreateFromURLFilterDictionary(
+ matcher.condition_factory(), invalid_conditions[i], 1, &error);
+ EXPECT_FALSE(error.empty()) << "in iteration " << i;
+ EXPECT_FALSE(result.get()) << "in iteration " << i;
+ }
+}
+
+// This class wraps a case sensitivity test for a single UrlFilter condition.
+class UrlConditionCaseTest {
+ public:
+ // The condition is identified by the key |condition_key|. If that key is
+ // associated with string values, then |use_list_of_strings| should be false,
+ // if the key is associated with list-of-string values, then
+ // |use_list_of_strings| should be true. In |url| is the URL to test against.
+ UrlConditionCaseTest(const char* condition_key,
+ bool use_list_of_strings,
+ const std::string& expected_value,
+ const std::string& incorrect_case_value,
+ bool case_sensitive,
+ bool lower_case_enforced,
+ const GURL& url)
+ : condition_key_(condition_key),
+ use_list_of_strings_(use_list_of_strings),
+ expected_value_(expected_value),
+ incorrect_case_value_(incorrect_case_value),
+ expected_result_for_wrong_case_(ExpectedResult(case_sensitive,
+ lower_case_enforced)),
+ url_(url) {}
+
+ ~UrlConditionCaseTest() {}
+
+ // Match the condition against |url_|. Checks via EXPECT_* macros that
+ // |expected_value_| matches always, and that |incorrect_case_value_| matches
+ // iff |case_sensitive_| is false.
+ void Test() const;
+
+ private:
+ enum ResultType { OK, NOT_FULFILLED, CREATE_FAILURE };
+
+ // What is the expected result of |CheckCondition| if a wrong-case |value|
+ // containing upper case letters is supplied.
+ static ResultType ExpectedResult(bool case_sensitive,
+ bool lower_case_enforced) {
+ if (lower_case_enforced)
+ return CREATE_FAILURE;
+ if (case_sensitive)
+ return NOT_FULFILLED;
+ return OK;
+ }
+
+ // Test the condition |condition_key_| = |value| against |url_|.
+ // Check, via EXPECT_* macros, that either the condition cannot be constructed
+ // at all, or that the condition is not fulfilled, or that it is fulfilled,
+ // depending on the value of |expected_result|.
+ void CheckCondition(const std::string& value,
+ ResultType expected_result) const;
+
+ const char* condition_key_;
+ const bool use_list_of_strings_;
+ const std::string& expected_value_;
+ const std::string& incorrect_case_value_;
+ const ResultType expected_result_for_wrong_case_;
+ const GURL& url_;
+
+ // Allow implicit copy and assign, because a public copy constructor is
+ // needed, but never used (!), for the definition of arrays of this class.
+};
+
+void UrlConditionCaseTest::Test() const {
+ CheckCondition(expected_value_, OK);
+ CheckCondition(incorrect_case_value_, expected_result_for_wrong_case_);
+}
+
+void UrlConditionCaseTest::CheckCondition(
+ const std::string& value,
+ UrlConditionCaseTest::ResultType expected_result) const {
+ DictionaryValue condition;
+ if (use_list_of_strings_) {
+ ListValue* list = new ListValue();
+ list->Append(Value::CreateStringValue(value));
+ condition.SetWithoutPathExpansion(condition_key_, list);
+ } else {
+ condition.SetStringWithoutPathExpansion(condition_key_, value);
+ }
+
+ URLMatcher matcher;
+ std::string error;
+ scoped_refptr<URLMatcherConditionSet> result;
+
+ result = URLMatcherFactory::CreateFromURLFilterDictionary(
+ matcher.condition_factory(), &condition, 1, &error);
+ if (expected_result == CREATE_FAILURE) {
+ EXPECT_FALSE(error.empty());
+ EXPECT_FALSE(result.get());
+ return;
+ }
+ EXPECT_EQ("", error);
+ ASSERT_TRUE(result.get());
+
+ URLMatcherConditionSet::Vector conditions;
+ conditions.push_back(result);
+ matcher.AddConditionSets(conditions);
+ EXPECT_EQ((expected_result == OK ? 1u : 0u), matcher.MatchURL(url_).size())
+ << "while matching condition " << condition_key_ << " with value "
+ << value << " against url " << url_;
+}
+
+// This tests that the UrlFilter handles case sensitivity on various parts of
+// URLs correctly.
+TEST(URLMatcherFactoryTest, CaseSensitivity) {
+ const std::string kScheme("https");
+ const std::string kSchemeUpper("HTTPS");
+ const std::string kHost("www.example.com");
+ const std::string kHostUpper("WWW.EXAMPLE.COM");
+ const std::string kPath("/path");
+ const std::string kPathUpper("/PATH");
+ const std::string kQuery("?option=value&A=B");
+ const std::string kQueryUpper("?OPTION=VALUE&A=B");
+ const std::string kUrl(kScheme + "://" + kHost + ":1234" + kPath + kQuery);
+ const std::string kUrlUpper(
+ kSchemeUpper + "://" + kHostUpper + ":1234" + kPathUpper + kQueryUpper);
+ const GURL url(kUrl);
+ // Note: according to RFC 3986, and RFC 1034, schema and host, respectively
+ // should be case insensitive. See crbug.com/160702#6 for why we still
+ // require them to be case sensitive in UrlFilter, and enforce lower case.
+ const bool kIsSchemeLowerCaseEnforced = true;
+ const bool kIsHostLowerCaseEnforced = true;
+ const bool kIsPathLowerCaseEnforced = false;
+ const bool kIsQueryLowerCaseEnforced = false;
+ const bool kIsUrlLowerCaseEnforced = false;
+ const bool kIsSchemeCaseSensitive = true;
+ const bool kIsHostCaseSensitive = true;
+ const bool kIsPathCaseSensitive = true;
+ const bool kIsQueryCaseSensitive = true;
+ const bool kIsUrlCaseSensitive = kIsSchemeCaseSensitive ||
+ kIsHostCaseSensitive ||
+ kIsPathCaseSensitive ||
+ kIsQueryCaseSensitive;
+
+ const UrlConditionCaseTest case_tests[] = {
+ UrlConditionCaseTest(keys::kSchemesKey, true, kScheme, kSchemeUpper,
+ kIsSchemeCaseSensitive, kIsSchemeLowerCaseEnforced,
+ url),
+ UrlConditionCaseTest(keys::kHostContainsKey, false, kHost, kHostUpper,
+ kIsHostCaseSensitive, kIsHostLowerCaseEnforced, url),
+ UrlConditionCaseTest(keys::kHostEqualsKey, false, kHost, kHostUpper,
+ kIsHostCaseSensitive, kIsHostLowerCaseEnforced, url),
+ UrlConditionCaseTest(keys::kHostPrefixKey, false, kHost, kHostUpper,
+ kIsHostCaseSensitive, kIsHostLowerCaseEnforced, url),
+ UrlConditionCaseTest(keys::kHostSuffixKey, false, kHost, kHostUpper,
+ kIsHostCaseSensitive, kIsHostLowerCaseEnforced, url),
+ UrlConditionCaseTest(keys::kPathContainsKey, false, kPath, kPathUpper,
+ kIsPathCaseSensitive, kIsPathLowerCaseEnforced, url),
+ UrlConditionCaseTest(keys::kPathEqualsKey, false, kPath, kPathUpper,
+ kIsPathCaseSensitive, kIsPathLowerCaseEnforced, url),
+ UrlConditionCaseTest(keys::kPathPrefixKey, false, kPath, kPathUpper,
+ kIsPathCaseSensitive, kIsPathLowerCaseEnforced, url),
+ UrlConditionCaseTest(keys::kPathSuffixKey, false, kPath, kPathUpper,
+ kIsPathCaseSensitive, kIsPathLowerCaseEnforced, url),
+ UrlConditionCaseTest(keys::kQueryContainsKey, false, kQuery, kQueryUpper,
+ kIsQueryCaseSensitive, kIsQueryLowerCaseEnforced, url),
+ UrlConditionCaseTest(keys::kQueryEqualsKey, false, kQuery, kQueryUpper,
+ kIsQueryCaseSensitive, kIsQueryLowerCaseEnforced, url),
+ UrlConditionCaseTest(keys::kQueryPrefixKey, false, kQuery, kQueryUpper,
+ kIsQueryCaseSensitive, kIsQueryLowerCaseEnforced, url),
+ UrlConditionCaseTest(keys::kQuerySuffixKey, false, kQuery, kQueryUpper,
+ kIsQueryCaseSensitive, kIsQueryLowerCaseEnforced, url),
+ // Excluding kURLMatchesKey because case sensitivity can be specified in the
+ // RE2 expression.
+ UrlConditionCaseTest(keys::kURLContainsKey, false, kUrl, kUrlUpper,
+ kIsUrlCaseSensitive, kIsUrlLowerCaseEnforced, url),
+ UrlConditionCaseTest(keys::kURLEqualsKey, false, kUrl, kUrlUpper,
+ kIsUrlCaseSensitive, kIsUrlLowerCaseEnforced, url),
+ UrlConditionCaseTest(keys::kURLPrefixKey, false, kUrl, kUrlUpper,
+ kIsUrlCaseSensitive, kIsUrlLowerCaseEnforced, url),
+ UrlConditionCaseTest(keys::kURLSuffixKey, false, kUrl, kUrlUpper,
+ kIsUrlCaseSensitive, kIsUrlLowerCaseEnforced, url),
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(case_tests); ++i) {
+ SCOPED_TRACE(base::StringPrintf("Iteration: %" PRIuS, i));
+ case_tests[i].Test();
+ }
+}
+
+} // namespace extensions
diff --git a/chromium/extensions/common/matcher/url_matcher_helpers.cc b/chromium/extensions/common/matcher/url_matcher_helpers.cc
new file mode 100644
index 00000000000..e4832cc18ac
--- /dev/null
+++ b/chromium/extensions/common/matcher/url_matcher_helpers.cc
@@ -0,0 +1,31 @@
+// 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.
+
+#include "extensions/common/matcher/url_matcher_helpers.h"
+
+#include "base/values.h"
+
+namespace extensions {
+namespace url_matcher_helpers {
+
+// Converts a ValueList |value| of strings into a vector. Returns true if
+// successful.
+bool GetAsStringVector(const base::Value* value,
+ std::vector<std::string>* out) {
+ const ListValue* value_as_list = 0;
+ if (!value->GetAsList(&value_as_list))
+ return false;
+
+ size_t number_types = value_as_list->GetSize();
+ for (size_t i = 0; i < number_types; ++i) {
+ std::string item;
+ if (!value_as_list->GetString(i, &item))
+ return false;
+ out->push_back(item);
+ }
+ return true;
+}
+
+} // namespace url_matcher_helpers
+} // namespace extensions
diff --git a/chromium/extensions/common/matcher/url_matcher_helpers.h b/chromium/extensions/common/matcher/url_matcher_helpers.h
new file mode 100644
index 00000000000..57bd2997cee
--- /dev/null
+++ b/chromium/extensions/common/matcher/url_matcher_helpers.h
@@ -0,0 +1,27 @@
+// 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.
+
+// Helper functions used for URLMatcher and Declarative APIs.
+
+#ifndef EXTENSIONS_COMMON_MATCHER_URL_MATCHER_HELPERS_H_
+#define EXTENSIONS_COMMON_MATCHER_URL_MATCHER_HELPERS_H_
+
+#include <string>
+#include <vector>
+
+namespace base {
+class Value;
+}
+
+namespace extensions {
+namespace url_matcher_helpers {
+
+// Converts a ValueList |value| of strings into a vector. Returns true if
+// successful.
+bool GetAsStringVector(const base::Value* value, std::vector<std::string>* out);
+
+} // namespace declarative_helpers
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_MATCHER_URL_MATCHER_HELPERS_H_
diff --git a/chromium/extensions/common/matcher/url_matcher_unittest.cc b/chromium/extensions/common/matcher/url_matcher_unittest.cc
new file mode 100644
index 00000000000..29b7c890dfd
--- /dev/null
+++ b/chromium/extensions/common/matcher/url_matcher_unittest.cc
@@ -0,0 +1,682 @@
+// 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.
+
+#include "extensions/common/matcher/url_matcher.h"
+
+#include "base/strings/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace extensions {
+
+//
+// URLMatcherCondition
+//
+
+TEST(URLMatcherConditionTest, Constructors) {
+ StringPattern pattern("example.com", 1);
+ URLMatcherCondition m1(URLMatcherCondition::HOST_SUFFIX, &pattern);
+ EXPECT_EQ(URLMatcherCondition::HOST_SUFFIX, m1.criterion());
+ EXPECT_EQ(&pattern, m1.string_pattern());
+
+ URLMatcherCondition m2;
+ m2 = m1;
+ EXPECT_EQ(URLMatcherCondition::HOST_SUFFIX, m2.criterion());
+ EXPECT_EQ(&pattern, m2.string_pattern());
+
+ URLMatcherCondition m3(m1);
+ EXPECT_EQ(URLMatcherCondition::HOST_SUFFIX, m3.criterion());
+ EXPECT_EQ(&pattern, m3.string_pattern());
+}
+
+TEST(URLMatcherSchemeFilter, TestMatching) {
+ URLMatcherSchemeFilter filter1("https");
+ std::vector<std::string> filter2_content;
+ filter2_content.push_back("http");
+ filter2_content.push_back("https");
+ URLMatcherSchemeFilter filter2(filter2_content);
+
+ GURL matching_url("https://www.foobar.com");
+ GURL non_matching_url("http://www.foobar.com");
+ EXPECT_TRUE(filter1.IsMatch(matching_url));
+ EXPECT_FALSE(filter1.IsMatch(non_matching_url));
+ EXPECT_TRUE(filter2.IsMatch(matching_url));
+ EXPECT_TRUE(filter2.IsMatch(non_matching_url));
+}
+
+TEST(URLMatcherPortFilter, TestMatching) {
+ std::vector<URLMatcherPortFilter::Range> ranges;
+ ranges.push_back(URLMatcherPortFilter::CreateRange(80, 90));
+ ranges.push_back(URLMatcherPortFilter::CreateRange(8080));
+ URLMatcherPortFilter filter(ranges);
+ EXPECT_TRUE(filter.IsMatch(GURL("http://www.example.com")));
+ EXPECT_TRUE(filter.IsMatch(GURL("http://www.example.com:80")));
+ EXPECT_TRUE(filter.IsMatch(GURL("http://www.example.com:81")));
+ EXPECT_TRUE(filter.IsMatch(GURL("http://www.example.com:90")));
+ EXPECT_TRUE(filter.IsMatch(GURL("http://www.example.com:8080")));
+ EXPECT_FALSE(filter.IsMatch(GURL("http://www.example.com:79")));
+ EXPECT_FALSE(filter.IsMatch(GURL("http://www.example.com:91")));
+ EXPECT_FALSE(filter.IsMatch(GURL("https://www.example.com")));
+}
+
+TEST(URLMatcherConditionTest, IsFullURLCondition) {
+ StringPattern pattern("example.com", 1);
+ EXPECT_FALSE(URLMatcherCondition(URLMatcherCondition::HOST_SUFFIX,
+ &pattern).IsFullURLCondition());
+
+ EXPECT_TRUE(URLMatcherCondition(URLMatcherCondition::HOST_CONTAINS,
+ &pattern).IsFullURLCondition());
+ EXPECT_TRUE(URLMatcherCondition(URLMatcherCondition::PATH_CONTAINS,
+ &pattern).IsFullURLCondition());
+ EXPECT_TRUE(URLMatcherCondition(URLMatcherCondition::QUERY_CONTAINS,
+ &pattern).IsFullURLCondition());
+
+ EXPECT_TRUE(URLMatcherCondition(URLMatcherCondition::URL_PREFIX,
+ &pattern).IsFullURLCondition());
+ EXPECT_TRUE(URLMatcherCondition(URLMatcherCondition::URL_SUFFIX,
+ &pattern).IsFullURLCondition());
+ EXPECT_TRUE(URLMatcherCondition(URLMatcherCondition::URL_CONTAINS,
+ &pattern).IsFullURLCondition());
+ EXPECT_TRUE(URLMatcherCondition(URLMatcherCondition::URL_EQUALS,
+ &pattern).IsFullURLCondition());
+}
+
+TEST(URLMatcherConditionTest, IsMatch) {
+ GURL url1("http://www.example.com/www.foobar.com/index.html");
+ GURL url2("http://www.foobar.com/example.com/index.html");
+
+ StringPattern pattern("example.com", 1);
+ URLMatcherCondition m1(URLMatcherCondition::HOST_SUFFIX, &pattern);
+
+ std::set<StringPattern::ID> matching_patterns;
+
+ // matches = {0} --> matcher did not indicate that m1 was a match.
+ matching_patterns.insert(0);
+ EXPECT_FALSE(m1.IsMatch(matching_patterns, url1));
+
+ // matches = {0, 1} --> matcher did indicate that m1 was a match.
+ matching_patterns.insert(1);
+ EXPECT_TRUE(m1.IsMatch(matching_patterns, url1));
+
+ // For m2 we use a HOST_CONTAINS test, which requires a post-validation
+ // whether the match reported by the SubstringSetMatcher occurs really
+ // in the correct url component.
+ URLMatcherCondition m2(URLMatcherCondition::HOST_CONTAINS, &pattern);
+ EXPECT_TRUE(m2.IsMatch(matching_patterns, url1));
+ EXPECT_FALSE(m2.IsMatch(matching_patterns, url2));
+}
+
+TEST(URLMatcherConditionTest, Comparison) {
+ StringPattern p1("foobar.com", 1);
+ StringPattern p2("foobar.com", 2);
+ // The first component of each test is expected to be < than the second.
+ URLMatcherCondition test_smaller[][2] = {
+ {URLMatcherCondition(URLMatcherCondition::HOST_PREFIX, &p1),
+ URLMatcherCondition(URLMatcherCondition::HOST_SUFFIX, &p1)},
+ {URLMatcherCondition(URLMatcherCondition::HOST_PREFIX, &p1),
+ URLMatcherCondition(URLMatcherCondition::HOST_PREFIX, &p2)},
+ {URLMatcherCondition(URLMatcherCondition::HOST_PREFIX, NULL),
+ URLMatcherCondition(URLMatcherCondition::HOST_PREFIX, &p2)},
+ {URLMatcherCondition(URLMatcherCondition::HOST_PREFIX, &p1),
+ URLMatcherCondition(URLMatcherCondition::HOST_SUFFIX, NULL)},
+ };
+ for (size_t i = 0; i < arraysize(test_smaller); ++i) {
+ EXPECT_TRUE(test_smaller[i][0] < test_smaller[i][1])
+ << "Test " << i << " of test_smaller failed";
+ EXPECT_FALSE(test_smaller[i][1] < test_smaller[i][0])
+ << "Test " << i << " of test_smaller failed";
+ }
+ URLMatcherCondition test_equal[][2] = {
+ {URLMatcherCondition(URLMatcherCondition::HOST_PREFIX, &p1),
+ URLMatcherCondition(URLMatcherCondition::HOST_PREFIX, &p1)},
+ {URLMatcherCondition(URLMatcherCondition::HOST_PREFIX, NULL),
+ URLMatcherCondition(URLMatcherCondition::HOST_PREFIX, NULL)},
+ };
+ for (size_t i = 0; i < arraysize(test_equal); ++i) {
+ EXPECT_FALSE(test_equal[i][0] < test_equal[i][1])
+ << "Test " << i << " of test_equal failed";
+ EXPECT_FALSE(test_equal[i][1] < test_equal[i][0])
+ << "Test " << i << " of test_equal failed";
+ }
+}
+
+//
+// URLMatcherConditionFactory
+//
+
+namespace {
+
+bool Matches(const URLMatcherCondition& condition, std::string text) {
+ return text.find(condition.string_pattern()->pattern()) !=
+ std::string::npos;
+}
+
+} // namespace
+
+TEST(URLMatcherConditionFactoryTest, GURLCharacterSet) {
+ // GURL guarantees that neither domain, nor path, nor query may contain
+ // non ASCII-7 characters. We test this here, because a change to this
+ // guarantee breaks this implementation horribly.
+ GURL url("http://www.föö.com/föö?föö#föö");
+ EXPECT_TRUE(IsStringASCII(url.host()));
+ EXPECT_TRUE(IsStringASCII(url.path()));
+ EXPECT_TRUE(IsStringASCII(url.query()));
+ EXPECT_FALSE(IsStringASCII(url.ref()));
+}
+
+TEST(URLMatcherConditionFactoryTest, Criteria) {
+ URLMatcherConditionFactory factory;
+ EXPECT_EQ(URLMatcherCondition::HOST_PREFIX,
+ factory.CreateHostPrefixCondition("foo").criterion());
+ EXPECT_EQ(URLMatcherCondition::HOST_SUFFIX,
+ factory.CreateHostSuffixCondition("foo").criterion());
+ EXPECT_EQ(URLMatcherCondition::HOST_CONTAINS,
+ factory.CreateHostContainsCondition("foo").criterion());
+ EXPECT_EQ(URLMatcherCondition::HOST_EQUALS,
+ factory.CreateHostEqualsCondition("foo").criterion());
+ EXPECT_EQ(URLMatcherCondition::PATH_PREFIX,
+ factory.CreatePathPrefixCondition("foo").criterion());
+ EXPECT_EQ(URLMatcherCondition::PATH_SUFFIX,
+ factory.CreatePathSuffixCondition("foo").criterion());
+ EXPECT_EQ(URLMatcherCondition::PATH_CONTAINS,
+ factory.CreatePathContainsCondition("foo").criterion());
+ EXPECT_EQ(URLMatcherCondition::PATH_EQUALS,
+ factory.CreatePathEqualsCondition("foo").criterion());
+ EXPECT_EQ(URLMatcherCondition::QUERY_PREFIX,
+ factory.CreateQueryPrefixCondition("foo").criterion());
+ EXPECT_EQ(URLMatcherCondition::QUERY_SUFFIX,
+ factory.CreateQuerySuffixCondition("foo").criterion());
+ EXPECT_EQ(URLMatcherCondition::QUERY_CONTAINS,
+ factory.CreateQueryContainsCondition("foo").criterion());
+ EXPECT_EQ(URLMatcherCondition::QUERY_EQUALS,
+ factory.CreateQueryEqualsCondition("foo").criterion());
+ EXPECT_EQ(URLMatcherCondition::HOST_SUFFIX_PATH_PREFIX,
+ factory.CreateHostSuffixPathPrefixCondition("foo",
+ "bar").criterion());
+ EXPECT_EQ(URLMatcherCondition::HOST_EQUALS_PATH_PREFIX,
+ factory.CreateHostEqualsPathPrefixCondition("foo",
+ "bar").criterion());
+ EXPECT_EQ(URLMatcherCondition::URL_PREFIX,
+ factory.CreateURLPrefixCondition("foo").criterion());
+ EXPECT_EQ(URLMatcherCondition::URL_SUFFIX,
+ factory.CreateURLSuffixCondition("foo").criterion());
+ EXPECT_EQ(URLMatcherCondition::URL_CONTAINS,
+ factory.CreateURLContainsCondition("foo").criterion());
+ EXPECT_EQ(URLMatcherCondition::URL_EQUALS,
+ factory.CreateURLEqualsCondition("foo").criterion());
+ EXPECT_EQ(URLMatcherCondition::URL_MATCHES,
+ factory.CreateURLMatchesCondition("foo").criterion());
+}
+
+TEST(URLMatcherConditionFactoryTest, TestSingletonProperty) {
+ URLMatcherConditionFactory factory;
+ URLMatcherCondition c1 = factory.CreateHostEqualsCondition("www.google.com");
+ URLMatcherCondition c2 = factory.CreateHostEqualsCondition("www.google.com");
+ EXPECT_EQ(c1.criterion(), c2.criterion());
+ EXPECT_EQ(c1.string_pattern(), c2.string_pattern());
+ URLMatcherCondition c3 = factory.CreateHostEqualsCondition("www.google.de");
+ EXPECT_EQ(c2.criterion(), c3.criterion());
+ EXPECT_NE(c2.string_pattern(), c3.string_pattern());
+ EXPECT_NE(c2.string_pattern()->id(), c3.string_pattern()->id());
+ EXPECT_NE(c2.string_pattern()->pattern(),
+ c3.string_pattern()->pattern());
+ URLMatcherCondition c4 = factory.CreateURLMatchesCondition("www.google.com");
+ URLMatcherCondition c5 = factory.CreateURLContainsCondition("www.google.com");
+ // Regex patterns and substring patterns do not share IDs.
+ EXPECT_EQ(c5.string_pattern()->pattern(), c4.string_pattern()->pattern());
+ EXPECT_NE(c5.string_pattern(), c4.string_pattern());
+ EXPECT_NE(c5.string_pattern()->id(), c4.string_pattern()->id());
+
+ // Check that all StringPattern singletons are freed if we call
+ // ForgetUnusedPatterns.
+ StringPattern::ID old_id_1 = c1.string_pattern()->id();
+ StringPattern::ID old_id_4 = c4.string_pattern()->id();
+ factory.ForgetUnusedPatterns(std::set<StringPattern::ID>());
+ EXPECT_TRUE(factory.IsEmpty());
+ URLMatcherCondition c6 = factory.CreateHostEqualsCondition("www.google.com");
+ EXPECT_NE(old_id_1, c6.string_pattern()->id());
+ URLMatcherCondition c7 = factory.CreateURLMatchesCondition("www.google.com");
+ EXPECT_NE(old_id_4, c7.string_pattern()->id());
+}
+
+TEST(URLMatcherConditionFactoryTest, TestComponentSearches) {
+ GURL gurl("https://www.google.com:1234/webhp?sourceid=chrome-instant&ie=UTF-8"
+ "&ion=1#hl=en&output=search&sclient=psy-ab&q=chrome%20is%20awesome");
+ URLMatcherConditionFactory factory;
+ std::string url = factory.CanonicalizeURLForComponentSearches(gurl);
+
+ // Test host component.
+ EXPECT_TRUE(Matches(factory.CreateHostPrefixCondition(std::string()), url));
+ EXPECT_TRUE(Matches(factory.CreateHostPrefixCondition("www.goog"), url));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostPrefixCondition("www.google.com"), url));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostPrefixCondition(".www.google.com"), url));
+ EXPECT_FALSE(Matches(factory.CreateHostPrefixCondition("google.com"), url));
+ EXPECT_FALSE(
+ Matches(factory.CreateHostPrefixCondition("www.google.com/"), url));
+ EXPECT_FALSE(Matches(factory.CreateHostPrefixCondition("webhp"), url));
+
+ EXPECT_TRUE(Matches(factory.CreateHostSuffixCondition(std::string()), url));
+ EXPECT_TRUE(Matches(factory.CreateHostSuffixCondition("com"), url));
+ EXPECT_TRUE(Matches(factory.CreateHostSuffixCondition(".com"), url));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostSuffixCondition("www.google.com"), url));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostSuffixCondition(".www.google.com"), url));
+ EXPECT_FALSE(Matches(factory.CreateHostSuffixCondition("www"), url));
+ EXPECT_FALSE(
+ Matches(factory.CreateHostSuffixCondition("www.google.com/"), url));
+ EXPECT_FALSE(Matches(factory.CreateHostSuffixCondition("webhp"), url));
+
+ EXPECT_FALSE(Matches(factory.CreateHostEqualsCondition(std::string()), url));
+ EXPECT_FALSE(Matches(factory.CreateHostEqualsCondition("www"), url));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostEqualsCondition("www.google.com"), url));
+ EXPECT_FALSE(
+ Matches(factory.CreateHostEqualsCondition("www.google.com/"), url));
+
+
+ // Test path component.
+ EXPECT_TRUE(Matches(factory.CreatePathPrefixCondition(std::string()), url));
+ EXPECT_TRUE(Matches(factory.CreatePathPrefixCondition("/web"), url));
+ EXPECT_TRUE(Matches(factory.CreatePathPrefixCondition("/webhp"), url));
+ EXPECT_FALSE(Matches(factory.CreatePathPrefixCondition("webhp"), url));
+ EXPECT_FALSE(Matches(factory.CreatePathPrefixCondition("/webhp?"), url));
+ EXPECT_FALSE(Matches(factory.CreatePathPrefixCondition("?sourceid"), url));
+
+ EXPECT_TRUE(Matches(factory.CreatePathSuffixCondition(std::string()), url));
+ EXPECT_TRUE(Matches(factory.CreatePathSuffixCondition("webhp"), url));
+ EXPECT_TRUE(Matches(factory.CreatePathSuffixCondition("/webhp"), url));
+ EXPECT_FALSE(Matches(factory.CreatePathSuffixCondition("/web"), url));
+ EXPECT_FALSE(Matches(factory.CreatePathSuffixCondition("/webhp?"), url));
+
+ EXPECT_TRUE(Matches(factory.CreatePathEqualsCondition("/webhp"), url));
+ EXPECT_FALSE(Matches(factory.CreatePathEqualsCondition("webhp"), url));
+ EXPECT_FALSE(Matches(factory.CreatePathEqualsCondition("/webhp?"), url));
+ EXPECT_FALSE(
+ Matches(factory.CreatePathEqualsCondition("www.google.com"), url));
+
+
+ // Test query component.
+ EXPECT_TRUE(Matches(factory.CreateQueryPrefixCondition(std::string()), url));
+ EXPECT_TRUE(Matches(factory.CreateQueryPrefixCondition("sourceid"), url));
+ // The '?' at the beginning is just ignored.
+ EXPECT_TRUE(Matches(factory.CreateQueryPrefixCondition("?sourceid"), url));
+
+ EXPECT_TRUE(Matches(factory.CreateQuerySuffixCondition(std::string()), url));
+ EXPECT_TRUE(Matches(factory.CreateQuerySuffixCondition("ion=1"), url));
+ EXPECT_FALSE(Matches(factory.CreateQuerySuffixCondition("www"), url));
+ // "Suffix" condition + pattern starting with '?' = "equals" condition.
+ EXPECT_FALSE(Matches(factory.CreateQuerySuffixCondition(
+ "?sourceid=chrome-instant&ie=UTF-8&ion="), url));
+ EXPECT_TRUE(Matches(factory.CreateQuerySuffixCondition(
+ "?sourceid=chrome-instant&ie=UTF-8&ion=1"), url));
+
+ EXPECT_FALSE(Matches(factory.CreateQueryEqualsCondition(
+ "?sourceid=chrome-instant&ie=UTF-8&ion="), url));
+ EXPECT_FALSE(Matches(factory.CreateQueryEqualsCondition(
+ "sourceid=chrome-instant&ie=UTF-8&ion="), url));
+ EXPECT_TRUE(Matches(factory.CreateQueryEqualsCondition(
+ "sourceid=chrome-instant&ie=UTF-8&ion=1"), url));
+ // The '?' at the beginning is just ignored.
+ EXPECT_TRUE(Matches(factory.CreateQueryEqualsCondition(
+ "?sourceid=chrome-instant&ie=UTF-8&ion=1"), url));
+ EXPECT_FALSE(
+ Matches(factory.CreateQueryEqualsCondition("www.google.com"), url));
+
+
+ // Test adjacent components
+ EXPECT_TRUE(Matches(factory.CreateHostSuffixPathPrefixCondition(
+ "google.com", "/webhp"), url));
+ EXPECT_TRUE(Matches(
+ factory.CreateHostSuffixPathPrefixCondition(std::string(), "/webhp"),
+ url));
+ EXPECT_TRUE(Matches(
+ factory.CreateHostSuffixPathPrefixCondition("google.com", std::string()),
+ url));
+ EXPECT_FALSE(Matches(
+ factory.CreateHostSuffixPathPrefixCondition("www", std::string()), url));
+
+ EXPECT_TRUE(Matches(factory.CreateHostEqualsPathPrefixCondition(
+ "www.google.com", "/webhp"), url));
+ EXPECT_FALSE(Matches(
+ factory.CreateHostEqualsPathPrefixCondition(std::string(), "/webhp"),
+ url));
+ EXPECT_TRUE(Matches(factory.CreateHostEqualsPathPrefixCondition(
+ "www.google.com", std::string()),
+ url));
+ EXPECT_FALSE(Matches(
+ factory.CreateHostEqualsPathPrefixCondition("google.com", std::string()),
+ url));
+}
+
+TEST(URLMatcherConditionFactoryTest, TestFullSearches) {
+ // The Port 443 is stripped because it is the default port for https.
+ GURL gurl("https://www.google.com:443/webhp?sourceid=chrome-instant&ie=UTF-8"
+ "&ion=1#hl=en&output=search&sclient=psy-ab&q=chrome%20is%20awesome");
+ URLMatcherConditionFactory factory;
+ std::string url = factory.CanonicalizeURLForFullSearches(gurl);
+
+ EXPECT_TRUE(Matches(factory.CreateURLPrefixCondition(std::string()), url));
+ EXPECT_TRUE(
+ Matches(factory.CreateURLPrefixCondition("https://www.goog"), url));
+ EXPECT_TRUE(Matches(factory.CreateURLPrefixCondition(
+ "https://www.google.com"), url));
+ EXPECT_TRUE(Matches(factory.CreateURLPrefixCondition(
+ "https://www.google.com/webhp?"), url));
+ EXPECT_FALSE(Matches(factory.CreateURLPrefixCondition(
+ "http://www.google.com"), url));
+ EXPECT_FALSE(Matches(factory.CreateURLPrefixCondition("webhp"), url));
+
+ EXPECT_TRUE(Matches(factory.CreateURLSuffixCondition(std::string()), url));
+ EXPECT_TRUE(Matches(factory.CreateURLSuffixCondition("ion=1"), url));
+ EXPECT_FALSE(Matches(factory.CreateURLSuffixCondition("www"), url));
+
+ EXPECT_TRUE(Matches(factory.CreateURLContainsCondition(std::string()), url));
+ EXPECT_TRUE(Matches(factory.CreateURLContainsCondition("www.goog"), url));
+ EXPECT_TRUE(Matches(factory.CreateURLContainsCondition("webhp"), url));
+ EXPECT_TRUE(Matches(factory.CreateURLContainsCondition("?"), url));
+ EXPECT_TRUE(Matches(factory.CreateURLContainsCondition("sourceid"), url));
+ EXPECT_TRUE(Matches(factory.CreateURLContainsCondition("ion=1"), url));
+ EXPECT_FALSE(Matches(factory.CreateURLContainsCondition(".www.goog"), url));
+ EXPECT_FALSE(Matches(factory.CreateURLContainsCondition("foobar"), url));
+ EXPECT_FALSE(Matches(factory.CreateURLContainsCondition("search"), url));
+ EXPECT_FALSE(Matches(factory.CreateURLContainsCondition(":443"), url));
+
+ EXPECT_TRUE(Matches(factory.CreateURLEqualsCondition(
+ "https://www.google.com/webhp?sourceid=chrome-instant&ie=UTF-8&ion=1"),
+ url));
+ EXPECT_FALSE(
+ Matches(factory.CreateURLEqualsCondition("https://www.google.com"), url));
+
+ // Same as above but this time with a non-standard port.
+ gurl = GURL("https://www.google.com:1234/webhp?sourceid=chrome-instant&"
+ "ie=UTF-8&ion=1#hl=en&output=search&sclient=psy-ab&q=chrome%20is%20"
+ "awesome");
+ url = factory.CanonicalizeURLForFullSearches(gurl);
+ EXPECT_TRUE(Matches(factory.CreateURLPrefixCondition(
+ "https://www.google.com:1234/webhp?"), url));
+ EXPECT_TRUE(Matches(factory.CreateURLContainsCondition(":1234"), url));
+}
+
+//
+// URLMatcherConditionSet
+//
+
+TEST(URLMatcherConditionSetTest, Constructor) {
+ URLMatcherConditionFactory factory;
+ URLMatcherCondition m1 = factory.CreateHostSuffixCondition("example.com");
+ URLMatcherCondition m2 = factory.CreatePathContainsCondition("foo");
+
+ std::set<URLMatcherCondition> conditions;
+ conditions.insert(m1);
+ conditions.insert(m2);
+
+ scoped_refptr<URLMatcherConditionSet> condition_set(
+ new URLMatcherConditionSet(1, conditions));
+ EXPECT_EQ(1, condition_set->id());
+ EXPECT_EQ(2u, condition_set->conditions().size());
+}
+
+TEST(URLMatcherConditionSetTest, Matching) {
+ GURL url1("http://www.example.com/foo?bar=1");
+ GURL url2("http://foo.example.com/index.html");
+ GURL url3("http://www.example.com:80/foo?bar=1");
+ GURL url4("http://www.example.com:8080/foo?bar=1");
+
+ URLMatcherConditionFactory factory;
+ URLMatcherCondition m1 = factory.CreateHostSuffixCondition("example.com");
+ URLMatcherCondition m2 = factory.CreatePathContainsCondition("foo");
+
+ std::set<URLMatcherCondition> conditions;
+ conditions.insert(m1);
+ conditions.insert(m2);
+
+ scoped_refptr<URLMatcherConditionSet> condition_set(
+ new URLMatcherConditionSet(1, conditions));
+ EXPECT_EQ(1, condition_set->id());
+ EXPECT_EQ(2u, condition_set->conditions().size());
+
+ std::set<StringPattern::ID> matching_patterns;
+ matching_patterns.insert(m1.string_pattern()->id());
+ EXPECT_FALSE(condition_set->IsMatch(matching_patterns, url1));
+
+ matching_patterns.insert(m2.string_pattern()->id());
+ EXPECT_TRUE(condition_set->IsMatch(matching_patterns, url1));
+ EXPECT_FALSE(condition_set->IsMatch(matching_patterns, url2));
+
+ // Test scheme filters.
+ scoped_refptr<URLMatcherConditionSet> condition_set2(
+ new URLMatcherConditionSet(1,
+ conditions,
+ scoped_ptr<URLMatcherSchemeFilter>(
+ new URLMatcherSchemeFilter("https")),
+ scoped_ptr<URLMatcherPortFilter>()));
+ EXPECT_FALSE(condition_set2->IsMatch(matching_patterns, url1));
+ scoped_refptr<URLMatcherConditionSet> condition_set3(
+ new URLMatcherConditionSet(1,
+ conditions,
+ scoped_ptr<URLMatcherSchemeFilter>(
+ new URLMatcherSchemeFilter("http")),
+ scoped_ptr<URLMatcherPortFilter>()));
+ EXPECT_TRUE(condition_set3->IsMatch(matching_patterns, url1));
+
+ // Test port filters.
+ std::vector<URLMatcherPortFilter::Range> ranges;
+ ranges.push_back(URLMatcherPortFilter::CreateRange(80));
+ scoped_ptr<URLMatcherPortFilter> filter(new URLMatcherPortFilter(ranges));
+ scoped_refptr<URLMatcherConditionSet> condition_set4(
+ new URLMatcherConditionSet(
+ 1, conditions, scoped_ptr<URLMatcherSchemeFilter>(), filter.Pass()));
+ EXPECT_TRUE(condition_set4->IsMatch(matching_patterns, url1));
+ EXPECT_TRUE(condition_set4->IsMatch(matching_patterns, url3));
+ EXPECT_FALSE(condition_set4->IsMatch(matching_patterns, url4));
+
+ // Test regex patterns.
+ matching_patterns.clear();
+ URLMatcherCondition r1 = factory.CreateURLMatchesCondition("/fo?oo");
+ std::set<URLMatcherCondition> regex_conditions;
+ regex_conditions.insert(r1);
+ scoped_refptr<URLMatcherConditionSet> condition_set5(
+ new URLMatcherConditionSet(1, regex_conditions));
+ EXPECT_FALSE(condition_set5->IsMatch(matching_patterns, url1));
+ matching_patterns.insert(r1.string_pattern()->id());
+ EXPECT_TRUE(condition_set5->IsMatch(matching_patterns, url1));
+
+ regex_conditions.insert(m1);
+ scoped_refptr<URLMatcherConditionSet> condition_set6(
+ new URLMatcherConditionSet(1, regex_conditions));
+ EXPECT_FALSE(condition_set6->IsMatch(matching_patterns, url1));
+ matching_patterns.insert(m1.string_pattern()->id());
+ EXPECT_TRUE(condition_set6->IsMatch(matching_patterns, url1));
+
+ matching_patterns.clear();
+ regex_conditions.clear();
+ URLMatcherCondition r2 = factory.CreateOriginAndPathMatchesCondition("b[a]r");
+ regex_conditions.insert(r2);
+ scoped_refptr<URLMatcherConditionSet> condition_set7(
+ new URLMatcherConditionSet(1, regex_conditions));
+ EXPECT_FALSE(condition_set7->IsMatch(matching_patterns, url1));
+ matching_patterns.insert(r2.string_pattern()->id());
+ EXPECT_TRUE(condition_set7->IsMatch(matching_patterns, url1));
+}
+
+
+//
+// URLMatcher
+//
+
+TEST(URLMatcherTest, FullTest) {
+ GURL url1("http://www.example.com/foo?bar=1");
+ GURL url2("http://foo.example.com/index.html");
+
+ URLMatcher matcher;
+ URLMatcherConditionFactory* factory = matcher.condition_factory();
+
+ // First insert.
+ URLMatcherConditionSet::Conditions conditions1;
+ conditions1.insert(factory->CreateHostSuffixCondition("example.com"));
+ conditions1.insert(factory->CreatePathContainsCondition("foo"));
+
+ const int kConditionSetId1 = 1;
+ URLMatcherConditionSet::Vector insert1;
+ insert1.push_back(make_scoped_refptr(
+ new URLMatcherConditionSet(kConditionSetId1, conditions1)));
+ matcher.AddConditionSets(insert1);
+ EXPECT_EQ(1u, matcher.MatchURL(url1).size());
+ EXPECT_EQ(0u, matcher.MatchURL(url2).size());
+
+ // Second insert.
+ URLMatcherConditionSet::Conditions conditions2;
+ conditions2.insert(factory->CreateHostSuffixCondition("example.com"));
+
+ const int kConditionSetId2 = 2;
+ URLMatcherConditionSet::Vector insert2;
+ insert2.push_back(make_scoped_refptr(
+ new URLMatcherConditionSet(kConditionSetId2, conditions2)));
+ matcher.AddConditionSets(insert2);
+ EXPECT_EQ(2u, matcher.MatchURL(url1).size());
+ EXPECT_EQ(1u, matcher.MatchURL(url2).size());
+
+ // This should be the cached singleton.
+ int patternId1 = factory->CreateHostSuffixCondition(
+ "example.com").string_pattern()->id();
+
+ // Third insert.
+ URLMatcherConditionSet::Conditions conditions3;
+ conditions3.insert(factory->CreateHostSuffixCondition("example.com"));
+ conditions3.insert(factory->CreateURLMatchesCondition("x.*[0-9]"));
+
+ const int kConditionSetId3 = 3;
+ URLMatcherConditionSet::Vector insert3;
+ insert3.push_back(make_scoped_refptr(
+ new URLMatcherConditionSet(kConditionSetId3, conditions3)));
+ matcher.AddConditionSets(insert3);
+ EXPECT_EQ(3u, matcher.MatchURL(url1).size());
+ EXPECT_EQ(1u, matcher.MatchURL(url2).size());
+
+ // Removal of third insert.
+ std::vector<URLMatcherConditionSet::ID> remove3;
+ remove3.push_back(kConditionSetId3);
+ matcher.RemoveConditionSets(remove3);
+ EXPECT_EQ(2u, matcher.MatchURL(url1).size());
+ EXPECT_EQ(1u, matcher.MatchURL(url2).size());
+
+ // Removal of second insert.
+ std::vector<URLMatcherConditionSet::ID> remove2;
+ remove2.push_back(kConditionSetId2);
+ matcher.RemoveConditionSets(remove2);
+ EXPECT_EQ(1u, matcher.MatchURL(url1).size());
+ EXPECT_EQ(0u, matcher.MatchURL(url2).size());
+
+ // Removal of first insert.
+ std::vector<URLMatcherConditionSet::ID> remove1;
+ remove1.push_back(kConditionSetId1);
+ matcher.RemoveConditionSets(remove1);
+ EXPECT_EQ(0u, matcher.MatchURL(url1).size());
+ EXPECT_EQ(0u, matcher.MatchURL(url2).size());
+
+ EXPECT_TRUE(matcher.IsEmpty());
+
+ // The cached singleton in matcher.condition_factory_ should be destroyed to
+ // free memory.
+ int patternId2 = factory->CreateHostSuffixCondition(
+ "example.com").string_pattern()->id();
+ // If patternId1 and patternId2 are different that indicates that
+ // matcher.condition_factory_ does not leak memory by holding onto
+ // unused patterns.
+ EXPECT_NE(patternId1, patternId2);
+}
+
+TEST(URLMatcherTest, TestComponentsImplyContains) {
+ // Due to a different implementation of component (prefix, suffix and equals)
+ // and *Contains conditions we need to check that when a pattern matches a
+ // given part of a URL as equal, prefix or suffix, it also matches it in the
+ // "contains" test.
+ GURL url("https://www.google.com:1234/webhp?test=val&a=b");
+
+ URLMatcher matcher;
+ URLMatcherConditionFactory* factory = matcher.condition_factory();
+
+ URLMatcherConditionSet::Conditions conditions;
+
+ // First insert all the matching equals => contains pairs.
+ conditions.insert(factory->CreateHostEqualsCondition("www.google.com"));
+ conditions.insert(factory->CreateHostContainsCondition("www.google.com"));
+
+ conditions.insert(factory->CreateHostPrefixCondition("www."));
+ conditions.insert(factory->CreateHostContainsCondition("www."));
+
+ conditions.insert(factory->CreateHostSuffixCondition("com"));
+ conditions.insert(factory->CreateHostContainsCondition("com"));
+
+ conditions.insert(factory->CreatePathEqualsCondition("/webhp"));
+ conditions.insert(factory->CreatePathContainsCondition("/webhp"));
+
+ conditions.insert(factory->CreatePathPrefixCondition("/we"));
+ conditions.insert(factory->CreatePathContainsCondition("/we"));
+
+ conditions.insert(factory->CreatePathSuffixCondition("hp"));
+ conditions.insert(factory->CreatePathContainsCondition("hp"));
+
+ conditions.insert(factory->CreateQueryEqualsCondition("test=val&a=b"));
+ conditions.insert(factory->CreateQueryContainsCondition("test=val&a=b"));
+
+ conditions.insert(factory->CreateQueryPrefixCondition("test=v"));
+ conditions.insert(factory->CreateQueryContainsCondition("test=v"));
+
+ conditions.insert(factory->CreateQuerySuffixCondition("l&a=b"));
+ conditions.insert(factory->CreateQueryContainsCondition("l&a=b"));
+
+ // The '?' for equality is just ignored.
+ conditions.insert(factory->CreateQueryEqualsCondition("?test=val&a=b"));
+ // Due to '?' the condition created here is a prefix-testing condition.
+ conditions.insert(factory->CreateQueryContainsCondition("?test=val&a=b"));
+
+ const int kConditionSetId = 1;
+ URLMatcherConditionSet::Vector insert;
+ insert.push_back(make_scoped_refptr(
+ new URLMatcherConditionSet(kConditionSetId, conditions)));
+ matcher.AddConditionSets(insert);
+ EXPECT_EQ(1u, matcher.MatchURL(url).size());
+}
+
+// Check that matches in everything but the query are found.
+TEST(URLMatcherTest, TestOriginAndPathRegExPositive) {
+ GURL url("https://www.google.com:1234/webhp?test=val&a=b");
+
+ URLMatcher matcher;
+ URLMatcherConditionFactory* factory = matcher.condition_factory();
+
+ URLMatcherConditionSet::Conditions conditions;
+
+ conditions.insert(factory->CreateOriginAndPathMatchesCondition("w..hp"));
+ const int kConditionSetId = 1;
+ URLMatcherConditionSet::Vector insert;
+ insert.push_back(make_scoped_refptr(
+ new URLMatcherConditionSet(kConditionSetId, conditions)));
+ matcher.AddConditionSets(insert);
+ EXPECT_EQ(1u, matcher.MatchURL(url).size());
+}
+
+// Check that matches in the query are ignored.
+TEST(URLMatcherTest, TestOriginAndPathRegExNegative) {
+ GURL url("https://www.google.com:1234/webhp?test=val&a=b");
+
+ URLMatcher matcher;
+ URLMatcherConditionFactory* factory = matcher.condition_factory();
+
+ URLMatcherConditionSet::Conditions conditions;
+
+ conditions.insert(factory->CreateOriginAndPathMatchesCondition("val"));
+ const int kConditionSetId = 1;
+ URLMatcherConditionSet::Vector insert;
+ insert.push_back(make_scoped_refptr(
+ new URLMatcherConditionSet(kConditionSetId, conditions)));
+ matcher.AddConditionSets(insert);
+ EXPECT_EQ(0u, matcher.MatchURL(url).size());
+}
+
+} // namespace extensions
diff --git a/chromium/extensions/common/one_shot_event.cc b/chromium/extensions/common/one_shot_event.cc
new file mode 100644
index 00000000000..318acad5c6b
--- /dev/null
+++ b/chromium/extensions/common/one_shot_event.cc
@@ -0,0 +1,70 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/common/one_shot_event.h"
+
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/task_runner.h"
+
+using base::TaskRunner;
+
+namespace extensions {
+
+struct OneShotEvent::TaskInfo {
+ TaskInfo() {}
+ TaskInfo(const tracked_objects::Location& from_here,
+ const scoped_refptr<TaskRunner>& runner,
+ const base::Closure& task)
+ : from_here(from_here), runner(runner), task(task) {
+ CHECK(runner.get()); // Detect mistakes with a decent stack frame.
+ }
+ tracked_objects::Location from_here;
+ scoped_refptr<TaskRunner> runner;
+ base::Closure task;
+};
+
+OneShotEvent::OneShotEvent() : signaled_(false) {
+ // It's acceptable to construct the OneShotEvent on one thread, but
+ // immediately move it to another thread.
+ thread_checker_.DetachFromThread();
+}
+OneShotEvent::~OneShotEvent() {}
+
+void OneShotEvent::Post(const tracked_objects::Location& from_here,
+ const base::Closure& task) const {
+ Post(from_here, task, base::MessageLoopProxy::current());
+}
+
+void OneShotEvent::Post(const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ const scoped_refptr<TaskRunner>& runner) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (is_signaled()) {
+ runner->PostTask(from_here, task);
+ } else {
+ tasks_.push_back(TaskInfo(from_here, runner, task));
+ }
+}
+
+void OneShotEvent::Signal() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ CHECK(!signaled_) << "Only call Signal once.";
+
+ signaled_ = true;
+ // After this point, a call to Post() from one of the queued tasks
+ // could proceed immediately, but the fact that this object is
+ // single-threaded prevents that from being relevant.
+
+ // We could randomize tasks_ in debug mode in order to check that
+ // the order doesn't matter...
+ for (size_t i = 0; i < tasks_.size(); ++i) {
+ tasks_[i].runner->PostTask(tasks_[i].from_here, tasks_[i].task);
+ }
+}
+
+} // namespace extensions
diff --git a/chromium/extensions/common/one_shot_event.h b/chromium/extensions/common/one_shot_event.h
new file mode 100644
index 00000000000..17ab5c88f73
--- /dev/null
+++ b/chromium/extensions/common/one_shot_event.h
@@ -0,0 +1,98 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_COMMON_ONE_SHOT_EVENT_H_
+#define EXTENSIONS_COMMON_ONE_SHOT_EVENT_H_
+
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+
+namespace base {
+class TaskRunner;
+}
+
+namespace tracked_objects {
+class Location;
+}
+
+namespace extensions {
+
+// This class represents an event that's expected to happen once. It
+// allows clients to guarantee that code is run after the OneShotEvent
+// is signaled. If the OneShotEvent is destroyed before it's
+// signaled, the delayed closures are destroyed without being run.
+//
+// This class is similar to a WaitableEvent combined with several
+// WaitableEventWatchers, but using it is simpler.
+//
+// This class is not thread-safe, and must be used from a single thread.
+class OneShotEvent {
+ public:
+ OneShotEvent();
+ ~OneShotEvent();
+
+ // True if Signal has been called. This function is mostly for
+ // migrating old code; usually calling Post() unconditionally will
+ // result in more readable code.
+ bool is_signaled() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return signaled_;
+ }
+
+ // Causes is_signaled() to return true and all queued tasks to be
+ // run in an arbitrary order. This method must only be called once.
+ void Signal();
+
+ // Scheduled |task| to be called on |runner| after is_signaled()
+ // becomes true. Inside |task|, if this OneShotEvent is still
+ // alive, CHECK(is_signaled()) will never fail (which implies that
+ // OneShotEvent::Reset() doesn't exist).
+ //
+ // If |*this| is destroyed before being released, none of these
+ // tasks will be executed.
+ //
+ // Omitting the |runner| argument indicates that |task| should run
+ // on MessageLoopProxy::current().
+ //
+ // Tasks may be run in an arbitrary order, not just FIFO. Tasks
+ // will never be called on the current thread before this function
+ // returns. Beware that there's no simple way to wait for all tasks
+ // on a OneShotEvent to complete, so it's almost never safe to use
+ // base::Unretained() when creating one.
+ //
+ // Const because Post() doesn't modify the logical state of this
+ // object (which is just the is_signaled() bit).
+ void Post(const tracked_objects::Location& from_here,
+ const base::Closure& task) const;
+ void Post(const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ const scoped_refptr<base::TaskRunner>& runner) const;
+
+ private:
+ struct TaskInfo;
+
+ base::ThreadChecker thread_checker_;
+
+ bool signaled_;
+
+ // The task list is mutable because it's not part of the logical
+ // state of the object. This lets us return const references to the
+ // OneShotEvent to clients that just want to run tasks through it
+ // without worrying that they'll signal the event.
+ //
+ // Optimization note: We could reduce the size of this class to a
+ // single pointer by storing |signaled_| in the low bit of a
+ // pointer, and storing the size and capacity of the array (if any)
+ // on the far end of the pointer.
+ mutable std::vector<TaskInfo> tasks_;
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_ONE_SHOT_EVENT_H_
diff --git a/chromium/extensions/common/one_shot_event_unittest.cc b/chromium/extensions/common/one_shot_event_unittest.cc
new file mode 100644
index 00000000000..4009a0106a2
--- /dev/null
+++ b/chromium/extensions/common/one_shot_event_unittest.cc
@@ -0,0 +1,111 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/common/one_shot_event.h"
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/test/test_simple_task_runner.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+
+namespace {
+
+void Increment(int* i) { ++*i; }
+
+TEST(OneShotEventTest, RecordsSignal) {
+ OneShotEvent event;
+ EXPECT_FALSE(event.is_signaled());
+ event.Signal();
+ EXPECT_TRUE(event.is_signaled());
+}
+
+TEST(OneShotEventTest, CallsQueue) {
+ OneShotEvent event;
+ scoped_refptr<base::TestSimpleTaskRunner> runner(
+ new base::TestSimpleTaskRunner);
+ int i = 0;
+ event.Post(FROM_HERE, base::Bind(&Increment, &i), runner);
+ event.Post(FROM_HERE, base::Bind(&Increment, &i), runner);
+ EXPECT_EQ(0U, runner->GetPendingTasks().size());
+ event.Signal();
+ ASSERT_EQ(2U, runner->GetPendingTasks().size());
+ EXPECT_NE(runner->GetPendingTasks()[0].location.line_number(),
+ runner->GetPendingTasks()[1].location.line_number())
+ << "Make sure FROM_HERE is propagated.";
+ EXPECT_EQ(0, i);
+ runner->RunPendingTasks();
+ EXPECT_EQ(2, i);
+}
+
+TEST(OneShotEventTest, CallsAfterSignalDontRunInline) {
+ OneShotEvent event;
+ scoped_refptr<base::TestSimpleTaskRunner> runner(
+ new base::TestSimpleTaskRunner);
+ int i = 0;
+
+ event.Signal();
+ event.Post(FROM_HERE, base::Bind(&Increment, &i), runner);
+ EXPECT_EQ(1U, runner->GetPendingTasks().size());
+ EXPECT_EQ(0, i);
+ runner->RunPendingTasks();
+ EXPECT_EQ(1, i);
+}
+
+TEST(OneShotEventTest, PostDefaultsToCurrentMessageLoop) {
+ OneShotEvent event;
+ scoped_refptr<base::TestSimpleTaskRunner> runner(
+ new base::TestSimpleTaskRunner);
+ base::MessageLoop loop;
+ int runner_i = 0;
+ int loop_i = 0;
+
+ event.Post(FROM_HERE, base::Bind(&Increment, &runner_i), runner);
+ event.Post(FROM_HERE, base::Bind(&Increment, &loop_i));
+ event.Signal();
+ EXPECT_EQ(1U, runner->GetPendingTasks().size());
+ EXPECT_EQ(0, runner_i);
+ runner->RunPendingTasks();
+ EXPECT_EQ(1, runner_i);
+ EXPECT_EQ(0, loop_i);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1, loop_i);
+}
+
+void CheckSignaledAndPostIncrement(
+ OneShotEvent* event,
+ const scoped_refptr<base::TaskRunner>& runner,
+ int* i) {
+ EXPECT_TRUE(event->is_signaled());
+ event->Post(FROM_HERE, base::Bind(&Increment, i), runner);
+}
+
+TEST(OneShotEventTest, IsSignaledAndPostsFromCallbackWork) {
+ OneShotEvent event;
+ scoped_refptr<base::TestSimpleTaskRunner> runner(
+ new base::TestSimpleTaskRunner);
+ int i = 0;
+
+ event.Post(FROM_HERE,
+ base::Bind(&CheckSignaledAndPostIncrement, &event, runner, &i),
+ runner);
+ EXPECT_EQ(0, i);
+ event.Signal();
+
+ // CheckSignaledAndPostIncrement is queued on |runner|.
+ EXPECT_EQ(1U, runner->GetPendingTasks().size());
+ EXPECT_EQ(0, i);
+ runner->RunPendingTasks();
+ // Increment is queued on |runner|.
+ EXPECT_EQ(1U, runner->GetPendingTasks().size());
+ EXPECT_EQ(0, i);
+ runner->RunPendingTasks();
+ // Increment has run.
+ EXPECT_EQ(0U, runner->GetPendingTasks().size());
+ EXPECT_EQ(1, i);
+}
+
+} // namespace
+} // namespace extensions
diff --git a/chromium/extensions/common/permissions/permissions_provider.h b/chromium/extensions/common/permissions/permissions_provider.h
new file mode 100644
index 00000000000..5a27a860419
--- /dev/null
+++ b/chromium/extensions/common/permissions/permissions_provider.h
@@ -0,0 +1,37 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_COMMON_PERMISSIONS_PERMISSIONS_PROVIDER_H_
+#define EXTENSIONS_COMMON_PERMISSIONS_PERMISSIONS_PROVIDER_H_
+
+#include <vector>
+
+namespace extensions {
+
+class APIPermissionInfo;
+
+// The PermissionsProvider creates the APIPermissions instances. It is only
+// needed at startup time.
+class PermissionsProvider {
+ public:
+ // An alias for a given permission |name|.
+ struct AliasInfo {
+ const char* name;
+ const char* alias;
+
+ AliasInfo(const char* name, const char* alias)
+ : name(name), alias(alias) {
+ }
+ };
+ // Returns all the known permissions. The caller, PermissionsInfo,
+ // takes ownership of the APIPermissionInfos.
+ virtual std::vector<APIPermissionInfo*> GetAllPermissions() const = 0;
+
+ // Returns all the known permission aliases.
+ virtual std::vector<AliasInfo> GetAllAliases() const = 0;
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_PERMISSIONS_PERMISSIONS_PROVIDER_H_
diff --git a/chromium/extensions/common/switches.cc b/chromium/extensions/common/switches.cc
new file mode 100644
index 00000000000..228a557fa58
--- /dev/null
+++ b/chromium/extensions/common/switches.cc
@@ -0,0 +1,36 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/common/switches.h"
+
+namespace extensions {
+
+namespace switches {
+
+// Allows the browser to load extensions that lack a modern manifest when that
+// would otherwise be forbidden.
+const char kAllowLegacyExtensionManifests[] =
+ "allow-legacy-extension-manifests";
+
+// Allows injecting extensions and user scripts on the extensions gallery
+// site. Normally prevented for security reasons, but can be useful for
+// automation testing of the gallery.
+const char kAllowScriptingGallery[] = "allow-scripting-gallery";
+
+// Enables extension APIs that are in development.
+const char kEnableExperimentalExtensionApis[] =
+ "enable-experimental-extension-apis";
+
+// Enables extensions running scripts on chrome:// URLs.
+// Extensions still need to explicitly request access to chrome:// URLs in the
+// manifest.
+const char kExtensionsOnChromeURLs[] = "extensions-on-chrome-urls";
+
+// Makes component extensions appear in chrome://settings/extensions.
+const char kShowComponentExtensionOptions[] =
+ "show-component-extension-options";
+
+} // namespace switches
+
+} // namespace extensions
diff --git a/chromium/extensions/common/switches.h b/chromium/extensions/common/switches.h
new file mode 100644
index 00000000000..7fceb54e9d6
--- /dev/null
+++ b/chromium/extensions/common/switches.h
@@ -0,0 +1,24 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_COMMON_SWITCHES_H_
+#define EXTENSIONS_COMMON_SWITCHES_H_
+
+// All switches in alphabetical order. The switches should be documented
+// alongside the definition of their values in the .cc file.
+namespace extensions {
+
+namespace switches {
+
+extern const char kAllowLegacyExtensionManifests[];
+extern const char kAllowScriptingGallery[];
+extern const char kEnableExperimentalExtensionApis[];
+extern const char kExtensionsOnChromeURLs[];
+extern const char kShowComponentExtensionOptions[];
+
+} // namespace switches
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_SWITCHES_H_
diff --git a/chromium/extensions/common/url_pattern.cc b/chromium/extensions/common/url_pattern.cc
new file mode 100644
index 00000000000..5400567bde5
--- /dev/null
+++ b/chromium/extensions/common/url_pattern.cc
@@ -0,0 +1,540 @@
+// 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.
+
+#include "extensions/common/url_pattern.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "content/public/common/url_constants.h"
+#include "extensions/common/constants.h"
+#include "url/gurl.h"
+#include "url/url_util.h"
+
+const char URLPattern::kAllUrlsPattern[] = "<all_urls>";
+
+namespace {
+
+// TODO(aa): What about more obscure schemes like data: and javascript: ?
+// Note: keep this array in sync with kValidSchemeMasks.
+const char* kValidSchemes[] = {
+ chrome::kHttpScheme,
+ chrome::kHttpsScheme,
+ chrome::kFileScheme,
+ chrome::kFtpScheme,
+ chrome::kChromeUIScheme,
+ extensions::kExtensionScheme,
+ chrome::kFileSystemScheme,
+};
+
+const int kValidSchemeMasks[] = {
+ URLPattern::SCHEME_HTTP,
+ URLPattern::SCHEME_HTTPS,
+ URLPattern::SCHEME_FILE,
+ URLPattern::SCHEME_FTP,
+ URLPattern::SCHEME_CHROMEUI,
+ URLPattern::SCHEME_EXTENSION,
+ URLPattern::SCHEME_FILESYSTEM,
+};
+
+COMPILE_ASSERT(arraysize(kValidSchemes) == arraysize(kValidSchemeMasks),
+ must_keep_these_arrays_in_sync);
+
+const char kParseSuccess[] = "Success.";
+const char kParseErrorMissingSchemeSeparator[] = "Missing scheme separator.";
+const char kParseErrorInvalidScheme[] = "Invalid scheme.";
+const char kParseErrorWrongSchemeType[] = "Wrong scheme type.";
+const char kParseErrorEmptyHost[] = "Host can not be empty.";
+const char kParseErrorInvalidHostWildcard[] = "Invalid host wildcard.";
+const char kParseErrorEmptyPath[] = "Empty path.";
+const char kParseErrorInvalidPort[] = "Invalid port.";
+
+// Message explaining each URLPattern::ParseResult.
+const char* const kParseResultMessages[] = {
+ kParseSuccess,
+ kParseErrorMissingSchemeSeparator,
+ kParseErrorInvalidScheme,
+ kParseErrorWrongSchemeType,
+ kParseErrorEmptyHost,
+ kParseErrorInvalidHostWildcard,
+ kParseErrorEmptyPath,
+ kParseErrorInvalidPort,
+};
+
+COMPILE_ASSERT(URLPattern::NUM_PARSE_RESULTS == arraysize(kParseResultMessages),
+ must_add_message_for_each_parse_result);
+
+const char kPathSeparator[] = "/";
+
+bool IsStandardScheme(const std::string& scheme) {
+ // "*" gets the same treatment as a standard scheme.
+ if (scheme == "*")
+ return true;
+
+ return url_util::IsStandard(scheme.c_str(),
+ url_parse::Component(0, static_cast<int>(scheme.length())));
+}
+
+bool IsValidPortForScheme(const std::string& scheme, const std::string& port) {
+ if (port == "*")
+ return true;
+
+ // Only accept non-wildcard ports if the scheme uses ports.
+ if (url_canon::DefaultPortForScheme(scheme.c_str(), scheme.length()) ==
+ url_parse::PORT_UNSPECIFIED) {
+ return false;
+ }
+
+ int parsed_port = url_parse::PORT_UNSPECIFIED;
+ if (!base::StringToInt(port, &parsed_port))
+ return false;
+ return (parsed_port >= 0) && (parsed_port < 65536);
+}
+
+// Returns |path| with the trailing wildcard stripped if one existed.
+//
+// The functions that rely on this (OverlapsWith and Contains) are only
+// called for the patterns inside URLPatternSet. In those cases, we know that
+// the path will have only a single wildcard at the end. This makes figuring
+// out overlap much easier. It seems like there is probably a computer-sciency
+// way to solve the general case, but we don't need that yet.
+std::string StripTrailingWildcard(const std::string& path) {
+ size_t wildcard_index = path.find('*');
+ size_t path_last = path.size() - 1;
+ DCHECK(wildcard_index == std::string::npos || wildcard_index == path_last);
+ return wildcard_index == path_last ? path.substr(0, path_last) : path;
+}
+
+} // namespace
+
+URLPattern::URLPattern()
+ : valid_schemes_(SCHEME_NONE),
+ match_all_urls_(false),
+ match_subdomains_(false),
+ port_("*") {}
+
+URLPattern::URLPattern(int valid_schemes)
+ : valid_schemes_(valid_schemes),
+ match_all_urls_(false),
+ match_subdomains_(false),
+ port_("*") {}
+
+URLPattern::URLPattern(int valid_schemes, const std::string& pattern)
+ // Strict error checking is used, because this constructor is only
+ // appropriate when we know |pattern| is valid.
+ : valid_schemes_(valid_schemes),
+ match_all_urls_(false),
+ match_subdomains_(false),
+ port_("*") {
+ if (PARSE_SUCCESS != Parse(pattern))
+ NOTREACHED() << "URLPattern is invalid: " << pattern;
+}
+
+URLPattern::~URLPattern() {
+}
+
+bool URLPattern::operator<(const URLPattern& other) const {
+ return GetAsString() < other.GetAsString();
+}
+
+bool URLPattern::operator==(const URLPattern& other) const {
+ return GetAsString() == other.GetAsString();
+}
+
+URLPattern::ParseResult URLPattern::Parse(const std::string& pattern) {
+ spec_.clear();
+ SetMatchAllURLs(false);
+ SetMatchSubdomains(false);
+ SetPort("*");
+
+ // Special case pattern to match every valid URL.
+ if (pattern == kAllUrlsPattern) {
+ SetMatchAllURLs(true);
+ return PARSE_SUCCESS;
+ }
+
+ // Parse out the scheme.
+ size_t scheme_end_pos = pattern.find(content::kStandardSchemeSeparator);
+ bool has_standard_scheme_separator = true;
+
+ // Some urls also use ':' alone as the scheme separator.
+ if (scheme_end_pos == std::string::npos) {
+ scheme_end_pos = pattern.find(':');
+ has_standard_scheme_separator = false;
+ }
+
+ if (scheme_end_pos == std::string::npos)
+ return PARSE_ERROR_MISSING_SCHEME_SEPARATOR;
+
+ if (!SetScheme(pattern.substr(0, scheme_end_pos)))
+ return PARSE_ERROR_INVALID_SCHEME;
+
+ bool standard_scheme = IsStandardScheme(scheme_);
+ if (standard_scheme != has_standard_scheme_separator)
+ return PARSE_ERROR_WRONG_SCHEME_SEPARATOR;
+
+ // Advance past the scheme separator.
+ scheme_end_pos +=
+ (standard_scheme ? strlen(content::kStandardSchemeSeparator) : 1);
+ if (scheme_end_pos >= pattern.size())
+ return PARSE_ERROR_EMPTY_HOST;
+
+ // Parse out the host and path.
+ size_t host_start_pos = scheme_end_pos;
+ size_t path_start_pos = 0;
+
+ if (!standard_scheme) {
+ path_start_pos = host_start_pos;
+ } else if (scheme_ == chrome::kFileScheme) {
+ size_t host_end_pos = pattern.find(kPathSeparator, host_start_pos);
+ if (host_end_pos == std::string::npos) {
+ // Allow hostname omission.
+ // e.g. file://* is interpreted as file:///*,
+ // file://foo* is interpreted as file:///foo*.
+ path_start_pos = host_start_pos - 1;
+ } else {
+ // Ignore hostname if scheme is file://.
+ // e.g. file://localhost/foo is equal to file:///foo.
+ path_start_pos = host_end_pos;
+ }
+ } else {
+ size_t host_end_pos = pattern.find(kPathSeparator, host_start_pos);
+
+ // Host is required.
+ if (host_start_pos == host_end_pos)
+ return PARSE_ERROR_EMPTY_HOST;
+
+ if (host_end_pos == std::string::npos)
+ return PARSE_ERROR_EMPTY_PATH;
+
+ host_ = pattern.substr(host_start_pos, host_end_pos - host_start_pos);
+
+ // The first component can optionally be '*' to match all subdomains.
+ std::vector<std::string> host_components;
+ base::SplitString(host_, '.', &host_components);
+ if (host_components[0] == "*") {
+ match_subdomains_ = true;
+ host_components.erase(host_components.begin(),
+ host_components.begin() + 1);
+ }
+ host_ = JoinString(host_components, '.');
+
+ path_start_pos = host_end_pos;
+ }
+
+ SetPath(pattern.substr(path_start_pos));
+
+ size_t port_pos = host_.find(':');
+ if (port_pos != std::string::npos) {
+ if (!SetPort(host_.substr(port_pos + 1)))
+ return PARSE_ERROR_INVALID_PORT;
+ host_ = host_.substr(0, port_pos);
+ }
+
+ // No other '*' can occur in the host, though. This isn't necessary, but is
+ // done as a convenience to developers who might otherwise be confused and
+ // think '*' works as a glob in the host.
+ if (host_.find('*') != std::string::npos)
+ return PARSE_ERROR_INVALID_HOST_WILDCARD;
+
+ return PARSE_SUCCESS;
+}
+
+void URLPattern::SetValidSchemes(int valid_schemes) {
+ spec_.clear();
+ valid_schemes_ = valid_schemes;
+}
+
+void URLPattern::SetHost(const std::string& host) {
+ spec_.clear();
+ host_ = host;
+}
+
+void URLPattern::SetMatchAllURLs(bool val) {
+ spec_.clear();
+ match_all_urls_ = val;
+
+ if (val) {
+ match_subdomains_ = true;
+ scheme_ = "*";
+ host_.clear();
+ SetPath("/*");
+ }
+}
+
+void URLPattern::SetMatchSubdomains(bool val) {
+ spec_.clear();
+ match_subdomains_ = val;
+}
+
+bool URLPattern::SetScheme(const std::string& scheme) {
+ spec_.clear();
+ 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 {
+ if (valid_schemes_ == SCHEME_ALL)
+ return true;
+
+ for (size_t i = 0; i < arraysize(kValidSchemes); ++i) {
+ if (scheme == kValidSchemes[i] && (valid_schemes_ & kValidSchemeMasks[i]))
+ return true;
+ }
+
+ return false;
+}
+
+void URLPattern::SetPath(const std::string& path) {
+ spec_.clear();
+ path_ = path;
+ path_escaped_ = path_;
+ ReplaceSubstringsAfterOffset(&path_escaped_, 0, "\\", "\\\\");
+ ReplaceSubstringsAfterOffset(&path_escaped_, 0, "?", "\\?");
+}
+
+bool URLPattern::SetPort(const std::string& port) {
+ spec_.clear();
+ if (IsValidPortForScheme(scheme_, port)) {
+ port_ = port;
+ return true;
+ }
+ return false;
+}
+
+bool URLPattern::MatchesURL(const GURL& test) const {
+ const GURL* test_url = &test;
+ bool has_inner_url = test.inner_url() != NULL;
+
+ if (has_inner_url) {
+ if (!test.SchemeIsFileSystem())
+ return false; // The only nested URLs we handle are filesystem URLs.
+ test_url = test.inner_url();
+ }
+
+ if (!MatchesScheme(test_url->scheme()))
+ return false;
+
+ if (match_all_urls_)
+ return true;
+
+ std::string path_for_request = test.PathForRequest();
+ if (has_inner_url)
+ path_for_request = test_url->path() + path_for_request;
+
+ return MatchesSecurityOriginHelper(*test_url) &&
+ MatchesPath(path_for_request);
+}
+
+bool URLPattern::MatchesSecurityOrigin(const GURL& test) const {
+ const GURL* test_url = &test;
+ bool has_inner_url = test.inner_url() != NULL;
+
+ if (has_inner_url) {
+ if (!test.SchemeIsFileSystem())
+ return false; // The only nested URLs we handle are filesystem URLs.
+ test_url = test.inner_url();
+ }
+
+ if (!MatchesScheme(test_url->scheme()))
+ return false;
+
+ if (match_all_urls_)
+ return true;
+
+ return MatchesSecurityOriginHelper(*test_url);
+}
+
+bool URLPattern::MatchesScheme(const std::string& test) const {
+ if (!IsValidScheme(test))
+ return false;
+
+ return scheme_ == "*" || test == scheme_;
+}
+
+bool URLPattern::MatchesHost(const std::string& host) const {
+ std::string test(chrome::kHttpScheme);
+ test += content::kStandardSchemeSeparator;
+ test += host;
+ test += "/";
+ return MatchesHost(GURL(test));
+}
+
+bool URLPattern::MatchesHost(const GURL& test) const {
+ // If the hosts are exactly equal, we have a match.
+ if (test.host() == host_)
+ return true;
+
+ // If we're matching subdomains, and we have no host in the match pattern,
+ // that means that we're matching all hosts, which means we have a match no
+ // matter what the test host is.
+ if (match_subdomains_ && host_.empty())
+ return true;
+
+ // Otherwise, we can only match if our match pattern matches subdomains.
+ if (!match_subdomains_)
+ return false;
+
+ // We don't do subdomain matching against IP addresses, so we can give up now
+ // if the test host is an IP address.
+ if (test.HostIsIPAddress())
+ return false;
+
+ // Check if the test host is a subdomain of our host.
+ if (test.host().length() <= (host_.length() + 1))
+ return false;
+
+ if (test.host().compare(test.host().length() - host_.length(),
+ host_.length(), host_) != 0)
+ return false;
+
+ return test.host()[test.host().length() - host_.length() - 1] == '.';
+}
+
+bool URLPattern::MatchesPath(const std::string& test) const {
+ // Make the behaviour of OverlapsWith consistent with MatchesURL, which is
+ // need to match hosted apps on e.g. 'google.com' also run on 'google.com/'.
+ if (test + "/*" == path_escaped_)
+ return true;
+
+ return MatchPattern(test, path_escaped_);
+}
+
+const std::string& URLPattern::GetAsString() const {
+ if (!spec_.empty())
+ return spec_;
+
+ if (match_all_urls_) {
+ spec_ = kAllUrlsPattern;
+ return spec_;
+ }
+
+ bool standard_scheme = IsStandardScheme(scheme_);
+
+ std::string spec = scheme_ +
+ (standard_scheme ? content::kStandardSchemeSeparator : ":");
+
+ if (scheme_ != chrome::kFileScheme && standard_scheme) {
+ if (match_subdomains_) {
+ spec += "*";
+ if (!host_.empty())
+ spec += ".";
+ }
+
+ if (!host_.empty())
+ spec += host_;
+
+ if (port_ != "*") {
+ spec += ":";
+ spec += port_;
+ }
+ }
+
+ if (!path_.empty())
+ spec += path_;
+
+ spec_ = spec;
+ return spec_;
+}
+
+bool URLPattern::OverlapsWith(const URLPattern& other) const {
+ if (match_all_urls() || other.match_all_urls())
+ return true;
+ return (MatchesAnyScheme(other.GetExplicitSchemes()) ||
+ other.MatchesAnyScheme(GetExplicitSchemes()))
+ && (MatchesHost(other.host()) || other.MatchesHost(host()))
+ && (MatchesPortPattern(other.port()) || other.MatchesPortPattern(port()))
+ && (MatchesPath(StripTrailingWildcard(other.path())) ||
+ other.MatchesPath(StripTrailingWildcard(path())));
+}
+
+bool URLPattern::Contains(const URLPattern& other) const {
+ if (match_all_urls())
+ return true;
+ return MatchesAllSchemes(other.GetExplicitSchemes())
+ && MatchesHost(other.host())
+ && MatchesPortPattern(other.port())
+ && MatchesPath(StripTrailingWildcard(other.path()));
+}
+
+bool URLPattern::MatchesAnyScheme(
+ const std::vector<std::string>& schemes) const {
+ for (std::vector<std::string>::const_iterator i = schemes.begin();
+ i != schemes.end(); ++i) {
+ if (MatchesScheme(*i))
+ return true;
+ }
+
+ return false;
+}
+
+bool URLPattern::MatchesAllSchemes(
+ const std::vector<std::string>& schemes) const {
+ for (std::vector<std::string>::const_iterator i = schemes.begin();
+ i != schemes.end(); ++i) {
+ if (!MatchesScheme(*i))
+ return false;
+ }
+
+ return true;
+}
+
+bool URLPattern::MatchesSecurityOriginHelper(const GURL& test) const {
+ // Ignore hostname if scheme is file://.
+ if (scheme_ != chrome::kFileScheme && !MatchesHost(test))
+ return false;
+
+ if (!MatchesPortPattern(base::IntToString(test.EffectiveIntPort())))
+ return false;
+
+ return true;
+}
+
+bool URLPattern::MatchesPortPattern(const std::string& port) const {
+ return port_ == "*" || port_ == port;
+}
+
+std::vector<std::string> URLPattern::GetExplicitSchemes() const {
+ std::vector<std::string> result;
+
+ if (scheme_ != "*" && !match_all_urls_ && IsValidScheme(scheme_)) {
+ result.push_back(scheme_);
+ return result;
+ }
+
+ for (size_t i = 0; i < arraysize(kValidSchemes); ++i) {
+ if (MatchesScheme(kValidSchemes[i])) {
+ result.push_back(kValidSchemes[i]);
+ }
+ }
+
+ return result;
+}
+
+std::vector<URLPattern> URLPattern::ConvertToExplicitSchemes() const {
+ std::vector<std::string> explicit_schemes = GetExplicitSchemes();
+ std::vector<URLPattern> result;
+
+ for (std::vector<std::string>::const_iterator i = explicit_schemes.begin();
+ i != explicit_schemes.end(); ++i) {
+ URLPattern temp = *this;
+ temp.SetScheme(*i);
+ temp.SetMatchAllURLs(false);
+ result.push_back(temp);
+ }
+
+ return result;
+}
+
+// static
+const char* URLPattern::GetParseResultString(
+ URLPattern::ParseResult parse_result) {
+ return kParseResultMessages[parse_result];
+}
diff --git a/chromium/extensions/common/url_pattern.h b/chromium/extensions/common/url_pattern.h
new file mode 100644
index 00000000000..7d55b8da1fd
--- /dev/null
+++ b/chromium/extensions/common/url_pattern.h
@@ -0,0 +1,245 @@
+// 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.
+#ifndef EXTENSIONS_COMMON_URL_PATTERN_H_
+#define EXTENSIONS_COMMON_URL_PATTERN_H_
+
+#include <functional>
+#include <string>
+#include <vector>
+
+class GURL;
+
+// A pattern that can be used to match URLs. A URLPattern is a very restricted
+// subset of URL syntax:
+//
+// <url-pattern> := <scheme>://<host><port><path> | '<all_urls>'
+// <scheme> := '*' | 'http' | 'https' | 'file' | 'ftp' | 'chrome' |
+// 'chrome-extension' | 'filesystem'
+// <host> := '*' | '*.' <anychar except '/' and '*'>+
+// <port> := [':' ('*' | <port number between 0 and 65535>)]
+// <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://*/*
+// - http://*/foo*
+// - https://*.google.com/foo*bar
+// - file://monkey*
+// - http://127.0.0.1/*
+//
+// Examples of invalid patterns:
+// - http://* -- path not specified
+// - http://*foo/bar -- * not allowed as substring of host component
+// - http://foo.*.bar/baz -- * must be first component
+// - http:/bar -- scheme separator not found
+// - foo://* -- invalid scheme
+// - chrome:// -- we don't support chrome internal URLs
+class URLPattern {
+ public:
+ // A collection of scheme bitmasks for use with valid_schemes.
+ enum SchemeMasks {
+ SCHEME_NONE = 0,
+ SCHEME_HTTP = 1 << 0,
+ SCHEME_HTTPS = 1 << 1,
+ SCHEME_FILE = 1 << 2,
+ SCHEME_FTP = 1 << 3,
+ SCHEME_CHROMEUI = 1 << 4,
+ SCHEME_EXTENSION = 1 << 5,
+ SCHEME_FILESYSTEM = 1 << 6,
+
+ // IMPORTANT!
+ // SCHEME_ALL will match every scheme, including chrome://, chrome-
+ // extension://, about:, etc. Because this has lots of security
+ // implications, third-party extensions should usually not be able to get
+ // access to URL patterns initialized this way. If there is a reason
+ // for violating this general rule, document why this it safe.
+ SCHEME_ALL = -1,
+ };
+
+ // Error codes returned from Parse().
+ enum ParseResult {
+ PARSE_SUCCESS = 0,
+ PARSE_ERROR_MISSING_SCHEME_SEPARATOR,
+ PARSE_ERROR_INVALID_SCHEME,
+ PARSE_ERROR_WRONG_SCHEME_SEPARATOR,
+ PARSE_ERROR_EMPTY_HOST,
+ PARSE_ERROR_INVALID_HOST_WILDCARD,
+ PARSE_ERROR_EMPTY_PATH,
+ PARSE_ERROR_INVALID_PORT,
+ NUM_PARSE_RESULTS
+ };
+
+ // The <all_urls> string pattern.
+ static const char kAllUrlsPattern[];
+
+ explicit URLPattern(int valid_schemes);
+
+ // Convenience to construct a URLPattern from a string. If the string is not
+ // known ahead of time, use Parse() instead, which returns success or failure.
+ URLPattern(int valid_schemes, const std::string& pattern);
+
+ URLPattern();
+ ~URLPattern();
+
+ bool operator<(const URLPattern& other) const;
+ bool operator==(const URLPattern& other) const;
+
+ // Initializes this instance by parsing the provided string. Returns
+ // URLPattern::PARSE_SUCCESS on success, or an error code otherwise. On
+ // failure, this instance will have some intermediate values and is in an
+ // invalid state.
+ ParseResult Parse(const std::string& pattern_str);
+
+ // Gets the bitmask of valid schemes.
+ int valid_schemes() const { return valid_schemes_; }
+ void SetValidSchemes(int 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>).
+ const std::string& host() const { return host_; }
+ void SetHost(const std::string& host);
+
+ // Gets whether to match subdomains of host().
+ bool match_subdomains() const { return match_subdomains_; }
+ void SetMatchSubdomains(bool val);
+
+ // Gets the path the pattern matches with the leading slash. This can have
+ // embedded asterisks which are interpreted using glob rules.
+ const std::string& path() const { return path_; }
+ void SetPath(const std::string& path);
+
+ // Returns true if this pattern matches all urls.
+ bool match_all_urls() const { return match_all_urls_; }
+ void SetMatchAllURLs(bool val);
+
+ // 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& test) const;
+
+ // Returns true if this instance matches the specified security origin.
+ bool MatchesSecurityOrigin(const GURL& test) const;
+
+ // Returns true if |test| matches our scheme.
+ // Note that if test is "filesystem", this may fail whereas MatchesURL
+ // may succeed. MatchesURL is smart enough to look at the inner_url instead
+ // of the outer "filesystem:" part.
+ 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;
+
+ // Returns true if |test| matches our path.
+ bool MatchesPath(const std::string& test) const;
+
+ // Sets the port. Returns false if the port is invalid.
+ bool SetPort(const std::string& port);
+ const std::string& port() const { return port_; }
+
+ // Returns a string representing this instance.
+ const std::string& GetAsString() const;
+
+ // Determines whether there is a URL that would match this instance and
+ // another instance. This method is symmetrical: Calling
+ // other.OverlapsWith(this) would result in the same answer.
+ bool OverlapsWith(const URLPattern& other) const;
+
+ // Returns true if this pattern matches all possible URLs that |other| can
+ // match. For example, http://*.google.com encompasses http://www.google.com.
+ bool Contains(const URLPattern& other) const;
+
+ // Converts this URLPattern into an equivalent set of URLPatterns that don't
+ // use a wildcard in the scheme component. If this URLPattern doesn't use a
+ // wildcard scheme, then the returned set will contain one element that is
+ // equivalent to this instance.
+ std::vector<URLPattern> ConvertToExplicitSchemes() const;
+
+ static bool EffectiveHostCompare(const URLPattern& a, const URLPattern& b) {
+ if (a.match_all_urls_ && b.match_all_urls_)
+ return false;
+ return a.host_.compare(b.host_) < 0;
+ };
+
+ // Used for origin comparisons in a std::set.
+ class EffectiveHostCompareFunctor {
+ public:
+ bool operator()(const URLPattern& a, const URLPattern& b) const {
+ return EffectiveHostCompare(a, b);
+ };
+ };
+
+ // Get an error string for a ParseResult.
+ static const char* GetParseResultString(URLPattern::ParseResult parse_result);
+
+ // Checks whether the bit is set for the given scheme in the given scheme mask
+ static bool IsSchemeBitSet(const std::string& scheme, const int mask);
+
+ private:
+ // Returns true if any of the |schemes| items matches our scheme.
+ bool MatchesAnyScheme(const std::vector<std::string>& schemes) const;
+
+ // Returns true if all of the |schemes| items matches our scheme.
+ bool MatchesAllSchemes(const std::vector<std::string>& schemes) const;
+
+ bool MatchesSecurityOriginHelper(const GURL& test) const;
+
+ // Returns true if our port matches the |port| pattern (it may be "*").
+ bool MatchesPortPattern(const std::string& port) const;
+
+ // If the URLPattern contains a wildcard scheme, returns a list of
+ // equivalent literal schemes, otherwise returns the current scheme.
+ std::vector<std::string> GetExplicitSchemes() const;
+
+ // A bitmask containing the schemes which are considered valid for this
+ // pattern. Parse() uses this to decide whether a pattern contains a valid
+ // 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_;
+
+ // The host without any leading "*" components.
+ std::string host_;
+
+ // Whether we should match subdomains of the host. This is true if the first
+ // component of the pattern's host was "*".
+ bool match_subdomains_;
+
+ // The port.
+ std::string port_;
+
+ // The path to match. This is everything after the host of the URL, or
+ // everything after the scheme in the case of file:// URLs.
+ std::string path_;
+
+ // The path with "?" and "\" characters escaped for use with the
+ // MatchPattern() function.
+ std::string path_escaped_;
+
+ // A string representing this URLPattern.
+ mutable std::string spec_;
+};
+
+typedef std::vector<URLPattern> URLPatternList;
+
+#endif // EXTENSIONS_COMMON_URL_PATTERN_H_
diff --git a/chromium/extensions/common/url_pattern_set.cc b/chromium/extensions/common/url_pattern_set.cc
new file mode 100644
index 00000000000..87a752c8683
--- /dev/null
+++ b/chromium/extensions/common/url_pattern_set.cc
@@ -0,0 +1,233 @@
+// 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.
+
+#include "extensions/common/url_pattern_set.h"
+
+#include <algorithm>
+#include <iterator>
+
+#include "base/logging.h"
+#include "base/memory/linked_ptr.h"
+#include "base/values.h"
+#include "content/public/common/url_constants.h"
+#include "extensions/common/error_utils.h"
+#include "extensions/common/url_pattern.h"
+#include "url/gurl.h"
+
+namespace extensions {
+
+namespace {
+
+const char kInvalidURLPatternError[] = "Invalid url pattern '*'";
+
+} // namespace
+
+// static
+void URLPatternSet::CreateDifference(const URLPatternSet& set1,
+ const URLPatternSet& set2,
+ URLPatternSet* out) {
+ out->ClearPatterns();
+ std::set_difference(set1.patterns_.begin(), set1.patterns_.end(),
+ set2.patterns_.begin(), set2.patterns_.end(),
+ std::inserter<std::set<URLPattern> >(
+ out->patterns_, out->patterns_.begin()));
+}
+
+// static
+void URLPatternSet::CreateIntersection(const URLPatternSet& set1,
+ const URLPatternSet& set2,
+ URLPatternSet* out) {
+ out->ClearPatterns();
+ std::set_intersection(set1.patterns_.begin(), set1.patterns_.end(),
+ set2.patterns_.begin(), set2.patterns_.end(),
+ std::inserter<std::set<URLPattern> >(
+ out->patterns_, out->patterns_.begin()));
+}
+
+// static
+void URLPatternSet::CreateUnion(const URLPatternSet& set1,
+ const URLPatternSet& set2,
+ URLPatternSet* out) {
+ out->ClearPatterns();
+ std::set_union(set1.patterns_.begin(), set1.patterns_.end(),
+ set2.patterns_.begin(), set2.patterns_.end(),
+ std::inserter<std::set<URLPattern> >(
+ 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)
+ : patterns_(rhs.patterns_) {}
+
+URLPatternSet::URLPatternSet(const std::set<URLPattern>& patterns)
+ : patterns_(patterns) {}
+
+URLPatternSet::~URLPatternSet() {}
+
+URLPatternSet& URLPatternSet::operator=(const URLPatternSet& rhs) {
+ patterns_ = rhs.patterns_;
+ return *this;
+}
+
+bool URLPatternSet::operator==(const URLPatternSet& other) const {
+ return patterns_ == other.patterns_;
+}
+
+bool URLPatternSet::is_empty() const {
+ return patterns_.empty();
+}
+
+size_t URLPatternSet::size() const {
+ return patterns_.size();
+}
+
+bool URLPatternSet::AddPattern(const URLPattern& pattern) {
+ return patterns_.insert(pattern).second;
+}
+
+void URLPatternSet::AddPatterns(const URLPatternSet& set) {
+ patterns_.insert(set.patterns().begin(),
+ set.patterns().end());
+}
+
+void URLPatternSet::ClearPatterns() {
+ patterns_.clear();
+}
+
+bool URLPatternSet::Contains(const URLPatternSet& other) const {
+ for (URLPatternSet::const_iterator it = other.begin();
+ it != other.end(); ++it) {
+ if (!ContainsPattern(*it))
+ return false;
+ }
+
+ return true;
+}
+
+bool URLPatternSet::ContainsPattern(const URLPattern& pattern) const {
+ for (URLPatternSet::const_iterator it = begin();
+ it != end(); ++it) {
+ if (it->Contains(pattern))
+ return true;
+ }
+ return false;
+}
+
+bool URLPatternSet::MatchesURL(const GURL& url) const {
+ for (URLPatternSet::const_iterator pattern = patterns_.begin();
+ pattern != patterns_.end(); ++pattern) {
+ if (pattern->MatchesURL(url))
+ return true;
+ }
+
+ return false;
+}
+
+bool URLPatternSet::MatchesSecurityOrigin(const GURL& origin) const {
+ for (URLPatternSet::const_iterator pattern = patterns_.begin();
+ pattern != patterns_.end(); ++pattern) {
+ if (pattern->MatchesSecurityOrigin(origin))
+ return true;
+ }
+
+ return false;
+}
+
+bool URLPatternSet::OverlapsWith(const URLPatternSet& other) const {
+ // Two extension extents overlap if there is any one URL that would match at
+ // least one pattern in each of the extents.
+ for (URLPatternSet::const_iterator i = patterns_.begin();
+ i != patterns_.end(); ++i) {
+ for (URLPatternSet::const_iterator j = other.patterns().begin();
+ j != other.patterns().end(); ++j) {
+ if (i->OverlapsWith(*j))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+scoped_ptr<base::ListValue> URLPatternSet::ToValue() const {
+ scoped_ptr<ListValue> value(new ListValue);
+ for (URLPatternSet::const_iterator i = patterns_.begin();
+ i != patterns_.end(); ++i)
+ value->AppendIfNotPresent(Value::CreateStringValue(i->GetAsString()));
+ return value.Pass();
+}
+
+bool URLPatternSet::Populate(const std::vector<std::string>& patterns,
+ int valid_schemes,
+ bool allow_file_access,
+ std::string* error) {
+ ClearPatterns();
+ for (size_t i = 0; i < patterns.size(); ++i) {
+ URLPattern pattern(valid_schemes);
+ if (pattern.Parse(patterns[i]) != URLPattern::PARSE_SUCCESS) {
+ if (error) {
+ *error = ErrorUtils::FormatErrorMessage(kInvalidURLPatternError,
+ patterns[i]);
+ } else {
+ LOG(ERROR) << "Invalid url pattern: " << patterns[i];
+ }
+ return false;
+ }
+ if (!allow_file_access && pattern.MatchesScheme(chrome::kFileScheme)) {
+ pattern.SetValidSchemes(
+ pattern.valid_schemes() & ~URLPattern::SCHEME_FILE);
+ }
+ AddPattern(pattern);
+ }
+ return true;
+}
+
+bool URLPatternSet::Populate(const base::ListValue& value,
+ int valid_schemes,
+ bool allow_file_access,
+ std::string* error) {
+ std::vector<std::string> patterns;
+ for (size_t i = 0; i < value.GetSize(); ++i) {
+ std::string item;
+ if (!value.GetString(i, &item))
+ return false;
+ patterns.push_back(item);
+ }
+ return Populate(patterns, valid_schemes, allow_file_access, error);
+}
+
+} // namespace extensions
diff --git a/chromium/extensions/common/url_pattern_set.h b/chromium/extensions/common/url_pattern_set.h
new file mode 100644
index 00000000000..da6ad132db1
--- /dev/null
+++ b/chromium/extensions/common/url_pattern_set.h
@@ -0,0 +1,105 @@
+// 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.
+
+#ifndef EXTENSIONS_COMMMON_URL_PATTERN_SET_H_
+#define EXTENSIONS_COMMMON_URL_PATTERN_SET_H_
+
+#include <set>
+
+#include "base/memory/scoped_ptr.h"
+#include "extensions/common/url_pattern.h"
+
+class GURL;
+
+namespace base {
+class ListValue;
+class Value;
+}
+
+namespace extensions {
+
+// Represents the set of URLs an extension uses for web content.
+class URLPatternSet {
+ public:
+ typedef std::set<URLPattern>::const_iterator const_iterator;
+ typedef std::set<URLPattern>::iterator iterator;
+
+ // Clears |out| and populates the set with |set1| - |set2|.
+ static void CreateDifference(const URLPatternSet& set1,
+ const URLPatternSet& set2,
+ URLPatternSet* out);
+
+ // Clears |out| and populates the set with the intersection of |set1|
+ // and |set2|.
+ static void CreateIntersection(const URLPatternSet& set1,
+ const URLPatternSet& set2,
+ URLPatternSet* out);
+
+ // Clears |out| and populates the set with the union of |set1| and |set2|.
+ static void CreateUnion(const URLPatternSet& set1,
+ 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);
+ ~URLPatternSet();
+
+ URLPatternSet& operator=(const URLPatternSet& rhs);
+ 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(); }
+
+ // Adds a pattern to the set. Returns true if a new pattern was inserted,
+ // false if the pattern was already in the set.
+ bool AddPattern(const URLPattern& pattern);
+
+ // Adds all patterns from |set| into this.
+ void AddPatterns(const URLPatternSet& set);
+
+ void ClearPatterns();
+
+ // Returns true if every URL that matches |set| is matched by this. In other
+ // words, if every pattern in |set| is encompassed by a pattern in this.
+ bool Contains(const URLPatternSet& set) const;
+
+ // Returns true if any pattern in this set encompasses |pattern|.
+ bool ContainsPattern(const URLPattern& pattern) const;
+
+ // Test if the extent contains a URL.
+ bool MatchesURL(const GURL& url) const;
+
+ bool MatchesSecurityOrigin(const GURL& origin) const;
+
+ // Returns true if there is a single URL that would be in two extents.
+ bool OverlapsWith(const URLPatternSet& other) const;
+
+ // Converts to and from Value for serialization to preferences.
+ scoped_ptr<base::ListValue> ToValue() const;
+ bool Populate(const base::ListValue& value,
+ int valid_schemes,
+ bool allow_file_access,
+ std::string* error);
+
+ bool Populate(const std::vector<std::string>& patterns,
+ int valid_schemes,
+ bool allow_file_access,
+ std::string* error);
+
+ private:
+ // The list of URL patterns that comprise the extent.
+ std::set<URLPattern> patterns_;
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMMON_URL_PATTERN_SET_H_
diff --git a/chromium/extensions/common/url_pattern_set_unittest.cc b/chromium/extensions/common/url_pattern_set_unittest.cc
new file mode 100644
index 00000000000..89b5435b820
--- /dev/null
+++ b/chromium/extensions/common/url_pattern_set_unittest.cc
@@ -0,0 +1,396 @@
+// 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.
+
+#include "extensions/common/url_pattern_set.h"
+
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace extensions {
+
+namespace {
+
+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")));
+ EXPECT_FALSE(set.MatchesURL(GURL()));
+ EXPECT_FALSE(set.MatchesURL(GURL("invalid")));
+}
+
+TEST(URLPatternSetTest, One) {
+ URLPatternSet set;
+ AddPattern(&set, "http://www.google.com/*");
+
+ EXPECT_TRUE(set.MatchesURL(GURL("http://www.google.com/")));
+ EXPECT_TRUE(set.MatchesURL(GURL("http://www.google.com/monkey")));
+ EXPECT_FALSE(set.MatchesURL(GURL("https://www.google.com/")));
+ EXPECT_FALSE(set.MatchesURL(GURL("https://www.microsoft.com/")));
+}
+
+TEST(URLPatternSetTest, Two) {
+ URLPatternSet set;
+ AddPattern(&set, "http://www.google.com/*");
+ AddPattern(&set, "http://www.yahoo.com/*");
+
+ EXPECT_TRUE(set.MatchesURL(GURL("http://www.google.com/monkey")));
+ EXPECT_TRUE(set.MatchesURL(GURL("http://www.yahoo.com/monkey")));
+ EXPECT_FALSE(set.MatchesURL(GURL("https://www.apple.com/monkey")));
+}
+
+TEST(URLPatternSetTest, OverlapsWith) {
+ URLPatternSet set1;
+ AddPattern(&set1, "http://www.google.com/f*");
+ AddPattern(&set1, "http://www.yahoo.com/b*");
+
+ URLPatternSet set2;
+ AddPattern(&set2, "http://www.reddit.com/f*");
+ AddPattern(&set2, "http://www.yahoo.com/z*");
+
+ URLPatternSet set3;
+ AddPattern(&set3, "http://www.google.com/q/*");
+ AddPattern(&set3, "http://www.yahoo.com/b/*");
+
+ EXPECT_FALSE(set1.OverlapsWith(set2));
+ EXPECT_FALSE(set2.OverlapsWith(set1));
+
+ EXPECT_TRUE(set1.OverlapsWith(set3));
+ EXPECT_TRUE(set3.OverlapsWith(set1));
+}
+
+TEST(URLPatternSetTest, CreateDifference) {
+ URLPatternSet expected;
+ URLPatternSet set1;
+ URLPatternSet set2;
+ AddPattern(&set1, "http://www.google.com/f*");
+ AddPattern(&set1, "http://www.yahoo.com/b*");
+
+ // Subtract an empty set.
+ URLPatternSet result;
+ URLPatternSet::CreateDifference(set1, set2, &result);
+ EXPECT_EQ(set1, result);
+
+ // Subtract a real set.
+ AddPattern(&set2, "http://www.reddit.com/f*");
+ AddPattern(&set2, "http://www.yahoo.com/z*");
+ AddPattern(&set2, "http://www.google.com/f*");
+
+ AddPattern(&expected, "http://www.yahoo.com/b*");
+
+ result.ClearPatterns();
+ URLPatternSet::CreateDifference(set1, set2, &result);
+ EXPECT_EQ(expected, result);
+ EXPECT_FALSE(result.is_empty());
+ EXPECT_TRUE(set1.Contains(result));
+ EXPECT_FALSE(result.Contains(set2));
+ EXPECT_FALSE(set2.Contains(result));
+
+ URLPatternSet intersection;
+ URLPatternSet::CreateIntersection(result, set2, &intersection);
+ EXPECT_TRUE(intersection.is_empty());
+}
+
+TEST(URLPatternSetTest, CreateIntersection) {
+ URLPatternSet empty_set;
+ URLPatternSet expected;
+ URLPatternSet set1;
+ AddPattern(&set1, "http://www.google.com/f*");
+ AddPattern(&set1, "http://www.yahoo.com/b*");
+
+ // Intersection with an empty set.
+ URLPatternSet result;
+ URLPatternSet::CreateIntersection(set1, empty_set, &result);
+ EXPECT_EQ(expected, result);
+ EXPECT_TRUE(result.is_empty());
+ EXPECT_TRUE(empty_set.Contains(result));
+ EXPECT_TRUE(result.Contains(empty_set));
+ EXPECT_TRUE(set1.Contains(result));
+
+ // Intersection with a real set.
+ URLPatternSet set2;
+ AddPattern(&set2, "http://www.reddit.com/f*");
+ AddPattern(&set2, "http://www.yahoo.com/z*");
+ AddPattern(&set2, "http://www.google.com/f*");
+
+ AddPattern(&expected, "http://www.google.com/f*");
+
+ result.ClearPatterns();
+ URLPatternSet::CreateIntersection(set1, set2, &result);
+ EXPECT_EQ(expected, result);
+ EXPECT_FALSE(result.is_empty());
+ EXPECT_TRUE(set1.Contains(result));
+ EXPECT_TRUE(set2.Contains(result));
+}
+
+TEST(URLPatternSetTest, CreateUnion) {
+ URLPatternSet empty_set;
+
+ URLPatternSet set1;
+ AddPattern(&set1, "http://www.google.com/f*");
+ AddPattern(&set1, "http://www.yahoo.com/b*");
+
+ URLPatternSet expected;
+ AddPattern(&expected, "http://www.google.com/f*");
+ AddPattern(&expected, "http://www.yahoo.com/b*");
+
+ // Union with an empty set.
+ URLPatternSet result;
+ URLPatternSet::CreateUnion(set1, empty_set, &result);
+ EXPECT_EQ(expected, result);
+
+ // Union with a real set.
+ URLPatternSet set2;
+ AddPattern(&set2, "http://www.reddit.com/f*");
+ AddPattern(&set2, "http://www.yahoo.com/z*");
+ AddPattern(&set2, "http://www.google.com/f*");
+
+ AddPattern(&expected, "http://www.reddit.com/f*");
+ AddPattern(&expected, "http://www.yahoo.com/z*");
+
+ result.ClearPatterns();
+ URLPatternSet::CreateUnion(set1, set2, &result);
+ EXPECT_EQ(expected, result);
+}
+
+TEST(URLPatternSetTest, Contains) {
+ URLPatternSet set1;
+ URLPatternSet set2;
+ URLPatternSet empty_set;
+
+ AddPattern(&set1, "http://www.google.com/*");
+ AddPattern(&set1, "http://www.yahoo.com/*");
+
+ AddPattern(&set2, "http://www.reddit.com/*");
+
+ EXPECT_FALSE(set1.Contains(set2));
+ EXPECT_TRUE(set1.Contains(empty_set));
+ EXPECT_FALSE(empty_set.Contains(set1));
+
+ AddPattern(&set2, "http://www.yahoo.com/*");
+
+ EXPECT_FALSE(set1.Contains(set2));
+ EXPECT_FALSE(set2.Contains(set1));
+
+ AddPattern(&set2, "http://www.google.com/*");
+
+ EXPECT_FALSE(set1.Contains(set2));
+ EXPECT_TRUE(set2.Contains(set1));
+
+ // Note that this checks if individual patterns contain other patterns, not
+ // just equality. For example:
+ AddPattern(&set1, "http://*.reddit.com/*");
+ EXPECT_TRUE(set1.Contains(set2));
+ EXPECT_FALSE(set2.Contains(set1));
+}
+
+TEST(URLPatternSetTest, Duplicates) {
+ URLPatternSet set1;
+ URLPatternSet set2;
+
+ AddPattern(&set1, "http://www.google.com/*");
+ AddPattern(&set2, "http://www.google.com/*");
+
+ AddPattern(&set1, "http://www.google.com/*");
+
+ // The sets should still be equal after adding a duplicate.
+ EXPECT_EQ(set2, set1);
+}
+
+TEST(URLPatternSetTest, ToValueAndPopulate) {
+ URLPatternSet set1;
+ URLPatternSet set2;
+
+ std::vector<std::string> patterns;
+ patterns.push_back("http://www.google.com/*");
+ patterns.push_back("http://www.yahoo.com/*");
+
+ for (size_t i = 0; i < patterns.size(); ++i)
+ AddPattern(&set1, patterns[i]);
+
+ std::string error;
+ bool allow_file_access = false;
+ scoped_ptr<base::ListValue> value(set1.ToValue());
+ set2.Populate(*value, URLPattern::SCHEME_ALL, allow_file_access, &error);
+ EXPECT_EQ(set1, set2);
+
+ set2.ClearPatterns();
+ set2.Populate(patterns, URLPattern::SCHEME_ALL, allow_file_access, &error);
+ EXPECT_EQ(set1, set2);
+}
+
+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);
+ }
+}
+
+} // namespace extensions
diff --git a/chromium/extensions/common/url_pattern_unittest.cc b/chromium/extensions/common/url_pattern_unittest.cc
new file mode 100644
index 00000000000..d1cc8c2103c
--- /dev/null
+++ b/chromium/extensions/common/url_pattern_unittest.cc
@@ -0,0 +1,802 @@
+// 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.
+
+#include "base/memory/scoped_ptr.h"
+#include "extensions/common/url_pattern.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace {
+
+// See url_pattern.h for examples of valid and invalid patterns.
+
+static const int kAllSchemes =
+ URLPattern::SCHEME_HTTP |
+ URLPattern::SCHEME_HTTPS |
+ URLPattern::SCHEME_FILE |
+ URLPattern::SCHEME_FTP |
+ URLPattern::SCHEME_CHROMEUI |
+ URLPattern::SCHEME_EXTENSION |
+ URLPattern::SCHEME_FILESYSTEM;
+
+TEST(ExtensionURLPatternTest, ParseInvalid) {
+ const struct {
+ const char* pattern;
+ URLPattern::ParseResult expected_result;
+ } kInvalidPatterns[] = {
+ { "http", URLPattern::PARSE_ERROR_MISSING_SCHEME_SEPARATOR },
+ { "http:", URLPattern::PARSE_ERROR_WRONG_SCHEME_SEPARATOR },
+ { "http:/", URLPattern::PARSE_ERROR_WRONG_SCHEME_SEPARATOR },
+ { "about://", URLPattern::PARSE_ERROR_WRONG_SCHEME_SEPARATOR },
+ { "http://", URLPattern::PARSE_ERROR_EMPTY_HOST },
+ { "http:///", URLPattern::PARSE_ERROR_EMPTY_HOST },
+ { "http://*foo/bar", URLPattern::PARSE_ERROR_INVALID_HOST_WILDCARD },
+ { "http://foo.*.bar/baz", URLPattern::PARSE_ERROR_INVALID_HOST_WILDCARD },
+ { "http://fo.*.ba:123/baz", URLPattern::PARSE_ERROR_INVALID_HOST_WILDCARD },
+ { "http:/bar", URLPattern::PARSE_ERROR_WRONG_SCHEME_SEPARATOR },
+ { "http://bar", URLPattern::PARSE_ERROR_EMPTY_PATH },
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kInvalidPatterns); ++i) {
+ URLPattern pattern(URLPattern::SCHEME_ALL);
+ EXPECT_EQ(kInvalidPatterns[i].expected_result,
+ pattern.Parse(kInvalidPatterns[i].pattern))
+ << kInvalidPatterns[i].pattern;
+ }
+};
+
+TEST(ExtensionURLPatternTest, Ports) {
+ const struct {
+ const char* pattern;
+ URLPattern::ParseResult expected_result;
+ const char* expected_port;
+ } kTestPatterns[] = {
+ { "http://foo:1234/", URLPattern::PARSE_SUCCESS, "1234" },
+ { "http://foo:1234/bar", URLPattern::PARSE_SUCCESS, "1234" },
+ { "http://*.foo:1234/", URLPattern::PARSE_SUCCESS, "1234" },
+ { "http://*.foo:1234/bar", URLPattern::PARSE_SUCCESS,"1234" },
+ { "http://:1234/", URLPattern::PARSE_SUCCESS, "1234" },
+ { "http://foo:/", URLPattern::PARSE_ERROR_INVALID_PORT, "*" },
+ { "http://foo:*/", URLPattern::PARSE_SUCCESS, "*" },
+ { "http://*.foo:/", URLPattern::PARSE_ERROR_INVALID_PORT, "*" },
+ { "http://foo:com/", URLPattern::PARSE_ERROR_INVALID_PORT, "*" },
+ { "http://foo:123456/", URLPattern::PARSE_ERROR_INVALID_PORT, "*" },
+ { "http://foo:80:80/monkey", URLPattern::PARSE_ERROR_INVALID_PORT, "*" },
+ { "file://foo:1234/bar", URLPattern::PARSE_SUCCESS, "*" },
+ { "chrome://foo:1234/bar", URLPattern::PARSE_ERROR_INVALID_PORT, "*" },
+
+ // Port-like strings in the path should not trigger a warning.
+ { "http://*/:1234", URLPattern::PARSE_SUCCESS, "*" },
+ { "http://*.foo/bar:1234", URLPattern::PARSE_SUCCESS, "*" },
+ { "http://foo/bar:1234/path", URLPattern::PARSE_SUCCESS,"*" },
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestPatterns); ++i) {
+ URLPattern pattern(URLPattern::SCHEME_ALL);
+ EXPECT_EQ(kTestPatterns[i].expected_result,
+ pattern.Parse(kTestPatterns[i].pattern))
+ << "Got unexpected result for URL pattern: "
+ << kTestPatterns[i].pattern;
+ EXPECT_EQ(kTestPatterns[i].expected_port, pattern.port())
+ << "Got unexpected port for URL pattern: " << kTestPatterns[i].pattern;
+ }
+};
+
+// all pages for a given scheme
+TEST(ExtensionURLPatternTest, Match1) {
+ URLPattern pattern(kAllSchemes);
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("http://*/*"));
+ EXPECT_EQ("http", pattern.scheme());
+ EXPECT_EQ("", pattern.host());
+ EXPECT_TRUE(pattern.match_subdomains());
+ EXPECT_FALSE(pattern.match_all_urls());
+ EXPECT_EQ("/*", pattern.path());
+ EXPECT_TRUE(pattern.MatchesURL(GURL("http://google.com")));
+ EXPECT_TRUE(pattern.MatchesURL(GURL("http://yahoo.com")));
+ EXPECT_TRUE(pattern.MatchesURL(GURL("http://google.com/foo")));
+ EXPECT_FALSE(pattern.MatchesURL(GURL("https://google.com")));
+ EXPECT_TRUE(pattern.MatchesURL(GURL("http://74.125.127.100/search")));
+}
+
+// all domains
+TEST(ExtensionURLPatternTest, Match2) {
+ URLPattern pattern(kAllSchemes);
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("https://*/foo*"));
+ EXPECT_EQ("https", pattern.scheme());
+ EXPECT_EQ("", pattern.host());
+ EXPECT_TRUE(pattern.match_subdomains());
+ EXPECT_FALSE(pattern.match_all_urls());
+ EXPECT_EQ("/foo*", pattern.path());
+ EXPECT_TRUE(pattern.MatchesURL(GURL("https://www.google.com/foo")));
+ EXPECT_TRUE(pattern.MatchesURL(GURL("https://www.google.com/foobar")));
+ EXPECT_FALSE(pattern.MatchesURL(GURL("http://www.google.com/foo")));
+ EXPECT_FALSE(pattern.MatchesURL(GURL("https://www.google.com/")));
+ EXPECT_TRUE(pattern.MatchesURL(
+ GURL("filesystem:https://www.google.com/foobar/")));
+}
+
+// subdomains
+TEST(URLPatternTest, Match3) {
+ URLPattern pattern(kAllSchemes);
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS,
+ pattern.Parse("http://*.google.com/foo*bar"));
+ EXPECT_EQ("http", pattern.scheme());
+ EXPECT_EQ("google.com", pattern.host());
+ EXPECT_TRUE(pattern.match_subdomains());
+ EXPECT_FALSE(pattern.match_all_urls());
+ EXPECT_EQ("/foo*bar", pattern.path());
+ EXPECT_TRUE(pattern.MatchesURL(GURL("http://google.com/foobar")));
+ EXPECT_TRUE(pattern.MatchesURL(GURL("http://www.google.com/foo?bar")));
+ EXPECT_TRUE(pattern.MatchesURL(
+ GURL("http://monkey.images.google.com/foooobar")));
+ EXPECT_FALSE(pattern.MatchesURL(GURL("http://yahoo.com/foobar")));
+ EXPECT_TRUE(pattern.MatchesURL(
+ GURL("filesystem:http://google.com/foo/bar")));
+ EXPECT_FALSE(pattern.MatchesURL(
+ GURL("filesystem:http://google.com/temporary/foobar")));
+}
+
+// glob escaping
+TEST(ExtensionURLPatternTest, Match5) {
+ URLPattern pattern(kAllSchemes);
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("file:///foo?bar\\*baz"));
+ EXPECT_EQ("file", pattern.scheme());
+ EXPECT_EQ("", pattern.host());
+ EXPECT_FALSE(pattern.match_subdomains());
+ EXPECT_FALSE(pattern.match_all_urls());
+ EXPECT_EQ("/foo?bar\\*baz", pattern.path());
+ EXPECT_TRUE(pattern.MatchesURL(GURL("file:///foo?bar\\hellobaz")));
+ EXPECT_FALSE(pattern.MatchesURL(GURL("file:///fooXbar\\hellobaz")));
+}
+
+// ip addresses
+TEST(ExtensionURLPatternTest, Match6) {
+ URLPattern pattern(kAllSchemes);
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("http://127.0.0.1/*"));
+ EXPECT_EQ("http", pattern.scheme());
+ EXPECT_EQ("127.0.0.1", pattern.host());
+ EXPECT_FALSE(pattern.match_subdomains());
+ EXPECT_FALSE(pattern.match_all_urls());
+ EXPECT_EQ("/*", pattern.path());
+ EXPECT_TRUE(pattern.MatchesURL(GURL("http://127.0.0.1")));
+}
+
+// subdomain matching with ip addresses
+TEST(ExtensionURLPatternTest, Match7) {
+ URLPattern pattern(kAllSchemes);
+ // allowed, but useless
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("http://*.0.0.1/*"));
+ EXPECT_EQ("http", pattern.scheme());
+ EXPECT_EQ("0.0.1", pattern.host());
+ EXPECT_TRUE(pattern.match_subdomains());
+ EXPECT_FALSE(pattern.match_all_urls());
+ EXPECT_EQ("/*", pattern.path());
+ // Subdomain matching is never done if the argument has an IP address host.
+ EXPECT_FALSE(pattern.MatchesURL(GURL("http://127.0.0.1")));
+};
+
+// unicode
+TEST(ExtensionURLPatternTest, Match8) {
+ URLPattern pattern(kAllSchemes);
+ // The below is the ASCII encoding of the following URL:
+ // http://*.\xe1\x80\xbf/a\xc2\x81\xe1*
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS,
+ pattern.Parse("http://*.xn--gkd/a%C2%81%E1*"));
+ EXPECT_EQ("http", pattern.scheme());
+ EXPECT_EQ("xn--gkd", pattern.host());
+ EXPECT_TRUE(pattern.match_subdomains());
+ EXPECT_FALSE(pattern.match_all_urls());
+ EXPECT_EQ("/a%C2%81%E1*", pattern.path());
+ EXPECT_TRUE(pattern.MatchesURL(
+ GURL("http://abc.\xe1\x80\xbf/a\xc2\x81\xe1xyz")));
+ EXPECT_TRUE(pattern.MatchesURL(
+ GURL("http://\xe1\x80\xbf/a\xc2\x81\xe1\xe1")));
+};
+
+// chrome://
+TEST(ExtensionURLPatternTest, Match9) {
+ URLPattern pattern(kAllSchemes);
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("chrome://favicon/*"));
+ EXPECT_EQ("chrome", pattern.scheme());
+ EXPECT_EQ("favicon", pattern.host());
+ EXPECT_FALSE(pattern.match_subdomains());
+ EXPECT_FALSE(pattern.match_all_urls());
+ EXPECT_EQ("/*", pattern.path());
+ EXPECT_TRUE(pattern.MatchesURL(GURL("chrome://favicon/http://google.com")));
+ EXPECT_TRUE(pattern.MatchesURL(GURL("chrome://favicon/https://google.com")));
+ EXPECT_FALSE(pattern.MatchesURL(GURL("chrome://history")));
+};
+
+// *://
+TEST(ExtensionURLPatternTest, Match10) {
+ URLPattern pattern(kAllSchemes);
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS, 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_FALSE(pattern.match_all_urls());
+ 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")));
+ EXPECT_FALSE(pattern.MatchesURL(GURL("file://localhost/foo/bar")));
+};
+
+// <all_urls>
+TEST(ExtensionURLPatternTest, Match11) {
+ URLPattern pattern(kAllSchemes);
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS, 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.MatchesScheme("filesystem"));
+ EXPECT_TRUE(pattern.MatchesScheme("chrome-extension"));
+ EXPECT_TRUE(pattern.match_subdomains());
+ EXPECT_TRUE(pattern.match_all_urls());
+ 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")));
+ EXPECT_TRUE(pattern.MatchesURL(GURL("file://localhost/foo/bar")));
+
+ // Make sure the properties are the same when creating an <all_urls> pattern
+ // via SetMatchAllURLs and by parsing <all_urls>.
+ URLPattern pattern2(kAllSchemes);
+ pattern2.SetMatchAllURLs(true);
+
+ EXPECT_EQ(pattern.valid_schemes(), pattern2.valid_schemes());
+ EXPECT_EQ(pattern.match_subdomains(), pattern2.match_subdomains());
+ EXPECT_EQ(pattern.path(), pattern2.path());
+ EXPECT_EQ(pattern.match_all_urls(), pattern2.match_all_urls());
+ EXPECT_EQ(pattern.scheme(), pattern2.scheme());
+ EXPECT_EQ(pattern.port(), pattern2.port());
+ EXPECT_EQ(pattern.GetAsString(), pattern2.GetAsString());
+};
+
+// SCHEME_ALL matches all schemes.
+TEST(ExtensionURLPatternTest, Match12) {
+ URLPattern pattern(URLPattern::SCHEME_ALL);
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS, 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.MatchesScheme("filesystem"));
+ EXPECT_TRUE(pattern.MatchesScheme("javascript"));
+ EXPECT_TRUE(pattern.MatchesScheme("data"));
+ EXPECT_TRUE(pattern.MatchesScheme("about"));
+ EXPECT_TRUE(pattern.MatchesScheme("chrome-extension"));
+ EXPECT_TRUE(pattern.match_subdomains());
+ EXPECT_TRUE(pattern.match_all_urls());
+ 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")));
+ EXPECT_TRUE(pattern.MatchesURL(GURL("file://localhost/foo/bar")));
+ EXPECT_TRUE(pattern.MatchesURL(GURL("chrome://newtab")));
+ EXPECT_TRUE(pattern.MatchesURL(GURL("about:blank")));
+ EXPECT_TRUE(pattern.MatchesURL(GURL("about:version")));
+ EXPECT_TRUE(pattern.MatchesURL(
+ GURL("data:text/html;charset=utf-8,<html>asdf</html>")));
+};
+
+static const struct MatchPatterns {
+ const char* pattern;
+ const char* matches;
+} kMatch13UrlPatternTestCases[] = {
+ {"about:*", "about:blank"},
+ {"about:blank", "about:blank"},
+ {"about:*", "about:version"},
+ {"chrome-extension://*/*", "chrome-extension://FTW"},
+ {"data:*", "data:monkey"},
+ {"javascript:*", "javascript:atemyhomework"},
+};
+
+// SCHEME_ALL and specific schemes.
+TEST(ExtensionURLPatternTest, Match13) {
+ for (size_t i = 0; i < arraysize(kMatch13UrlPatternTestCases); ++i) {
+ URLPattern pattern(URLPattern::SCHEME_ALL);
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS,
+ pattern.Parse(kMatch13UrlPatternTestCases[i].pattern))
+ << " while parsing " << kMatch13UrlPatternTestCases[i].pattern;
+ EXPECT_TRUE(pattern.MatchesURL(
+ GURL(kMatch13UrlPatternTestCases[i].matches)))
+ << " while matching " << kMatch13UrlPatternTestCases[i].matches;
+ }
+
+ // Negative test.
+ URLPattern pattern(URLPattern::SCHEME_ALL);
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("data:*"));
+ EXPECT_FALSE(pattern.MatchesURL(GURL("about:blank")));
+};
+
+// file scheme with empty hostname
+TEST(ExtensionURLPatternTest, Match14) {
+ URLPattern pattern(kAllSchemes);
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("file:///foo*"));
+ EXPECT_EQ("file", pattern.scheme());
+ EXPECT_EQ("", pattern.host());
+ EXPECT_FALSE(pattern.match_subdomains());
+ EXPECT_FALSE(pattern.match_all_urls());
+ EXPECT_EQ("/foo*", pattern.path());
+ EXPECT_FALSE(pattern.MatchesURL(GURL("file://foo")));
+ EXPECT_FALSE(pattern.MatchesURL(GURL("file://foobar")));
+ EXPECT_TRUE(pattern.MatchesURL(GURL("file:///foo")));
+ EXPECT_TRUE(pattern.MatchesURL(GURL("file:///foobar")));
+ EXPECT_TRUE(pattern.MatchesURL(GURL("file://localhost/foo")));
+}
+
+// file scheme without hostname part
+TEST(ExtensionURLPatternTest, Match15) {
+ URLPattern pattern(kAllSchemes);
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("file://foo*"));
+ EXPECT_EQ("file", pattern.scheme());
+ EXPECT_EQ("", pattern.host());
+ EXPECT_FALSE(pattern.match_subdomains());
+ EXPECT_FALSE(pattern.match_all_urls());
+ EXPECT_EQ("/foo*", pattern.path());
+ EXPECT_FALSE(pattern.MatchesURL(GURL("file://foo")));
+ EXPECT_FALSE(pattern.MatchesURL(GURL("file://foobar")));
+ EXPECT_TRUE(pattern.MatchesURL(GURL("file:///foo")));
+ EXPECT_TRUE(pattern.MatchesURL(GURL("file:///foobar")));
+ EXPECT_TRUE(pattern.MatchesURL(GURL("file://localhost/foo")));
+}
+
+// file scheme with hostname
+TEST(ExtensionURLPatternTest, Match16) {
+ URLPattern pattern(kAllSchemes);
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("file://localhost/foo*"));
+ EXPECT_EQ("file", pattern.scheme());
+ // Since hostname is ignored for file://.
+ EXPECT_EQ("", pattern.host());
+ EXPECT_FALSE(pattern.match_subdomains());
+ EXPECT_FALSE(pattern.match_all_urls());
+ EXPECT_EQ("/foo*", pattern.path());
+ EXPECT_FALSE(pattern.MatchesURL(GURL("file://foo")));
+ EXPECT_FALSE(pattern.MatchesURL(GURL("file://foobar")));
+ EXPECT_TRUE(pattern.MatchesURL(GURL("file:///foo")));
+ EXPECT_TRUE(pattern.MatchesURL(GURL("file:///foobar")));
+ EXPECT_TRUE(pattern.MatchesURL(GURL("file://localhost/foo")));
+}
+
+// Specific port
+TEST(ExtensionURLPatternTest, Match17) {
+ URLPattern pattern(kAllSchemes);
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS,
+ pattern.Parse("http://www.example.com:80/foo"));
+ EXPECT_EQ("http", pattern.scheme());
+ EXPECT_EQ("www.example.com", pattern.host());
+ EXPECT_FALSE(pattern.match_subdomains());
+ EXPECT_FALSE(pattern.match_all_urls());
+ EXPECT_EQ("/foo", pattern.path());
+ EXPECT_EQ("80", pattern.port());
+ EXPECT_TRUE(pattern.MatchesURL(GURL("http://www.example.com:80/foo")));
+ EXPECT_TRUE(pattern.MatchesURL(GURL("http://www.example.com/foo")));
+ EXPECT_FALSE(pattern.MatchesURL(GURL("http://www.example.com:8080/foo")));
+ EXPECT_FALSE(pattern.MatchesURL(
+ GURL("filesystem:http://www.example.com:8080/foo/")));
+ EXPECT_FALSE(pattern.MatchesURL(GURL("filesystem:http://www.example.com/f/foo")));
+}
+
+// Explicit port wildcard
+TEST(ExtensionURLPatternTest, Match18) {
+ URLPattern pattern(kAllSchemes);
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS,
+ pattern.Parse("http://www.example.com:*/foo"));
+ EXPECT_EQ("http", pattern.scheme());
+ EXPECT_EQ("www.example.com", pattern.host());
+ EXPECT_FALSE(pattern.match_subdomains());
+ EXPECT_FALSE(pattern.match_all_urls());
+ EXPECT_EQ("/foo", pattern.path());
+ EXPECT_EQ("*", pattern.port());
+ EXPECT_TRUE(pattern.MatchesURL(GURL("http://www.example.com:80/foo")));
+ EXPECT_TRUE(pattern.MatchesURL(GURL("http://www.example.com/foo")));
+ EXPECT_TRUE(pattern.MatchesURL(GURL("http://www.example.com:8080/foo")));
+ EXPECT_FALSE(pattern.MatchesURL(
+ GURL("filesystem:http://www.example.com:8080/foo/")));
+}
+
+// chrome-extension://
+TEST(ExtensionURLPatternTest, Match19) {
+ URLPattern pattern(URLPattern::SCHEME_EXTENSION);
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS,
+ pattern.Parse("chrome-extension://ftw/*"));
+ EXPECT_EQ("chrome-extension", pattern.scheme());
+ EXPECT_EQ("ftw", pattern.host());
+ EXPECT_FALSE(pattern.match_subdomains());
+ EXPECT_FALSE(pattern.match_all_urls());
+ EXPECT_EQ("/*", pattern.path());
+ EXPECT_TRUE(pattern.MatchesURL(GURL("chrome-extension://ftw")));
+ EXPECT_TRUE(pattern.MatchesURL(
+ GURL("chrome-extension://ftw/http://google.com")));
+ EXPECT_TRUE(pattern.MatchesURL(
+ GURL("chrome-extension://ftw/https://google.com")));
+ EXPECT_FALSE(pattern.MatchesURL(GURL("chrome-extension://foobar")));
+ EXPECT_TRUE(pattern.MatchesURL(
+ GURL("filesystem:chrome-extension://ftw/t/file.txt")));
+};
+
+static const struct GetAsStringPatterns {
+ const char* pattern;
+} kGetAsStringTestCases[] = {
+ { "http://www/" },
+ { "http://*/*" },
+ { "chrome://*/*" },
+ { "chrome://newtab/" },
+ { "about:*" },
+ { "about:blank" },
+ { "chrome-extension://*/*" },
+ { "chrome-extension://FTW/" },
+ { "data:*" },
+ { "data:monkey" },
+ { "javascript:*" },
+ { "javascript:atemyhomework" },
+ { "http://www.example.com:8080/foo" },
+};
+
+TEST(ExtensionURLPatternTest, GetAsString) {
+ for (size_t i = 0; i < arraysize(kGetAsStringTestCases); ++i) {
+ URLPattern pattern(URLPattern::SCHEME_ALL);
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS,
+ pattern.Parse(kGetAsStringTestCases[i].pattern))
+ << "Error parsing " << kGetAsStringTestCases[i].pattern;
+ EXPECT_EQ(kGetAsStringTestCases[i].pattern,
+ pattern.GetAsString());
+ }
+}
+
+testing::AssertionResult Overlaps(const URLPattern& pattern1,
+ const URLPattern& pattern2) {
+ if (!pattern1.OverlapsWith(pattern2)) {
+ return testing::AssertionFailure()
+ << pattern1.GetAsString() << " does not overlap " <<
+ pattern2.GetAsString();
+ }
+ if (!pattern2.OverlapsWith(pattern1)) {
+ return testing::AssertionFailure()
+ << pattern2.GetAsString() << " does not overlap " <<
+ pattern1.GetAsString();
+ }
+ return testing::AssertionSuccess()
+ << pattern1.GetAsString() << " overlaps with " << pattern2.GetAsString();
+}
+
+TEST(ExtensionURLPatternTest, Overlaps) {
+ URLPattern pattern1(kAllSchemes, "http://www.google.com/foo/*");
+ URLPattern pattern2(kAllSchemes, "https://www.google.com/foo/*");
+ URLPattern pattern3(kAllSchemes, "http://*.google.com/foo/*");
+ URLPattern pattern4(kAllSchemes, "http://*.yahooo.com/foo/*");
+ URLPattern pattern5(kAllSchemes, "http://www.yahooo.com/bar/*");
+ URLPattern pattern6(kAllSchemes,
+ "http://www.yahooo.com/bar/baz/*");
+ URLPattern pattern7(kAllSchemes, "file:///*");
+ URLPattern pattern8(kAllSchemes, "*://*/*");
+ URLPattern pattern9(URLPattern::SCHEME_HTTPS, "*://*/*");
+ URLPattern pattern10(kAllSchemes, "<all_urls>");
+
+ EXPECT_TRUE(Overlaps(pattern1, pattern1));
+ EXPECT_FALSE(Overlaps(pattern1, pattern2));
+ EXPECT_TRUE(Overlaps(pattern1, pattern3));
+ EXPECT_FALSE(Overlaps(pattern1, pattern4));
+ EXPECT_FALSE(Overlaps(pattern3, pattern4));
+ EXPECT_FALSE(Overlaps(pattern4, pattern5));
+ EXPECT_TRUE(Overlaps(pattern5, pattern6));
+
+ // Test that scheme restrictions work.
+ EXPECT_TRUE(Overlaps(pattern1, pattern8));
+ EXPECT_FALSE(Overlaps(pattern1, pattern9));
+ EXPECT_TRUE(Overlaps(pattern1, pattern10));
+
+ // Test that '<all_urls>' includes file URLs, while scheme '*' does not.
+ EXPECT_FALSE(Overlaps(pattern7, pattern8));
+ EXPECT_TRUE(Overlaps(pattern7, pattern10));
+
+ // Test that wildcard schemes are handled correctly, especially when compared
+ // to each-other.
+ URLPattern pattern11(kAllSchemes, "http://example.com/*");
+ URLPattern pattern12(kAllSchemes, "*://example.com/*");
+ URLPattern pattern13(kAllSchemes, "*://example.com/foo/*");
+ URLPattern pattern14(kAllSchemes, "*://google.com/*");
+ EXPECT_TRUE(Overlaps(pattern8, pattern12));
+ EXPECT_TRUE(Overlaps(pattern9, pattern12));
+ EXPECT_TRUE(Overlaps(pattern10, pattern12));
+ EXPECT_TRUE(Overlaps(pattern11, pattern12));
+ EXPECT_TRUE(Overlaps(pattern12, pattern13));
+ EXPECT_TRUE(Overlaps(pattern11, pattern13));
+ EXPECT_FALSE(Overlaps(pattern14, pattern12));
+ EXPECT_FALSE(Overlaps(pattern14, pattern13));
+}
+
+TEST(ExtensionURLPatternTest, ConvertToExplicitSchemes) {
+ URLPatternList all_urls(URLPattern(
+ kAllSchemes,
+ "<all_urls>").ConvertToExplicitSchemes());
+
+ URLPatternList all_schemes(URLPattern(
+ kAllSchemes,
+ "*://google.com/foo").ConvertToExplicitSchemes());
+
+ URLPatternList monkey(URLPattern(
+ URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS |
+ URLPattern::SCHEME_FTP,
+ "http://google.com/monkey").ConvertToExplicitSchemes());
+
+ ASSERT_EQ(7u, all_urls.size());
+ ASSERT_EQ(2u, all_schemes.size());
+ ASSERT_EQ(1u, monkey.size());
+
+ EXPECT_EQ("http://*/*", all_urls[0].GetAsString());
+ EXPECT_EQ("https://*/*", all_urls[1].GetAsString());
+ EXPECT_EQ("file:///*", all_urls[2].GetAsString());
+ EXPECT_EQ("ftp://*/*", all_urls[3].GetAsString());
+ EXPECT_EQ("chrome://*/*", all_urls[4].GetAsString());
+
+ EXPECT_EQ("http://google.com/foo", all_schemes[0].GetAsString());
+ EXPECT_EQ("https://google.com/foo", all_schemes[1].GetAsString());
+
+ EXPECT_EQ("http://google.com/monkey", monkey[0].GetAsString());
+}
+
+TEST(ExtensionURLPatternTest, IgnorePorts) {
+ std::string pattern_str = "http://www.example.com:8080/foo";
+ GURL url("http://www.example.com:1234/foo");
+
+ URLPattern pattern(kAllSchemes);
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse(pattern_str));
+
+ EXPECT_EQ(pattern_str, pattern.GetAsString());
+ EXPECT_FALSE(pattern.MatchesURL(url));
+}
+
+TEST(ExtensionURLPatternTest, IgnoreMissingBackslashes) {
+ std::string pattern_str1 = "http://www.example.com/example";
+ std::string pattern_str2 = "http://www.example.com/example/*";
+ GURL url1("http://www.example.com/example");
+ GURL url2("http://www.example.com/example/");
+
+ URLPattern pattern1(kAllSchemes);
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern1.Parse(pattern_str1));
+ URLPattern pattern2(kAllSchemes);
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern2.Parse(pattern_str2));
+
+ // Same patterns should match same urls.
+ EXPECT_TRUE(pattern1.MatchesURL(url1));
+ EXPECT_TRUE(pattern2.MatchesURL(url2));
+ // The not terminated path should match the terminated pattern.
+ EXPECT_TRUE(pattern2.MatchesURL(url1));
+ // The terminated path however should not match the unterminated pattern.
+ EXPECT_FALSE(pattern1.MatchesURL(url2));
+}
+
+TEST(ExtensionURLPatternTest, Equals) {
+ const struct {
+ const char* pattern1;
+ const char* pattern2;
+ bool expected_equal;
+ } kEqualsTestCases[] = {
+ // schemes
+ { "http://en.google.com/blah/*/foo",
+ "https://en.google.com/blah/*/foo",
+ false
+ },
+ { "https://en.google.com/blah/*/foo",
+ "https://en.google.com/blah/*/foo",
+ true
+ },
+ { "https://en.google.com/blah/*/foo",
+ "ftp://en.google.com/blah/*/foo",
+ false
+ },
+
+ // subdomains
+ { "https://en.google.com/blah/*/foo",
+ "https://fr.google.com/blah/*/foo",
+ false
+ },
+ { "https://www.google.com/blah/*/foo",
+ "https://*.google.com/blah/*/foo",
+ false
+ },
+ { "https://*.google.com/blah/*/foo",
+ "https://*.google.com/blah/*/foo",
+ true
+ },
+
+ // domains
+ { "http://en.example.com/blah/*/foo",
+ "http://en.google.com/blah/*/foo",
+ false
+ },
+
+ // ports
+ { "http://en.google.com:8000/blah/*/foo",
+ "http://en.google.com/blah/*/foo",
+ false
+ },
+ { "http://fr.google.com:8000/blah/*/foo",
+ "http://fr.google.com:8000/blah/*/foo",
+ true
+ },
+ { "http://en.google.com:8000/blah/*/foo",
+ "http://en.google.com:8080/blah/*/foo",
+ false
+ },
+
+ // paths
+ { "http://en.google.com/blah/*/foo",
+ "http://en.google.com/blah/*",
+ false
+ },
+ { "http://en.google.com/*",
+ "http://en.google.com/",
+ false
+ },
+ { "http://en.google.com/*",
+ "http://en.google.com/*",
+ true
+ },
+
+ // all_urls
+ { "<all_urls>",
+ "<all_urls>",
+ true
+ },
+ { "<all_urls>",
+ "http://*/*",
+ false
+ }
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kEqualsTestCases); ++i) {
+ std::string message = kEqualsTestCases[i].pattern1;
+ message += " ";
+ message += kEqualsTestCases[i].pattern2;
+
+ URLPattern pattern1(URLPattern::SCHEME_ALL);
+ URLPattern pattern2(URLPattern::SCHEME_ALL);
+
+ pattern1.Parse(kEqualsTestCases[i].pattern1);
+ pattern2.Parse(kEqualsTestCases[i].pattern2);
+ EXPECT_EQ(kEqualsTestCases[i].expected_equal, pattern1 == pattern2)
+ << message;
+ }
+}
+
+TEST(ExtensionURLPatternTest, CanReusePatternWithParse) {
+ URLPattern pattern1(URLPattern::SCHEME_ALL);
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern1.Parse("http://aa.com/*"));
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern1.Parse("http://bb.com/*"));
+
+ EXPECT_TRUE(pattern1.MatchesURL(GURL("http://bb.com/path")));
+ EXPECT_FALSE(pattern1.MatchesURL(GURL("http://aa.com/path")));
+
+ URLPattern pattern2(URLPattern::SCHEME_ALL, URLPattern::kAllUrlsPattern);
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern2.Parse("http://aa.com/*"));
+
+ EXPECT_FALSE(pattern2.MatchesURL(GURL("http://bb.com/path")));
+ EXPECT_TRUE(pattern2.MatchesURL(GURL("http://aa.com/path")));
+ EXPECT_FALSE(pattern2.MatchesURL(GURL("http://sub.aa.com/path")));
+
+ URLPattern pattern3(URLPattern::SCHEME_ALL, "http://aa.com/*");
+ EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern3.Parse("http://aa.com:88/*"));
+ EXPECT_FALSE(pattern3.MatchesURL(GURL("http://aa.com/path")));
+ EXPECT_TRUE(pattern3.MatchesURL(GURL("http://aa.com:88/path")));
+}
+
+// Returns success if neither |a| nor |b| encompasses the other.
+testing::AssertionResult NeitherContains(const URLPattern& a,
+ const URLPattern& b) {
+ if (a.Contains(b))
+ return testing::AssertionFailure() << a.GetAsString() << " encompasses " <<
+ b.GetAsString();
+ if (b.Contains(a))
+ return testing::AssertionFailure() << b.GetAsString() << " encompasses " <<
+ a.GetAsString();
+ return testing::AssertionSuccess() <<
+ "Neither " << a.GetAsString() << " nor " << b.GetAsString() <<
+ " encompass the other";
+}
+
+// Returns success if |a| encompasses |b| but not the other way around.
+testing::AssertionResult StrictlyContains(const URLPattern& a,
+ const URLPattern& b) {
+ if (!a.Contains(b))
+ return testing::AssertionFailure() << a.GetAsString() <<
+ " does not encompass " <<
+ b.GetAsString();
+ if (b.Contains(a))
+ return testing::AssertionFailure() << b.GetAsString() << " encompasses " <<
+ a.GetAsString();
+ return testing::AssertionSuccess() << a.GetAsString() <<
+ " strictly encompasses " <<
+ b.GetAsString();
+}
+
+TEST(ExtensionURLPatternTest, Subset) {
+ URLPattern pattern1(kAllSchemes, "http://www.google.com/foo/*");
+ URLPattern pattern2(kAllSchemes, "https://www.google.com/foo/*");
+ URLPattern pattern3(kAllSchemes, "http://*.google.com/foo/*");
+ URLPattern pattern4(kAllSchemes, "http://*.yahooo.com/foo/*");
+ URLPattern pattern5(kAllSchemes, "http://www.yahooo.com/bar/*");
+ URLPattern pattern6(kAllSchemes, "http://www.yahooo.com/bar/baz/*");
+ URLPattern pattern7(kAllSchemes, "file:///*");
+ URLPattern pattern8(kAllSchemes, "*://*/*");
+ URLPattern pattern9(URLPattern::SCHEME_HTTPS, "*://*/*");
+ URLPattern pattern10(kAllSchemes, "<all_urls>");
+ URLPattern pattern11(kAllSchemes, "http://example.com/*");
+ URLPattern pattern12(kAllSchemes, "*://example.com/*");
+ URLPattern pattern13(kAllSchemes, "*://example.com/foo/*");
+
+ // All patterns should encompass themselves.
+ EXPECT_TRUE(pattern1.Contains(pattern1));
+ EXPECT_TRUE(pattern2.Contains(pattern2));
+ EXPECT_TRUE(pattern3.Contains(pattern3));
+ EXPECT_TRUE(pattern4.Contains(pattern4));
+ EXPECT_TRUE(pattern5.Contains(pattern5));
+ EXPECT_TRUE(pattern6.Contains(pattern6));
+ EXPECT_TRUE(pattern7.Contains(pattern7));
+ EXPECT_TRUE(pattern8.Contains(pattern8));
+ EXPECT_TRUE(pattern9.Contains(pattern9));
+ EXPECT_TRUE(pattern10.Contains(pattern10));
+ EXPECT_TRUE(pattern11.Contains(pattern11));
+ EXPECT_TRUE(pattern12.Contains(pattern12));
+ EXPECT_TRUE(pattern13.Contains(pattern13));
+
+ // pattern1's relationship to the other patterns.
+ EXPECT_TRUE(NeitherContains(pattern1, pattern2));
+ EXPECT_TRUE(StrictlyContains(pattern3, pattern1));
+ EXPECT_TRUE(NeitherContains(pattern1, pattern4));
+ EXPECT_TRUE(NeitherContains(pattern1, pattern5));
+ EXPECT_TRUE(NeitherContains(pattern1, pattern6));
+ EXPECT_TRUE(NeitherContains(pattern1, pattern7));
+ EXPECT_TRUE(StrictlyContains(pattern8, pattern1));
+ EXPECT_TRUE(NeitherContains(pattern1, pattern9));
+ EXPECT_TRUE(StrictlyContains(pattern10, pattern1));
+ EXPECT_TRUE(NeitherContains(pattern1, pattern11));
+ EXPECT_TRUE(NeitherContains(pattern1, pattern12));
+ EXPECT_TRUE(NeitherContains(pattern1, pattern13));
+
+ // pattern2's relationship to the other patterns.
+ EXPECT_TRUE(NeitherContains(pattern2, pattern3));
+ EXPECT_TRUE(NeitherContains(pattern2, pattern4));
+ EXPECT_TRUE(NeitherContains(pattern2, pattern5));
+ EXPECT_TRUE(NeitherContains(pattern2, pattern6));
+ EXPECT_TRUE(NeitherContains(pattern2, pattern7));
+ EXPECT_TRUE(StrictlyContains(pattern8, pattern2));
+ EXPECT_TRUE(StrictlyContains(pattern9, pattern2));
+ EXPECT_TRUE(StrictlyContains(pattern10, pattern2));
+ EXPECT_TRUE(NeitherContains(pattern2, pattern11));
+ EXPECT_TRUE(NeitherContains(pattern2, pattern12));
+ EXPECT_TRUE(NeitherContains(pattern2, pattern13));
+
+ // Specifically test file:// URLs.
+ EXPECT_TRUE(NeitherContains(pattern7, pattern8));
+ EXPECT_TRUE(NeitherContains(pattern7, pattern9));
+ EXPECT_TRUE(StrictlyContains(pattern10, pattern7));
+
+ // <all_urls> encompasses everything.
+ EXPECT_TRUE(StrictlyContains(pattern10, pattern1));
+ EXPECT_TRUE(StrictlyContains(pattern10, pattern2));
+ EXPECT_TRUE(StrictlyContains(pattern10, pattern3));
+ EXPECT_TRUE(StrictlyContains(pattern10, pattern4));
+ EXPECT_TRUE(StrictlyContains(pattern10, pattern5));
+ EXPECT_TRUE(StrictlyContains(pattern10, pattern6));
+ EXPECT_TRUE(StrictlyContains(pattern10, pattern7));
+ EXPECT_TRUE(StrictlyContains(pattern10, pattern8));
+ EXPECT_TRUE(StrictlyContains(pattern10, pattern9));
+ EXPECT_TRUE(StrictlyContains(pattern10, pattern11));
+ EXPECT_TRUE(StrictlyContains(pattern10, pattern12));
+ EXPECT_TRUE(StrictlyContains(pattern10, pattern13));
+
+ // More...
+ EXPECT_TRUE(StrictlyContains(pattern12, pattern11));
+ EXPECT_TRUE(NeitherContains(pattern11, pattern13));
+ EXPECT_TRUE(StrictlyContains(pattern12, pattern13));
+}
+
+} // namespace
diff --git a/chromium/extensions/common/user_script.cc b/chromium/extensions/common/user_script.cc
new file mode 100644
index 00000000000..84c1236c604
--- /dev/null
+++ b/chromium/extensions/common/user_script.cc
@@ -0,0 +1,236 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/common/user_script.h"
+
+#include "base/command_line.h"
+#include "base/pickle.h"
+#include "base/strings/string_util.h"
+#include "extensions/common/switches.h"
+
+namespace {
+
+bool UrlMatchesGlobs(const std::vector<std::string>* globs,
+ const GURL& url) {
+ for (std::vector<std::string>::const_iterator glob = globs->begin();
+ glob != globs->end(); ++glob) {
+ if (MatchPattern(url.spec(), *glob))
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace
+
+namespace extensions {
+
+// The bitmask for valid user script injectable schemes used by URLPattern.
+enum {
+ kValidUserScriptSchemes = URLPattern::SCHEME_CHROMEUI |
+ URLPattern::SCHEME_HTTP |
+ URLPattern::SCHEME_HTTPS |
+ URLPattern::SCHEME_FILE |
+ URLPattern::SCHEME_FTP
+};
+
+// static
+const char UserScript::kFileExtension[] = ".user.js";
+
+bool UserScript::IsURLUserScript(const GURL& url,
+ const std::string& mime_type) {
+ return EndsWith(url.ExtractFileName(), kFileExtension, false) &&
+ mime_type != "text/html";
+}
+
+// static
+int UserScript::ValidUserScriptSchemes(bool canExecuteScriptEverywhere) {
+ if (canExecuteScriptEverywhere)
+ return URLPattern::SCHEME_ALL;
+ int valid_schemes = kValidUserScriptSchemes;
+ if (!CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kExtensionsOnChromeURLs)) {
+ valid_schemes &= ~URLPattern::SCHEME_CHROMEUI;
+ }
+ return valid_schemes;
+}
+
+UserScript::File::File(const base::FilePath& extension_root,
+ const base::FilePath& relative_path,
+ const GURL& url)
+ : extension_root_(extension_root),
+ relative_path_(relative_path),
+ url_(url) {
+}
+
+UserScript::File::File() {}
+
+UserScript::File::~File() {}
+
+UserScript::UserScript()
+ : run_location_(DOCUMENT_IDLE), emulate_greasemonkey_(false),
+ match_all_frames_(false), incognito_enabled_(false) {
+}
+
+UserScript::~UserScript() {
+}
+
+void UserScript::add_url_pattern(const URLPattern& pattern) {
+ url_set_.AddPattern(pattern);
+}
+
+void UserScript::add_exclude_url_pattern(const URLPattern& pattern) {
+ exclude_url_set_.AddPattern(pattern);
+}
+
+bool UserScript::MatchesURL(const GURL& url) const {
+ if (!url_set_.is_empty()) {
+ if (!url_set_.MatchesURL(url))
+ return false;
+ }
+
+ if (!exclude_url_set_.is_empty()) {
+ if (exclude_url_set_.MatchesURL(url))
+ return false;
+ }
+
+ if (!globs_.empty()) {
+ if (!UrlMatchesGlobs(&globs_, url))
+ return false;
+ }
+
+ if (!exclude_globs_.empty()) {
+ if (UrlMatchesGlobs(&exclude_globs_, url))
+ return false;
+ }
+
+ return true;
+}
+
+void UserScript::File::Pickle(::Pickle* pickle) const {
+ pickle->WriteString(url_.spec());
+ // Do not write path. It's not needed in the renderer.
+ // Do not write content. It will be serialized by other means.
+}
+
+void UserScript::File::Unpickle(const ::Pickle& pickle, PickleIterator* iter) {
+ // Read the url from the pickle.
+ std::string url;
+ CHECK(pickle.ReadString(iter, &url));
+ set_url(GURL(url));
+}
+
+void UserScript::Pickle(::Pickle* pickle) const {
+ // Write the simple types to the pickle.
+ pickle->WriteInt(run_location());
+ pickle->WriteString(extension_id());
+ pickle->WriteBool(emulate_greasemonkey());
+ pickle->WriteBool(match_all_frames());
+ pickle->WriteBool(is_incognito_enabled());
+
+ PickleGlobs(pickle, globs_);
+ PickleGlobs(pickle, exclude_globs_);
+ PickleURLPatternSet(pickle, url_set_);
+ PickleURLPatternSet(pickle, exclude_url_set_);
+ PickleScripts(pickle, js_scripts_);
+ PickleScripts(pickle, css_scripts_);
+}
+
+void UserScript::PickleGlobs(::Pickle* pickle,
+ const std::vector<std::string>& globs) const {
+ pickle->WriteUInt64(globs.size());
+ for (std::vector<std::string>::const_iterator glob = globs.begin();
+ glob != globs.end(); ++glob) {
+ pickle->WriteString(*glob);
+ }
+}
+
+void UserScript::PickleURLPatternSet(::Pickle* pickle,
+ const URLPatternSet& pattern_list) const {
+ pickle->WriteUInt64(pattern_list.patterns().size());
+ for (URLPatternSet::const_iterator pattern = pattern_list.begin();
+ pattern != pattern_list.end(); ++pattern) {
+ pickle->WriteInt(pattern->valid_schemes());
+ pickle->WriteString(pattern->GetAsString());
+ }
+}
+
+void UserScript::PickleScripts(::Pickle* pickle,
+ const FileList& scripts) const {
+ pickle->WriteUInt64(scripts.size());
+ for (FileList::const_iterator file = scripts.begin();
+ file != scripts.end(); ++file) {
+ file->Pickle(pickle);
+ }
+}
+
+void UserScript::Unpickle(const ::Pickle& pickle, PickleIterator* iter) {
+ // Read the run location.
+ int run_location = 0;
+ CHECK(pickle.ReadInt(iter, &run_location));
+ CHECK(run_location >= 0 && run_location < RUN_LOCATION_LAST);
+ run_location_ = static_cast<RunLocation>(run_location);
+
+ CHECK(pickle.ReadString(iter, &extension_id_));
+ CHECK(pickle.ReadBool(iter, &emulate_greasemonkey_));
+ CHECK(pickle.ReadBool(iter, &match_all_frames_));
+ CHECK(pickle.ReadBool(iter, &incognito_enabled_));
+
+ UnpickleGlobs(pickle, iter, &globs_);
+ UnpickleGlobs(pickle, iter, &exclude_globs_);
+ UnpickleURLPatternSet(pickle, iter, &url_set_);
+ UnpickleURLPatternSet(pickle, iter, &exclude_url_set_);
+ UnpickleScripts(pickle, iter, &js_scripts_);
+ UnpickleScripts(pickle, iter, &css_scripts_);
+}
+
+void UserScript::UnpickleGlobs(const ::Pickle& pickle, PickleIterator* iter,
+ std::vector<std::string>* globs) {
+ uint64 num_globs = 0;
+ CHECK(pickle.ReadUInt64(iter, &num_globs));
+ globs->clear();
+ for (uint64 i = 0; i < num_globs; ++i) {
+ std::string glob;
+ CHECK(pickle.ReadString(iter, &glob));
+ globs->push_back(glob);
+ }
+}
+
+void UserScript::UnpickleURLPatternSet(const ::Pickle& pickle,
+ PickleIterator* iter,
+ URLPatternSet* pattern_list) {
+ uint64 num_patterns = 0;
+ CHECK(pickle.ReadUInt64(iter, &num_patterns));
+
+ pattern_list->ClearPatterns();
+ for (uint64 i = 0; i < num_patterns; ++i) {
+ int valid_schemes;
+ CHECK(pickle.ReadInt(iter, &valid_schemes));
+
+ std::string pattern_str;
+ CHECK(pickle.ReadString(iter, &pattern_str));
+
+ URLPattern pattern(kValidUserScriptSchemes);
+ URLPattern::ParseResult result = pattern.Parse(pattern_str);
+ CHECK(URLPattern::PARSE_SUCCESS == result) <<
+ URLPattern::GetParseResultString(result) << " " << pattern_str.c_str();
+
+ pattern.SetValidSchemes(valid_schemes);
+ pattern_list->AddPattern(pattern);
+ }
+}
+
+void UserScript::UnpickleScripts(const ::Pickle& pickle, PickleIterator* iter,
+ FileList* scripts) {
+ uint64 num_files = 0;
+ CHECK(pickle.ReadUInt64(iter, &num_files));
+ scripts->clear();
+ for (uint64 i = 0; i < num_files; ++i) {
+ File file;
+ file.Unpickle(pickle, iter);
+ scripts->push_back(file);
+ }
+}
+
+} // namespace extensions
diff --git a/chromium/extensions/common/user_script.h b/chromium/extensions/common/user_script.h
new file mode 100644
index 00000000000..76b64d459a7
--- /dev/null
+++ b/chromium/extensions/common/user_script.h
@@ -0,0 +1,260 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_COMMON_USER_SCRIPT_H_
+#define EXTENSIONS_COMMON_USER_SCRIPT_H_
+
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/strings/string_piece.h"
+#include "extensions/common/url_pattern.h"
+#include "extensions/common/url_pattern_set.h"
+#include "url/gurl.h"
+
+class Pickle;
+class PickleIterator;
+
+namespace extensions {
+
+// Represents a user script, either a standalone one, or one that is part of an
+// extension.
+class UserScript {
+ public:
+ // The file extension for standalone user scripts.
+ static const char kFileExtension[];
+
+ // Check if a URL should be treated as a user script and converted to an
+ // extension.
+ static bool IsURLUserScript(const GURL& url, const std::string& mime_type);
+
+ // Get the valid user script schemes for the current process. If
+ // canExecuteScriptEverywhere is true, this will return ALL_SCHEMES.
+ static int ValidUserScriptSchemes(bool canExecuteScriptEverywhere = false);
+
+ // Locations that user scripts can be run inside the document.
+ enum RunLocation {
+ UNDEFINED,
+ DOCUMENT_START, // After the documentElement is created, but before
+ // anything else happens.
+ DOCUMENT_END, // After the entire document is parsed. Same as
+ // DOMContentLoaded.
+ DOCUMENT_IDLE, // Sometime after DOMContentLoaded, as soon as the document
+ // is "idle". Currently this uses the simple heuristic of:
+ // min(DOM_CONTENT_LOADED + TIMEOUT, ONLOAD), but no
+ // particular injection point is guaranteed.
+ RUN_LOCATION_LAST // Leave this as the last item.
+ };
+
+ // Holds actual script file info.
+ class File {
+ public:
+ File(const base::FilePath& extension_root,
+ const base::FilePath& relative_path,
+ const GURL& url);
+ File();
+ ~File();
+
+ const base::FilePath& extension_root() const { return extension_root_; }
+ const base::FilePath& relative_path() const { return relative_path_; }
+
+ const GURL& url() const { return url_; }
+ void set_url(const GURL& url) { url_ = url; }
+
+ // If external_content_ is set returns it as content otherwise it returns
+ // content_
+ const base::StringPiece GetContent() const {
+ if (external_content_.data())
+ return external_content_;
+ else
+ return content_;
+ }
+ void set_external_content(const base::StringPiece& content) {
+ external_content_ = content;
+ }
+ void set_content(const base::StringPiece& content) {
+ content_.assign(content.begin(), content.end());
+ }
+
+ // Serialization support. The content and FilePath members will not be
+ // serialized!
+ void Pickle(::Pickle* pickle) const;
+ void Unpickle(const ::Pickle& pickle, PickleIterator* iter);
+
+ private:
+ // Where the script file lives on the disk. We keep the path split so that
+ // it can be localized at will.
+ base::FilePath extension_root_;
+ base::FilePath relative_path_;
+
+ // The url to this scipt file.
+ GURL url_;
+
+ // The script content. It can be set to either loaded_content_ or
+ // externally allocated string.
+ base::StringPiece external_content_;
+
+ // Set when the content is loaded by LoadContent
+ std::string content_;
+ };
+
+ typedef std::vector<File> FileList;
+
+ // Constructor. Default the run location to document end, which is like
+ // Greasemonkey and probably more useful for typical scripts.
+ UserScript();
+ ~UserScript();
+
+ const std::string& name_space() const { return name_space_; }
+ void set_name_space(const std::string& name_space) {
+ name_space_ = name_space;
+ }
+
+ const std::string& name() const { return name_; }
+ void set_name(const std::string& name) { name_ = name; }
+
+ const std::string& version() const { return version_; }
+ void set_version(const std::string& version) {
+ version_ = version;
+ }
+
+ const std::string& description() const { return description_; }
+ void set_description(const std::string& description) {
+ description_ = description;
+ }
+
+ // The place in the document to run the script.
+ RunLocation run_location() const { return run_location_; }
+ void set_run_location(RunLocation location) { run_location_ = location; }
+
+ // Whether to emulate greasemonkey when running this script.
+ bool emulate_greasemonkey() const { return emulate_greasemonkey_; }
+ void set_emulate_greasemonkey(bool val) { emulate_greasemonkey_ = val; }
+
+ // Whether to match all frames, or only the top one.
+ bool match_all_frames() const { return match_all_frames_; }
+ void set_match_all_frames(bool val) { match_all_frames_ = val; }
+
+ // The globs, if any, that determine which pages this script runs against.
+ // These are only used with "standalone" Greasemonkey-like user scripts.
+ const std::vector<std::string>& globs() const { return globs_; }
+ void add_glob(const std::string& glob) { globs_.push_back(glob); }
+ void clear_globs() { globs_.clear(); }
+ const std::vector<std::string>& exclude_globs() const {
+ return exclude_globs_;
+ }
+ void add_exclude_glob(const std::string& glob) {
+ exclude_globs_.push_back(glob);
+ }
+ void clear_exclude_globs() { exclude_globs_.clear(); }
+
+ // The URLPatterns, if any, that determine which pages this script runs
+ // against.
+ const URLPatternSet& url_patterns() const { return url_set_; }
+ void add_url_pattern(const URLPattern& pattern);
+ const URLPatternSet& exclude_url_patterns() const {
+ return exclude_url_set_;
+ }
+ void add_exclude_url_pattern(const URLPattern& pattern);
+
+ // List of js scripts for this user script
+ FileList& js_scripts() { return js_scripts_; }
+ const FileList& js_scripts() const { return js_scripts_; }
+
+ // List of css scripts for this user script
+ FileList& css_scripts() { return css_scripts_; }
+ const FileList& css_scripts() const { return css_scripts_; }
+
+ const std::string& extension_id() const { return extension_id_; }
+ void set_extension_id(const std::string& id) { extension_id_ = id; }
+
+ bool is_incognito_enabled() const { return incognito_enabled_; }
+ void set_incognito_enabled(bool enabled) { incognito_enabled_ = enabled; }
+
+ bool is_standalone() const { return extension_id_.empty(); }
+
+ // Returns true if the script should be applied to the specified URL, false
+ // otherwise.
+ bool MatchesURL(const GURL& url) const;
+
+ // Serialize the UserScript into a pickle. The content of the scripts and
+ // paths to UserScript::Files will not be serialized!
+ void Pickle(::Pickle* pickle) const;
+
+ // Deserialize the script from a pickle. Note that this always succeeds
+ // because presumably we were the one that pickled it, and we did it
+ // correctly.
+ void Unpickle(const ::Pickle& pickle, PickleIterator* iter);
+
+ private:
+ // Pickle helper functions used to pickle the individual types of components.
+ void PickleGlobs(::Pickle* pickle,
+ const std::vector<std::string>& globs) const;
+ void PickleURLPatternSet(::Pickle* pickle,
+ const URLPatternSet& pattern_list) const;
+ void PickleScripts(::Pickle* pickle, const FileList& scripts) const;
+
+ // Unpickle helper functions used to unpickle individual types of components.
+ void UnpickleGlobs(const ::Pickle& pickle, PickleIterator* iter,
+ std::vector<std::string>* globs);
+ void UnpickleURLPatternSet(const ::Pickle& pickle, PickleIterator* iter,
+ URLPatternSet* pattern_list);
+ void UnpickleScripts(const ::Pickle& pickle, PickleIterator* iter,
+ FileList* scripts);
+
+ // The location to run the script inside the document.
+ RunLocation run_location_;
+
+ // The namespace of the script. This is used by Greasemonkey in the same way
+ // as XML namespaces. Only used when parsing Greasemonkey-style scripts.
+ std::string name_space_;
+
+ // The script's name. Only used when parsing Greasemonkey-style scripts.
+ std::string name_;
+
+ // A longer description. Only used when parsing Greasemonkey-style scripts.
+ std::string description_;
+
+ // A version number of the script. Only used when parsing Greasemonkey-style
+ // scripts.
+ std::string version_;
+
+ // Greasemonkey-style globs that determine pages to inject the script into.
+ // These are only used with standalone scripts.
+ std::vector<std::string> globs_;
+ std::vector<std::string> exclude_globs_;
+
+ // URLPatterns that determine pages to inject the script into. These are
+ // only used with scripts that are part of extensions.
+ URLPatternSet url_set_;
+ URLPatternSet exclude_url_set_;
+
+ // List of js scripts defined in content_scripts
+ FileList js_scripts_;
+
+ // List of css scripts defined in content_scripts
+ FileList css_scripts_;
+
+ // The ID of the extension this script is a part of, if any. Can be empty if
+ // the script is a "standlone" user script.
+ std::string extension_id_;
+
+ // Whether we should try to emulate Greasemonkey's APIs when running this
+ // script.
+ bool emulate_greasemonkey_;
+
+ // Whether the user script should run in all frames, or only just the top one.
+ // Defaults to false.
+ bool match_all_frames_;
+
+ // True if the script should be injected into an incognito tab.
+ bool incognito_enabled_;
+};
+
+typedef std::vector<UserScript> UserScriptList;
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_USER_SCRIPT_H_
diff --git a/chromium/extensions/common/user_script_unittest.cc b/chromium/extensions/common/user_script_unittest.cc
new file mode 100644
index 00000000000..ffec4f0e1db
--- /dev/null
+++ b/chromium/extensions/common/user_script_unittest.cc
@@ -0,0 +1,220 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/file_path.h"
+#include "base/pickle.h"
+#include "extensions/common/user_script.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace extensions {
+
+static const int kAllSchemes =
+ URLPattern::SCHEME_HTTP |
+ URLPattern::SCHEME_HTTPS |
+ URLPattern::SCHEME_FILE |
+ URLPattern::SCHEME_FTP |
+ URLPattern::SCHEME_CHROMEUI;
+
+TEST(ExtensionUserScriptTest, Glob_HostString) {
+ UserScript script;
+ script.add_glob("*mail.google.com*");
+ script.add_glob("*mail.yahoo.com*");
+ script.add_glob("*mail.msn.com*");
+ EXPECT_TRUE(script.MatchesURL(GURL("http://mail.google.com")));
+ EXPECT_TRUE(script.MatchesURL(GURL("http://mail.google.com/foo")));
+ EXPECT_TRUE(script.MatchesURL(GURL("https://mail.google.com/foo")));
+ EXPECT_TRUE(script.MatchesURL(GURL("ftp://mail.google.com/foo")));
+ EXPECT_TRUE(script.MatchesURL(GURL("http://woo.mail.google.com/foo")));
+ EXPECT_TRUE(script.MatchesURL(GURL("http://mail.yahoo.com/bar")));
+ EXPECT_TRUE(script.MatchesURL(GURL("http://mail.msn.com/baz")));
+ EXPECT_FALSE(script.MatchesURL(GURL("http://www.hotmail.com")));
+
+ script.add_exclude_glob("*foo*");
+ EXPECT_TRUE(script.MatchesURL(GURL("http://mail.google.com")));
+ EXPECT_FALSE(script.MatchesURL(GURL("http://mail.google.com/foo")));
+}
+
+TEST(ExtensionUserScriptTest, Glob_TrailingSlash) {
+ UserScript script;
+ script.add_glob("*mail.google.com/");
+ // GURL normalizes the URL to have a trailing "/"
+ EXPECT_TRUE(script.MatchesURL(GURL("http://mail.google.com")));
+ EXPECT_TRUE(script.MatchesURL(GURL("http://mail.google.com/")));
+ EXPECT_FALSE(script.MatchesURL(GURL("http://mail.google.com/foo")));
+}
+
+TEST(ExtensionUserScriptTest, Glob_TrailingSlashStar) {
+ UserScript script;
+ script.add_glob("http://mail.google.com/*");
+ // GURL normalizes the URL to have a trailing "/"
+ EXPECT_TRUE(script.MatchesURL(GURL("http://mail.google.com")));
+ EXPECT_TRUE(script.MatchesURL(GURL("http://mail.google.com/foo")));
+ EXPECT_FALSE(script.MatchesURL(GURL("https://mail.google.com/foo")));
+}
+
+TEST(ExtensionUserScriptTest, Glob_Star) {
+ UserScript script;
+ script.add_glob("*");
+ EXPECT_TRUE(script.MatchesURL(GURL("http://foo.com/bar")));
+ EXPECT_TRUE(script.MatchesURL(GURL("http://hot.com/dog")));
+ EXPECT_TRUE(script.MatchesURL(GURL("https://hot.com/dog")));
+ EXPECT_TRUE(script.MatchesURL(GURL("file:///foo/bar")));
+ EXPECT_TRUE(script.MatchesURL(GURL("file://localhost/foo/bar")));
+}
+
+TEST(ExtensionUserScriptTest, Glob_StringAnywhere) {
+ UserScript script;
+ script.add_glob("*foo*");
+ EXPECT_TRUE(script.MatchesURL(GURL("http://foo.com/bar")));
+ EXPECT_TRUE(script.MatchesURL(GURL("http://baz.org/foo/bar")));
+ EXPECT_FALSE(script.MatchesURL(GURL("http://baz.org")));
+}
+
+TEST(ExtensionUserScriptTest, UrlPattern) {
+ URLPattern pattern(kAllSchemes);
+ ASSERT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("http://*/foo*"));
+
+ UserScript script;
+ script.add_url_pattern(pattern);
+ EXPECT_TRUE(script.MatchesURL(GURL("http://monkey.com/foobar")));
+ EXPECT_FALSE(script.MatchesURL(GURL("http://monkey.com/hotdog")));
+
+ // NOTE: URLPattern is tested more extensively in url_pattern_unittest.cc.
+}
+
+TEST(ExtensionUserScriptTest, ExcludeUrlPattern) {
+ UserScript script;
+
+ URLPattern pattern(kAllSchemes);
+ ASSERT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("http://*.nytimes.com/*"));
+ script.add_url_pattern(pattern);
+
+ URLPattern exclude(kAllSchemes);
+ ASSERT_EQ(URLPattern::PARSE_SUCCESS, exclude.Parse("*://*/*business*"));
+ script.add_exclude_url_pattern(exclude);
+
+ EXPECT_TRUE(script.MatchesURL(GURL("http://www.nytimes.com/health")));
+ EXPECT_FALSE(script.MatchesURL(GURL("http://www.nytimes.com/business")));
+ EXPECT_TRUE(script.MatchesURL(GURL("http://business.nytimes.com")));
+}
+
+TEST(ExtensionUserScriptTest, UrlPatternAndIncludeGlobs) {
+ UserScript script;
+
+ URLPattern pattern(kAllSchemes);
+ ASSERT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("http://*.nytimes.com/*"));
+ script.add_url_pattern(pattern);
+
+ script.add_glob("*nytimes.com/???s/*");
+
+ EXPECT_TRUE(script.MatchesURL(GURL("http://www.nytimes.com/arts/1.html")));
+ EXPECT_TRUE(script.MatchesURL(GURL("http://www.nytimes.com/jobs/1.html")));
+ EXPECT_FALSE(script.MatchesURL(GURL("http://www.nytimes.com/sports/1.html")));
+}
+
+TEST(ExtensionUserScriptTest, UrlPatternAndExcludeGlobs) {
+ UserScript script;
+
+ URLPattern pattern(kAllSchemes);
+ ASSERT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("http://*.nytimes.com/*"));
+ script.add_url_pattern(pattern);
+
+ script.add_exclude_glob("*science*");
+
+ EXPECT_TRUE(script.MatchesURL(GURL("http://www.nytimes.com")));
+ EXPECT_FALSE(script.MatchesURL(GURL("http://science.nytimes.com")));
+ EXPECT_FALSE(script.MatchesURL(GURL("http://www.nytimes.com/science")));
+}
+
+TEST(ExtensionUserScriptTest, UrlPatternGlobInteraction) {
+ // If there are both, match intersection(union(globs), union(urlpatterns)).
+ UserScript script;
+
+ URLPattern pattern(kAllSchemes);
+ ASSERT_EQ(URLPattern::PARSE_SUCCESS,pattern.Parse("http://www.google.com/*"));
+ script.add_url_pattern(pattern);
+
+ script.add_glob("*bar*");
+
+ // No match, because it doesn't match the glob.
+ EXPECT_FALSE(script.MatchesURL(GURL("http://www.google.com/foo")));
+
+ script.add_exclude_glob("*baz*");
+
+ // No match, because it matches the exclude glob.
+ EXPECT_FALSE(script.MatchesURL(GURL("http://www.google.com/baz")));
+
+ // Match, because it matches the glob, doesn't match the exclude glob.
+ EXPECT_TRUE(script.MatchesURL(GURL("http://www.google.com/bar")));
+
+ // Try with just a single exclude glob.
+ script.clear_globs();
+ EXPECT_TRUE(script.MatchesURL(GURL("http://www.google.com/foo")));
+
+ // Try with no globs or exclude globs.
+ script.clear_exclude_globs();
+ EXPECT_TRUE(script.MatchesURL(GURL("http://www.google.com/foo")));
+}
+
+TEST(ExtensionUserScriptTest, Pickle) {
+ URLPattern pattern1(kAllSchemes);
+ URLPattern pattern2(kAllSchemes);
+ URLPattern exclude1(kAllSchemes);
+ URLPattern exclude2(kAllSchemes);
+ ASSERT_EQ(URLPattern::PARSE_SUCCESS, pattern1.Parse("http://*/foo*"));
+ ASSERT_EQ(URLPattern::PARSE_SUCCESS, pattern2.Parse("http://bar/baz*"));
+ ASSERT_EQ(URLPattern::PARSE_SUCCESS, exclude1.Parse("*://*/*bar"));
+ ASSERT_EQ(URLPattern::PARSE_SUCCESS, exclude2.Parse("https://*/*"));
+
+ UserScript script1;
+ script1.js_scripts().push_back(UserScript::File(
+ base::FilePath(FILE_PATH_LITERAL("c:\\foo\\")),
+ base::FilePath(FILE_PATH_LITERAL("foo.user.js")),
+ GURL("chrome-extension://abc/foo.user.js")));
+ script1.css_scripts().push_back(UserScript::File(
+ base::FilePath(FILE_PATH_LITERAL("c:\\foo\\")),
+ base::FilePath(FILE_PATH_LITERAL("foo.user.css")),
+ GURL("chrome-extension://abc/foo.user.css")));
+ script1.css_scripts().push_back(UserScript::File(
+ base::FilePath(FILE_PATH_LITERAL("c:\\foo\\")),
+ base::FilePath(FILE_PATH_LITERAL("foo2.user.css")),
+ GURL("chrome-extension://abc/foo2.user.css")));
+ script1.set_run_location(UserScript::DOCUMENT_START);
+
+ script1.add_url_pattern(pattern1);
+ script1.add_url_pattern(pattern2);
+ script1.add_exclude_url_pattern(exclude1);
+ script1.add_exclude_url_pattern(exclude2);
+
+ Pickle pickle;
+ script1.Pickle(&pickle);
+
+ PickleIterator iter(pickle);
+ UserScript script2;
+ script2.Unpickle(pickle, &iter);
+
+ EXPECT_EQ(1U, script2.js_scripts().size());
+ EXPECT_EQ(script1.js_scripts()[0].url(), script2.js_scripts()[0].url());
+
+ EXPECT_EQ(2U, script2.css_scripts().size());
+ for (size_t i = 0; i < script2.js_scripts().size(); ++i) {
+ EXPECT_EQ(script1.css_scripts()[i].url(), script2.css_scripts()[i].url());
+ }
+
+ ASSERT_EQ(script1.globs().size(), script2.globs().size());
+ for (size_t i = 0; i < script1.globs().size(); ++i) {
+ EXPECT_EQ(script1.globs()[i], script2.globs()[i]);
+ }
+
+ ASSERT_EQ(script1.url_patterns(), script2.url_patterns());
+ ASSERT_EQ(script1.exclude_url_patterns(), script2.exclude_url_patterns());
+}
+
+TEST(ExtensionUserScriptTest, Defaults) {
+ UserScript script;
+ ASSERT_EQ(UserScript::DOCUMENT_IDLE, script.run_location());
+}
+
+} // namespace extensions
diff --git a/chromium/extensions/common/view_type.cc b/chromium/extensions/common/view_type.cc
new file mode 100644
index 00000000000..679532a5acd
--- /dev/null
+++ b/chromium/extensions/common/view_type.cc
@@ -0,0 +1,19 @@
+// 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.
+
+#include "extensions/common/view_type.h"
+
+namespace extensions {
+
+const char kViewTypeTabContents[] = "TAB";
+const char kViewTypeBackgroundPage[] = "BACKGROUND";
+const char kViewTypePopup[] = "POPUP";
+const char kViewTypePanel[] = "PANEL";
+const char kViewTypeInfobar[] = "INFOBAR";
+const char kViewTypeNotification[] = "NOTIFICATION";
+const char kViewTypeExtensionDialog[] = "EXTENSION_DIALOG";
+const char kViewTypeAppShell[] = "SHELL";
+const char kViewTypeAll[] = "ALL";
+
+} // namespace extensions
diff --git a/chromium/extensions/common/view_type.h b/chromium/extensions/common/view_type.h
new file mode 100644
index 00000000000..6030e6ca92c
--- /dev/null
+++ b/chromium/extensions/common/view_type.h
@@ -0,0 +1,45 @@
+// 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.
+
+#ifndef EXTENSIONS_COMMON_VIEW_TYPE_H_
+#define EXTENSIONS_COMMON_VIEW_TYPE_H_
+
+namespace extensions {
+
+// Icky RTTI used by a few systems to distinguish the host type of a given
+// WebContents.
+//
+// TODO(aa): Remove this and teach those systems to keep track of their own
+// data.
+enum ViewType {
+ VIEW_TYPE_INVALID,
+ VIEW_TYPE_APP_SHELL,
+ VIEW_TYPE_BACKGROUND_CONTENTS,
+ VIEW_TYPE_EXTENSION_BACKGROUND_PAGE,
+ VIEW_TYPE_EXTENSION_DIALOG,
+ VIEW_TYPE_EXTENSION_INFOBAR,
+ VIEW_TYPE_EXTENSION_POPUP,
+ // TODO(jam): remove this once http://crbug.com/137297 is fixed and HTML5
+ // notifications don't use WebContents.
+ VIEW_TYPE_NOTIFICATION,
+ VIEW_TYPE_PANEL,
+ VIEW_TYPE_TAB_CONTENTS,
+ VIEW_TYPE_VIRTUAL_KEYBOARD,
+};
+
+// Constant strings corresponding to the Type enumeration values. Used
+// when converting JS arguments.
+extern const char kViewTypeAll[];
+extern const char kViewTypeAppShell[];
+extern const char kViewTypeBackgroundPage[];
+extern const char kViewTypeExtensionDialog[];
+extern const char kViewTypeInfobar[];
+extern const char kViewTypeNotification[];
+extern const char kViewTypePanel[];
+extern const char kViewTypePopup[];
+extern const char kViewTypeTabContents[];
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_VIEW_TYPE_H_