diff options
author | Torne (Richard Coles) <torne@google.com> | 2014-02-21 12:16:55 +0000 |
---|---|---|
committer | Torne (Richard Coles) <torne@google.com> | 2014-02-21 12:16:55 +0000 |
commit | 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7 (patch) | |
tree | 5d4ae202b870bd86673f596f0d424bc4b3e55ebe /extensions | |
parent | e862bac9c33104a29d98631d62668ae7b6676510 (diff) | |
download | chromium_org-5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7.tar.gz |
Merge from Chromium at DEPS revision 251904
This commit was generated by merge_to_master.py.
Change-Id: I1f9543259d7d2a57d81aa41a1b84f85837439d21
Diffstat (limited to 'extensions')
168 files changed, 7999 insertions, 4380 deletions
diff --git a/extensions/DEPS b/extensions/DEPS index f286a2e609..898a76b883 100644 --- a/extensions/DEPS +++ b/extensions/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+components/url_matcher", "+content/public/common", "+crypto", "+testing", diff --git a/extensions/OWNERS b/extensions/OWNERS index 3393de66e4..3dced1e92b 100644 --- a/extensions/OWNERS +++ b/extensions/OWNERS @@ -4,7 +4,6 @@ koz@chromium.org yoz@chromium.org asargent@chromium.org scheib@chromium.org -mpcomplete@chromium.org jyasskin@chromium.org kalman@chromium.org mek@chromium.org diff --git a/extensions/browser/DEPS b/extensions/browser/DEPS index 35e31587e4..f1751ccf32 100644 --- a/extensions/browser/DEPS +++ b/extensions/browser/DEPS @@ -1,4 +1,6 @@ include_rules = [ + "+components/browser_context_keyed_service", + "+components/user_prefs", "+content/public/browser", "+sync", @@ -9,17 +11,16 @@ include_rules = [ # # TODO(jamescook): Remove these. http://crbug.com/162530 "+chrome/browser/chrome_notification_types.h", + "+chrome/browser/extensions/api/content_settings/content_settings_store.h", + "+chrome/browser/extensions/api/preference/preference_api.h", "+chrome/browser/extensions/api/runtime/runtime_api.h", "+chrome/browser/extensions/extension_function_dispatcher.h", "+chrome/browser/extensions/extension_function_histogram_value.h", "+chrome/browser/extensions/extension_host.h", - "+chrome/browser/extensions/extension_prefs.h", "+chrome/browser/extensions/extension_service.h", - "+chrome/browser/extensions/extension_system.h", - "+chrome/browser/extensions/extension_util.h", "+chrome/browser/renderer_host/chrome_render_message_filter.h", "+chrome/common/extensions/extension_messages.h", - "+chrome/common/extensions/extension_set.h", + "+chrome/common/extensions/features/feature_channel.h", "+grit/generated_resources.h", ] diff --git a/extensions/browser/admin_policy.cc b/extensions/browser/admin_policy.cc index 903c4e0956..cb948879aa 100644 --- a/extensions/browser/admin_policy.cc +++ b/extensions/browser/admin_policy.cc @@ -13,10 +13,10 @@ namespace { bool ManagementPolicyImpl(const extensions::Extension* extension, - string16* error, + base::string16* error, bool modifiable_value) { bool modifiable = - extension->location() != extensions::Manifest::COMPONENT && + !extensions::Manifest::IsComponentLocation(extension->location()) && !extensions::Manifest::IsPolicyLocation(extension->location()); // Some callers equate "no restriction" to true, others to false. if (modifiable) @@ -25,17 +25,18 @@ bool ManagementPolicyImpl(const extensions::Extension* extension, if (error) { *error = l10n_util::GetStringFUTF16( IDS_EXTENSION_CANT_MODIFY_POLICY_REQUIRED, - UTF8ToUTF16(extension->name())); + base::UTF8ToUTF16(extension->name())); } return !modifiable_value; } -bool ReturnLoadError(const extensions::Extension* extension, string16* error) { +bool ReturnLoadError(const extensions::Extension* extension, + base::string16* error) { if (error) { *error = l10n_util::GetStringFUTF16( IDS_EXTENSION_CANT_INSTALL_POLICY_BLOCKED, - UTF8ToUTF16(extension->name()), - UTF8ToUTF16(extension->id())); + base::UTF8ToUTF16(extension->name()), + base::UTF8ToUTF16(extension->id())); } return false; } @@ -55,7 +56,7 @@ bool UserMayLoad(const base::ListValue* blacklist, const base::DictionaryValue* forcelist, const base::ListValue* allowed_types, const Extension* extension, - string16* error) { + base::string16* error) { // Component extensions are always allowed. if (extension->location() == Manifest::COMPONENT) return true; @@ -109,11 +110,11 @@ bool UserMayLoad(const base::ListValue* blacklist, return true; } -bool UserMayModifySettings(const Extension* extension, string16* error) { +bool UserMayModifySettings(const Extension* extension, base::string16* error) { return ManagementPolicyImpl(extension, error, true); } -bool MustRemainEnabled(const Extension* extension, string16* error) { +bool MustRemainEnabled(const Extension* extension, base::string16* error) { return ManagementPolicyImpl(extension, error, false); } diff --git a/extensions/browser/admin_policy.h b/extensions/browser/admin_policy.h index ea32ee78d5..6091bf8e78 100644 --- a/extensions/browser/admin_policy.h +++ b/extensions/browser/admin_policy.h @@ -26,15 +26,15 @@ bool UserMayLoad(const base::ListValue* blacklist, const base::DictionaryValue* forcelist, const base::ListValue* allowed_types, const Extension* extension, - string16* error); + base::string16* error); // Returns false if the extension is required to remain running. In practice // this enforces the admin policy forcelist. -bool UserMayModifySettings(const Extension* extension, string16* error); +bool UserMayModifySettings(const Extension* extension, base::string16* error); // Returns false if the extension is required to remain running. In practice // this enforces the admin policy forcelist. -bool MustRemainEnabled(const Extension* extension, string16* error); +bool MustRemainEnabled(const Extension* extension, base::string16* error); } // namespace } // namespace diff --git a/extensions/browser/admin_policy_unittest.cc b/extensions/browser/admin_policy_unittest.cc index f85e3ef5a6..13b3f03de7 100644 --- a/extensions/browser/admin_policy_unittest.cc +++ b/extensions/browser/admin_policy_unittest.cc @@ -64,7 +64,7 @@ TEST_F(ExtensionAdminPolicyTest, BlacklistedByDefault) { TEST_F(ExtensionAdminPolicyTest, UserMayLoadRequired) { CreateExtension(Manifest::COMPONENT); EXPECT_TRUE(ap::UserMayLoad(NULL, NULL, NULL, NULL, extension_.get(), NULL)); - string16 error; + base::string16 error; EXPECT_TRUE(ap::UserMayLoad(NULL, NULL, NULL, NULL, extension_.get(), &error)); EXPECT_TRUE(error.empty()); @@ -87,7 +87,7 @@ TEST_F(ExtensionAdminPolicyTest, UserMayLoadNoBlacklist) { base::ListValue blacklist; EXPECT_TRUE(ap::UserMayLoad(&blacklist, NULL, NULL, NULL, extension_.get(), NULL)); - string16 error; + base::string16 error; EXPECT_TRUE(ap::UserMayLoad(&blacklist, NULL, NULL, NULL, extension_.get(), &error)); EXPECT_TRUE(error.empty()); @@ -106,7 +106,7 @@ TEST_F(ExtensionAdminPolicyTest, UserMayLoadWhitelisted) { blacklist.Append(new base::StringValue(extension_->id())); EXPECT_TRUE(ap::UserMayLoad(NULL, &whitelist, NULL, NULL, extension_.get(), NULL)); - string16 error; + base::string16 error; EXPECT_TRUE(ap::UserMayLoad(NULL, &whitelist, NULL, NULL, extension_.get(), &error)); EXPECT_TRUE(error.empty()); @@ -121,7 +121,7 @@ TEST_F(ExtensionAdminPolicyTest, UserMayLoadBlacklisted) { blacklist.Append(new base::StringValue("*")); EXPECT_FALSE(ap::UserMayLoad(&blacklist, NULL, NULL, NULL, extension_.get(), NULL)); - string16 error; + base::string16 error; EXPECT_FALSE(ap::UserMayLoad(&blacklist, NULL, NULL, NULL, extension_.get(), &error)); EXPECT_FALSE(error.empty()); @@ -170,7 +170,7 @@ TEST_F(ExtensionAdminPolicyTest, UserMayLoadAllowedTypes) { TEST_F(ExtensionAdminPolicyTest, UserMayModifySettings) { CreateExtension(Manifest::INTERNAL); EXPECT_TRUE(ap::UserMayModifySettings(extension_.get(), NULL)); - string16 error; + base::string16 error; EXPECT_TRUE(ap::UserMayModifySettings(extension_.get(), &error)); EXPECT_TRUE(error.empty()); @@ -184,7 +184,7 @@ TEST_F(ExtensionAdminPolicyTest, UserMayModifySettings) { TEST_F(ExtensionAdminPolicyTest, MustRemainEnabled) { CreateExtension(Manifest::EXTERNAL_POLICY_DOWNLOAD); EXPECT_TRUE(ap::MustRemainEnabled(extension_.get(), NULL)); - string16 error; + base::string16 error; EXPECT_TRUE(ap::MustRemainEnabled(extension_.get(), &error)); EXPECT_FALSE(error.empty()); diff --git a/extensions/browser/api_activity_monitor.h b/extensions/browser/api_activity_monitor.h new file mode 100644 index 0000000000..b8f0cc32c4 --- /dev/null +++ b/extensions/browser/api_activity_monitor.h @@ -0,0 +1,39 @@ +// Copyright 2014 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_API_ACTIVITY_MONITOR_H_ +#define EXTENSIONS_BROWSER_API_ACTIVITY_MONITOR_H_ + +#include <string> + +#include "base/memory/scoped_ptr.h" + +namespace base { +class ListValue; +} + +namespace extensions { + +// ApiActivityMonitor is used to monitor extension API event dispatch and API +// function calls. An embedder can use this interface to log low-level extension +// activity. +class ApiActivityMonitor { + public: + // Called when an API event is dispatched to an extension. + virtual void OnApiEventDispatched(const std::string& extension_id, + const std::string& event_name, + scoped_ptr<base::ListValue> event_args) = 0; + + // Called when an extension calls an API function. + virtual void OnApiFunctionCalled(const std::string& extension_id, + const std::string& api_name, + scoped_ptr<base::ListValue> args) = 0; + + protected: + virtual ~ApiActivityMonitor() {} +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_API_ACTIVITY_MONITOR_H_ diff --git a/extensions/browser/app_sorting.h b/extensions/browser/app_sorting.h index 967fb46eea..ede93f134d 100644 --- a/extensions/browser/app_sorting.h +++ b/extensions/browser/app_sorting.h @@ -1,4 +1,4 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// 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. @@ -8,6 +8,7 @@ #include <string> #include "base/basictypes.h" +#include "extensions/browser/extension_prefs.h" #include "extensions/common/extension.h" #include "sync/api/string_ordinal.h" diff --git a/extensions/browser/blacklist_state.h b/extensions/browser/blacklist_state.h new file mode 100644 index 0000000000..d7f16e2315 --- /dev/null +++ b/extensions/browser/blacklist_state.h @@ -0,0 +1,24 @@ +// Copyright 2014 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_BLACKLIST_STATE_H_ +#define EXTENSIONS_BROWSER_BLACKLIST_STATE_H_ + +namespace extensions { + +// The numeric values here match the values of the respective enum in +// ClientCRXListInfoResponse proto. +enum BlacklistState { + NOT_BLACKLISTED = 0, + BLACKLISTED_MALWARE = 1, + BLACKLISTED_SECURITY_VULNERABILITY = 2, + BLACKLISTED_CWS_POLICY_VIOLATION = 3, + BLACKLISTED_POTENTIALLY_UNWANTED = 4, + BLACKLISTED_UNKNOWN // Used when we couldn't connect to server, + // e.g. when offline. +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_BLACKLIST_STATE_H_ diff --git a/extensions/browser/error_map.cc b/extensions/browser/error_map.cc new file mode 100644 index 0000000000..dfcef9fd05 --- /dev/null +++ b/extensions/browser/error_map.cc @@ -0,0 +1,138 @@ +// Copyright 2014 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/error_map.h" + +#include "base/lazy_instance.h" +#include "base/stl_util.h" +#include "extensions/common/extension.h" + +namespace extensions { +namespace { + +// The maximum number of errors to be stored per extension. +const size_t kMaxErrorsPerExtension = 100; + +base::LazyInstance<ErrorList> g_empty_error_list = LAZY_INSTANCE_INITIALIZER; + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// +// ErrorMap::ExtensionEntry +ErrorMap::ExtensionEntry::ExtensionEntry() { +} + +ErrorMap::ExtensionEntry::~ExtensionEntry() { + DeleteAllErrors(); +} + +void ErrorMap::ExtensionEntry::DeleteAllErrors() { + STLDeleteContainerPointers(list_.begin(), list_.end()); + list_.clear(); +} + +void ErrorMap::ExtensionEntry::DeleteIncognitoErrors() { + ErrorList::iterator iter = list_.begin(); + while (iter != list_.end()) { + if ((*iter)->from_incognito()) { + delete *iter; + iter = list_.erase(iter); + } else { + ++iter; + } + } +} + +void ErrorMap::ExtensionEntry::DeleteErrorsOfType(ExtensionError::Type type) { + ErrorList::iterator iter = list_.begin(); + while (iter != list_.end()) { + if ((*iter)->type() == type) { + delete *iter; + iter = list_.erase(iter); + } else { + ++iter; + } + } +} + +const ExtensionError* ErrorMap::ExtensionEntry::AddError( + scoped_ptr<ExtensionError> error) { + for (ErrorList::iterator iter = list_.begin(); iter != list_.end(); ++iter) { + // If we find a duplicate error, remove the old error and add the new one, + // incrementing the occurrence count of the error. We use the new error + // for runtime errors, so we can link to the latest context, inspectable + // view, etc. + if (error->IsEqual(*iter)) { + error->set_occurrences((*iter)->occurrences() + 1); + delete *iter; + list_.erase(iter); + break; + } + } + + // If there are too many errors for an extension already, limit ourselves to + // the most recent ones. + if (list_.size() >= kMaxErrorsPerExtension) { + delete list_.front(); + list_.pop_front(); + } + + list_.push_back(error.release()); + return list_.back(); +} + +//////////////////////////////////////////////////////////////////////////////// +// ErrorMap +ErrorMap::ErrorMap() { +} + +ErrorMap::~ErrorMap() { + RemoveAllErrors(); +} + +const ErrorList& ErrorMap::GetErrorsForExtension( + const std::string& extension_id) const { + EntryMap::const_iterator iter = map_.find(extension_id); + return iter != map_.end() ? *iter->second->list() : g_empty_error_list.Get(); +} + +const ExtensionError* ErrorMap::AddError(scoped_ptr<ExtensionError> error) { + EntryMap::iterator iter = map_.find(error->extension_id()); + if (iter == map_.end()) { + iter = map_.insert(std::pair<std::string, ExtensionEntry*>( + error->extension_id(), new ExtensionEntry)).first; + } + return iter->second->AddError(error.Pass()); +} + +void ErrorMap::Remove(const std::string& extension_id) { + EntryMap::iterator iter = map_.find(extension_id); + if (iter == map_.end()) + return; + + delete iter->second; + map_.erase(iter); +} + +void ErrorMap::RemoveErrorsForExtensionOfType(const std::string& extension_id, + ExtensionError::Type type) { + EntryMap::iterator iter = map_.find(extension_id); + if (iter != map_.end()) + iter->second->DeleteErrorsOfType(type); +} + +void ErrorMap::RemoveIncognitoErrors() { + for (EntryMap::iterator iter = map_.begin(); iter != map_.end(); ++iter) + iter->second->DeleteIncognitoErrors(); +} + +void ErrorMap::RemoveAllErrors() { + for (EntryMap::iterator iter = map_.begin(); iter != map_.end(); ++iter) { + iter->second->DeleteAllErrors(); + delete iter->second; + } + map_.clear(); +} + +} // namespace extensions diff --git a/extensions/browser/error_map.h b/extensions/browser/error_map.h new file mode 100644 index 0000000000..2dcff220cb --- /dev/null +++ b/extensions/browser/error_map.h @@ -0,0 +1,84 @@ +// Copyright 2014 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_ERROR_MAP_H_ +#define EXTENSIONS_BROWSER_ERROR_MAP_H_ + +#include <deque> +#include <map> +#include <string> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "extensions/browser/extension_error.h" + +namespace extensions { + +typedef std::deque<const ExtensionError*> ErrorList; + +// An ErrorMap is responsible for storing Extension-related errors, keyed by +// Extension ID. The errors are owned by the ErrorMap, and are deleted upon +// destruction. +class ErrorMap { + public: + explicit ErrorMap(); + ~ErrorMap(); + + // Return the list of all errors associated with the given extension. + const ErrorList& GetErrorsForExtension(const std::string& extension_id) const; + + // Add the |error| to the ErrorMap. + const ExtensionError* AddError(scoped_ptr<ExtensionError> error); + + // Remove an extension from the ErrorMap, deleting all associated errors. + void Remove(const std::string& extension_id); + // Remove all errors of a given type for an extension. + void RemoveErrorsForExtensionOfType(const std::string& extension_id, + ExtensionError::Type type); + // Remove all incognito errors for all extensions. + void RemoveIncognitoErrors(); + // Remove all errors for all extensions, and clear the map. + void RemoveAllErrors(); + + size_t size() const { return map_.size(); } + + private: + // An Entry is created for each Extension ID, and stores the errors related to + // that Extension. + class ExtensionEntry { + public: + explicit ExtensionEntry(); + ~ExtensionEntry(); + + // Delete all errors associated with this extension. + void DeleteAllErrors(); + // Delete all incognito errors associated with this extension. + void DeleteIncognitoErrors(); + // Delete all errors of the given |type| associated with this extension. + void DeleteErrorsOfType(ExtensionError::Type type); + + // Add the error to the list, and return a weak reference. + const ExtensionError* AddError(scoped_ptr<ExtensionError> error); + + const ErrorList* list() const { return &list_; } + + private: + // The list of all errors associated with the extension. The errors are + // owned by the Entry (in turn owned by the ErrorMap) and are deleted upon + // destruction. + ErrorList list_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionEntry); + }; + typedef std::map<std::string, ExtensionEntry*> EntryMap; + + // The mapping between Extension IDs and their corresponding Entries. + EntryMap map_; + + DISALLOW_COPY_AND_ASSIGN(ErrorMap); +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_ERROR_MAP_H_ diff --git a/extensions/browser/error_map_unittest.cc b/extensions/browser/error_map_unittest.cc new file mode 100644 index 0000000000..8330c6afe7 --- /dev/null +++ b/extensions/browser/error_map_unittest.cc @@ -0,0 +1,147 @@ +// Copyright 2014 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/error_map.h" + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_number_conversions.h" +#include "extensions/browser/extension_error.h" +#include "extensions/browser/extension_error_test_util.h" +#include "extensions/common/constants.h" +#include "extensions/common/id_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace extensions { + +using error_test_util::CreateNewRuntimeError; + +class ErrorMapUnitTest : public testing::Test { + public: + ErrorMapUnitTest() { } + virtual ~ErrorMapUnitTest() { } + + virtual void SetUp() OVERRIDE { + testing::Test::SetUp(); + } + + protected: + ErrorMap errors_; +}; + +// Test adding errors, and removing them by reference, by incognito status, +// and in bulk. +TEST_F(ErrorMapUnitTest, AddAndRemoveErrors) { + ASSERT_EQ(0u, errors_.size()); + + const size_t kNumTotalErrors = 6; + const size_t kNumNonIncognitoErrors = 3; + const std::string kId = id_util::GenerateId("id"); + // Populate with both incognito and non-incognito errors (evenly distributed). + for (size_t i = 0; i < kNumTotalErrors; ++i) { + ASSERT_TRUE(errors_.AddError( + CreateNewRuntimeError(kId, base::UintToString(i), i % 2 == 0))); + } + + // There should only be one entry in the map, since errors are stored in lists + // keyed by extension id. + ASSERT_EQ(1u, errors_.size()); + + ASSERT_EQ(kNumTotalErrors, errors_.GetErrorsForExtension(kId).size()); + + // Remove the incognito errors; three errors should remain, and all should + // be from non-incognito contexts. + errors_.RemoveIncognitoErrors(); + const ErrorList& list = errors_.GetErrorsForExtension(kId); + ASSERT_EQ(kNumNonIncognitoErrors, list.size()); + for (size_t i = 0; i < list.size(); ++i) + ASSERT_FALSE(list[i]->from_incognito()); + + // Add another error for a different extension id. + const std::string kSecondId = id_util::GenerateId("id2"); + ASSERT_TRUE(errors_.AddError(CreateNewRuntimeError(kSecondId, "foo"))); + + // There should be two entries now, one for each id, and there should be one + // error for the second extension. + ASSERT_EQ(2u, errors_.size()); + ASSERT_EQ(1u, errors_.GetErrorsForExtension(kSecondId).size()); + + // Remove all errors for the second id. + errors_.Remove(kSecondId); + ASSERT_EQ(1u, errors_.size()); + ASSERT_EQ(0u, errors_.GetErrorsForExtension(kSecondId).size()); + // First extension should be unaffected. + ASSERT_EQ(kNumNonIncognitoErrors, + errors_.GetErrorsForExtension(kId).size()); + + // Remove remaining errors. + errors_.RemoveAllErrors(); + ASSERT_EQ(0u, errors_.size()); + ASSERT_EQ(0u, errors_.GetErrorsForExtension(kId).size()); +} + +// Test that if we add enough errors, only the most recent +// kMaxErrorsPerExtension are kept. +TEST_F(ErrorMapUnitTest, ExcessiveErrorsGetCropped) { + ASSERT_EQ(0u, errors_.size()); + + // This constant matches one of the same name in error_console.cc. + const size_t kMaxErrorsPerExtension = 100; + const size_t kNumExtraErrors = 5; + const std::string kId = id_util::GenerateId("id"); + + // Add new errors, with each error's message set to its number. + for (size_t i = 0; i < kMaxErrorsPerExtension + kNumExtraErrors; ++i) { + ASSERT_TRUE(errors_.AddError( + CreateNewRuntimeError(kId, base::UintToString(i)))); + } + + ASSERT_EQ(1u, errors_.size()); + + const ErrorList& list = errors_.GetErrorsForExtension(kId); + ASSERT_EQ(kMaxErrorsPerExtension, list.size()); + + // We should have popped off errors in the order they arrived, so the + // first stored error should be the 6th reported (zero-based)... + ASSERT_EQ(base::UintToString16(kNumExtraErrors), + list.front()->message()); + // ..and the last stored should be the 105th reported. + ASSERT_EQ(base::UintToString16(kMaxErrorsPerExtension + kNumExtraErrors - 1), + list.back()->message()); +} + +// Test to ensure that the error console will not add duplicate errors, but will +// keep the latest version of an error. +TEST_F(ErrorMapUnitTest, DuplicateErrorsAreReplaced) { + ASSERT_EQ(0u, errors_.size()); + + const std::string kId = id_util::GenerateId("id"); + const size_t kNumErrors = 3u; + + // Report three errors. + for (size_t i = 0; i < kNumErrors; ++i) { + ASSERT_TRUE(errors_.AddError( + CreateNewRuntimeError(kId, base::UintToString(i)))); + } + + // Create an error identical to the second error reported, save its + // location, and add it to the error map. + scoped_ptr<ExtensionError> runtime_error2 = + CreateNewRuntimeError(kId, base::UintToString(1u)); + const ExtensionError* weak_error = runtime_error2.get(); + ASSERT_TRUE(errors_.AddError(runtime_error2.Pass())); + + // We should only have three errors stored, since two of the four reported + // were identical, and the older should have been replaced. + ASSERT_EQ(1u, errors_.size()); + const ErrorList& list = errors_.GetErrorsForExtension(kId); + ASSERT_EQ(kNumErrors, list.size()); + + // The duplicate error should be the last reported (pointer comparison)... + ASSERT_EQ(weak_error, list.back()); + // ... and should have two reported occurrences. + ASSERT_EQ(2u, list.back()->occurrences()); +} + +} // namespace extensions diff --git a/extensions/browser/event_router.cc b/extensions/browser/event_router.cc index 6105572a22..58cbf24607 100644 --- a/extensions/browser/event_router.cc +++ b/extensions/browser/event_router.cc @@ -13,13 +13,13 @@ #include "base/values.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/extensions/extension_host.h" -#include "chrome/browser/extensions/extension_prefs.h" -#include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/extensions/extension_system.h" -#include "chrome/browser/extensions/extension_util.h" #include "chrome/common/extensions/extension_messages.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/render_process_host.h" +#include "extensions/browser/api_activity_monitor.h" +#include "extensions/browser/extension_prefs.h" +#include "extensions/browser/extension_registry.h" +#include "extensions/browser/extension_system.h" #include "extensions/browser/extensions_browser_client.h" #include "extensions/browser/lazy_background_task_queue.h" #include "extensions/browser/process_manager.h" @@ -45,6 +45,35 @@ void DoNothing(ExtensionHost* host) {} // registered from its lazy background page. const char kFilteredEvents[] = "filtered_events"; +// Sends a notification about an event to the API activity monitor on the +// UI thread. Can be called from any thread. +void NotifyApiEventDispatched(void* browser_context_id, + const std::string& extension_id, + const std::string& event_name, + scoped_ptr<ListValue> args) { + // The ApiActivityMonitor can only be accessed from the UI thread. + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&NotifyApiEventDispatched, + browser_context_id, + extension_id, + event_name, + base::Passed(&args))); + return; + } + + // Notify the ApiActivityMonitor about the event dispatch. + BrowserContext* context = static_cast<BrowserContext*>(browser_context_id); + if (!ExtensionsBrowserClient::Get()->IsValidContext(context)) + return; + ApiActivityMonitor* monitor = + ExtensionsBrowserClient::Get()->GetApiActivityMonitor(context); + if (monitor) + monitor->OnApiEventDispatched(extension_id, event_name, args.Pass()); +} + } // namespace const char EventRouter::kRegisteredEvents[] = "events"; @@ -67,33 +96,6 @@ struct EventRouter::ListenerProcess { }; // static -void EventRouter::NotifyExtensionDispatchObserverOnUIThread( - void* browser_context_id, - scoped_ptr<EventDispatchInfo> details) { - if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { - BrowserThread::PostTask( - BrowserThread::UI, - FROM_HERE, - base::Bind(&NotifyExtensionDispatchObserverOnUIThread, - browser_context_id, base::Passed(&details))); - } else { - BrowserContext* context = - reinterpret_cast<BrowserContext*>(browser_context_id); - if (!ExtensionsBrowserClient::Get()->IsValidContext(context)) - return; - ExtensionSystem* extension_system = - ExtensionSystem::GetForBrowserContext(context); - EventRouter* event_router = extension_system->event_router(); - if (!event_router) - return; - if (event_router->event_dispatch_observer_) { - event_router->event_dispatch_observer_->OnWillDispatchEvent( - details.Pass()); - } - } -} - -// static void EventRouter::DispatchExtensionMessage(IPC::Sender* ipc_sender, void* browser_context_id, const std::string& extension_id, @@ -101,12 +103,10 @@ void EventRouter::DispatchExtensionMessage(IPC::Sender* ipc_sender, ListValue* event_args, UserGestureState user_gesture, const EventFilteringInfo& info) { - NotifyExtensionDispatchObserverOnUIThread( - browser_context_id, - make_scoped_ptr(new EventDispatchInfo( - extension_id, - event_name, - make_scoped_ptr(event_args->DeepCopy())))); + NotifyApiEventDispatched(browser_context_id, + extension_id, + event_name, + make_scoped_ptr(event_args->DeepCopy())); ListValue args; args.Set(0, new base::StringValue(event_name)); @@ -122,7 +122,7 @@ void EventRouter::DispatchExtensionMessage(IPC::Sender* ipc_sender, // DispatchExtensionMessage does _not_ take ownership of event_args, so we // must ensure that the destruction of args does not attempt to free it. - scoped_ptr<Value> removed_event_args; + scoped_ptr<base::Value> removed_event_args; args.Remove(1, &removed_event_args); ignore_result(removed_event_args.release()); } @@ -161,8 +161,7 @@ EventRouter::EventRouter(BrowserContext* browser_context, ExtensionPrefs* extension_prefs) : browser_context_(browser_context), extension_prefs_(extension_prefs), - listeners_(this), - event_dispatch_observer_(NULL) { + listeners_(this) { registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, content::NotificationService::AllSources()); registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, @@ -210,11 +209,6 @@ void EventRouter::UnregisterObserver(Observer* observer) { observers_.erase(iters_to_remove[i]); } -void EventRouter::SetEventDispatchObserver(EventDispatchObserver* observer) { - CHECK(!event_dispatch_observer_); - event_dispatch_observer_ = observer; -} - void EventRouter::OnListenerAdded(const EventListener* listener) { const EventListenerInfo details( listener->event_name, @@ -356,7 +350,7 @@ void EventRouter::SetRegisteredEvents(const std::string& extension_id, ListValue* events_value = new ListValue; for (std::set<std::string>::const_iterator iter = events.begin(); iter != events.end(); ++iter) { - events_value->Append(new StringValue(*iter)); + events_value->Append(new base::StringValue(*iter)); } extension_prefs_->UpdateExtensionPref( extension_id, kRegisteredEvents, events_value); @@ -482,12 +476,12 @@ void EventRouter::DispatchLazyEvent( const std::string& extension_id, const linked_ptr<Event>& event, std::set<EventDispatchIdentifier>* already_dispatched) { - ExtensionService* service = ExtensionSystem::GetForBrowserContext( - browser_context_)->extension_service(); // Check both the original and the incognito browser context to see if we // should load a lazy bg page to handle the event. The latter case // occurs in the case of split-mode extensions. - const Extension* extension = service->extensions()->GetByID(extension_id); + const Extension* extension = + ExtensionRegistry::Get(browser_context_)->enabled_extensions().GetByID( + extension_id); if (!extension) return; @@ -512,9 +506,9 @@ void EventRouter::DispatchLazyEvent( void EventRouter::DispatchEventToProcess(const std::string& extension_id, content::RenderProcessHost* process, const linked_ptr<Event>& event) { - ExtensionService* service = ExtensionSystem::GetForBrowserContext( - browser_context_)->extension_service(); - const Extension* extension = service->extensions()->GetByID(extension_id); + const Extension* extension = + ExtensionRegistry::Get(browser_context_)->enabled_extensions().GetByID( + extension_id); // The extension could have been removed, but we do not unregister it until // the extension process is unloaded. @@ -522,10 +516,7 @@ void EventRouter::DispatchEventToProcess(const std::string& extension_id, return; BrowserContext* listener_context = process->GetBrowserContext(); - ProcessMap* process_map = - ExtensionSystem::GetForBrowserContext(listener_context) - ->extension_service() - ->process_map(); + ProcessMap* process_map = ProcessMap::Get(listener_context); // If the event is privileged, only send to extension processes. Otherwise, // it's OK to send to normal renderers (e.g., for content scripts). if (ExtensionAPI::GetSharedInstance()->IsPrivileged(event->event_name) && @@ -566,19 +557,26 @@ bool EventRouter::CanDispatchEventToBrowserContext( context != event->restrict_to_browser_context; if (!cross_incognito) return true; - ExtensionService* service = - ExtensionSystem::GetForBrowserContext(context)->extension_service(); - return extension_util::CanCrossIncognito(extension, service); + return ExtensionsBrowserClient::Get()->CanExtensionCrossIncognito( + extension, context); } bool EventRouter::MaybeLoadLazyBackgroundPageToDispatchEvent( BrowserContext* context, const Extension* extension, const linked_ptr<Event>& event) { + if (extension->is_ephemeral() && !event->can_load_ephemeral_apps) { + // Most events can only be dispatched to ephemeral apps that are already + // running. + ProcessManager* pm = ExtensionSystem::Get(context)->process_manager(); + if (!pm->GetBackgroundHostForExtension(extension->id())) + return false; + } + if (!CanDispatchEventToBrowserContext(context, extension, event)) return false; - LazyBackgroundTaskQueue* queue = ExtensionSystem::GetForBrowserContext( + LazyBackgroundTaskQueue* queue = ExtensionSystem::Get( context)->lazy_background_task_queue(); if (queue->ShouldEnqueueTask(context, extension)) { linked_ptr<Event> dispatched_event(event); @@ -612,14 +610,13 @@ void EventRouter::IncrementInFlightEventsOnUI( reinterpret_cast<BrowserContext*>(browser_context_id); if (!ExtensionsBrowserClient::Get()->IsValidContext(browser_context)) return; - ExtensionSystem* extension_system = - ExtensionSystem::GetForBrowserContext(browser_context); + ExtensionSystem* extension_system = ExtensionSystem::Get(browser_context); EventRouter* event_router = extension_system->event_router(); if (!event_router) return; - ExtensionService* extension_service = extension_system->extension_service(); const Extension* extension = - extension_service->extensions()->GetByID(extension_id); + ExtensionRegistry::Get(browser_context)->enabled_extensions().GetByID( + extension_id); if (!extension) return; event_router->IncrementInFlightEvents(browser_context, extension); @@ -630,8 +627,7 @@ void EventRouter::IncrementInFlightEvents(BrowserContext* context, // Only increment in-flight events if the lazy background page is active, // because that's the only time we'll get an ACK. if (BackgroundInfo::HasLazyBackgroundPage(extension)) { - ProcessManager* pm = - ExtensionSystem::GetForBrowserContext(context)->process_manager(); + ProcessManager* pm = ExtensionSystem::Get(context)->process_manager(); ExtensionHost* host = pm->GetBackgroundHostForExtension(extension->id()); if (host) pm->IncrementLazyKeepaliveCount(extension); @@ -640,8 +636,7 @@ void EventRouter::IncrementInFlightEvents(BrowserContext* context, void EventRouter::OnEventAck(BrowserContext* context, const std::string& extension_id) { - ProcessManager* pm = - ExtensionSystem::GetForBrowserContext(context)->process_manager(); + ProcessManager* pm = ExtensionSystem::Get(context)->process_manager(); ExtensionHost* host = pm->GetBackgroundHostForExtension(extension_id); // The event ACK is routed to the background host, so this should never be // NULL. @@ -683,7 +678,7 @@ void EventRouter::Observe(int type, const Extension* extension = content::Details<const Extension>(details).ptr(); if (BackgroundInfo::HasLazyBackgroundPage(extension)) { - LazyBackgroundTaskQueue* queue = ExtensionSystem::GetForBrowserContext( + LazyBackgroundTaskQueue* queue = ExtensionSystem::Get( browser_context_)->lazy_background_task_queue(); queue->AddPendingTask(browser_context_, extension->id(), base::Bind(&DoNothing)); @@ -722,7 +717,8 @@ Event::Event(const std::string& event_name, : event_name(event_name), event_args(event_args.Pass()), restrict_to_browser_context(NULL), - user_gesture(EventRouter::USER_GESTURE_UNKNOWN) { + user_gesture(EventRouter::USER_GESTURE_UNKNOWN), + can_load_ephemeral_apps(false) { DCHECK(this->event_args.get()); } @@ -732,7 +728,8 @@ Event::Event(const std::string& event_name, : event_name(event_name), event_args(event_args.Pass()), restrict_to_browser_context(restrict_to_browser_context), - user_gesture(EventRouter::USER_GESTURE_UNKNOWN) { + user_gesture(EventRouter::USER_GESTURE_UNKNOWN), + can_load_ephemeral_apps(false) { DCHECK(this->event_args.get()); } @@ -747,7 +744,8 @@ Event::Event(const std::string& event_name, restrict_to_browser_context(restrict_to_browser_context), event_url(event_url), user_gesture(user_gesture), - filter_info(filter_info) { + filter_info(filter_info), + can_load_ephemeral_apps(false) { DCHECK(this->event_args.get()); } @@ -771,13 +769,4 @@ EventListenerInfo::EventListenerInfo(const std::string& event_name, extension_id(extension_id), browser_context(browser_context) {} -EventDispatchInfo::EventDispatchInfo(const std::string& extension_id, - const std::string& event_name, - scoped_ptr<ListValue> event_args) - : extension_id(extension_id), - event_name(event_name), - event_args(event_args.Pass()) {} - -EventDispatchInfo::~EventDispatchInfo() {} - } // namespace extensions diff --git a/extensions/browser/event_router.h b/extensions/browser/event_router.h index d65adb097d..467e7f396a 100644 --- a/extensions/browser/event_router.h +++ b/extensions/browser/event_router.h @@ -68,13 +68,6 @@ class EventRouter : public content::NotificationObserver, virtual void OnListenerRemoved(const EventListenerInfo& details) {} }; - // The EventDispatchObserver is notified on the UI thread whenever - // an event is dispatched. There can be only one EventDispatchObserver. - class EventDispatchObserver { - public: - virtual void OnWillDispatchEvent(scoped_ptr<EventDispatchInfo> details) = 0; - }; - // Converts event names like "foo.onBar/123" into "foo.onBar". Event names // without a "/" are returned unchanged. static std::string GetBaseEventName(const std::string& full_event_name); @@ -117,10 +110,6 @@ class EventRouter : public content::NotificationObserver, // Unregisters an observer from all events. void UnregisterObserver(Observer* observer); - // Sets the observer to be notified whenever an event is dispatched to an - // extension. - void SetEventDispatchObserver(EventDispatchObserver* observer); - // Add or remove the extension as having a lazy background page that listens // to the event. The difference from the above methods is that these will be // remembered even after the process goes away. We use this list to decide @@ -192,12 +181,6 @@ class EventRouter : public content::NotificationObserver, typedef std::pair<const content::BrowserContext*, std::string> EventDispatchIdentifier; - // Sends a notification about an event to the event dispatch observer on the - // UI thread. Can be called from any thread. - static void NotifyExtensionDispatchObserverOnUIThread( - void* browser_context_id, - scoped_ptr<EventDispatchInfo> details); - // TODO(gdk): Document this. static void DispatchExtensionMessage( IPC::Sender* ipc_sender, @@ -300,8 +283,6 @@ class EventRouter : public content::NotificationObserver, typedef base::hash_map<std::string, Observer*> ObserverMap; ObserverMap observers_; - EventDispatchObserver* event_dispatch_observer_; - DISALLOW_COPY_AND_ASSIGN(EventRouter); }; @@ -338,6 +319,12 @@ struct Event { // DispatchEvent, so callers don't need to worry about lifetime. WillDispatchCallback will_dispatch_callback; + // If true, this event will always be dispatched to ephemeral apps, regardless + // of whether they are running or inactive. Defaults to false. + // Most events can only be dispatched to ephemeral apps that are already + // running. Cached ephemeral apps are inactive until launched by the user. + bool can_load_ephemeral_apps; + Event(const std::string& event_name, scoped_ptr<base::ListValue> event_args); @@ -371,17 +358,6 @@ struct EventListenerInfo { content::BrowserContext* browser_context; }; -struct EventDispatchInfo { - EventDispatchInfo(const std::string& extension_id, - const std::string& event_name, - scoped_ptr<ListValue> event_args); - ~EventDispatchInfo(); - - const std::string extension_id; - const std::string event_name; - scoped_ptr<ListValue> event_args; -}; - } // namespace extensions #endif // EXTENSIONS_BROWSER_EVENT_ROUTER_H_ diff --git a/extensions/browser/event_router_unittest.cc b/extensions/browser/event_router_unittest.cc index ba5fed300d..c47b7f20ed 100644 --- a/extensions/browser/event_router_unittest.cc +++ b/extensions/browser/event_router_unittest.cc @@ -69,7 +69,7 @@ TEST_F(EventRouterTest, GetBaseEventName) { TEST_F(EventRouterTest, EventRouterObserver) { EventRouter router(NULL, NULL); EventListener listener( - "event_name", "extension_id", NULL, scoped_ptr<DictionaryValue>()); + "event_name", "extension_id", NULL, scoped_ptr<base::DictionaryValue>()); // Add/remove works without any observers. router.OnListenerAdded(&listener); @@ -104,8 +104,10 @@ TEST_F(EventRouterTest, EventRouterObserver) { // Adding a listener with a sub-event notifies the main observer with // proper details. matching_observer.Reset(); - EventListener sub_event_listener( - "event_name/1", "extension_id", NULL, scoped_ptr<DictionaryValue>()); + EventListener sub_event_listener("event_name/1", + "extension_id", + NULL, + scoped_ptr<base::DictionaryValue>()); router.OnListenerAdded(&sub_event_listener); EXPECT_EQ(1, matching_observer.listener_added_count()); EXPECT_EQ(0, matching_observer.listener_removed_count()); diff --git a/extensions/browser/extension_error.cc b/extensions/browser/extension_error.cc index ee6b47e58a..d8733fe51d 100644 --- a/extensions/browser/extension_error.cc +++ b/extensions/browser/extension_error.cc @@ -10,7 +10,6 @@ #include "extensions/common/constants.h" #include "url/gurl.h" -using base::string16; using base::DictionaryValue; namespace extensions { @@ -30,8 +29,8 @@ ExtensionError::ExtensionError(Type type, const std::string& extension_id, bool from_incognito, logging::LogSeverity level, - const string16& source, - const string16& message) + const base::string16& source, + const base::string16& message) : type_(type), extension_id_(extension_id), from_incognito_(from_incognito), @@ -84,9 +83,9 @@ const char ManifestError::kManifestKeyKey[] = "manifestKey"; const char ManifestError::kManifestSpecificKey[] = "manifestSpecific"; ManifestError::ManifestError(const std::string& extension_id, - const string16& message, - const string16& manifest_key, - const string16& manifest_specific) + const base::string16& message, + const base::string16& manifest_key, + const base::string16& manifest_specific) : ExtensionError(ExtensionError::MANIFEST_ERROR, extension_id, false, // extensions can't be installed while incognito. @@ -135,8 +134,8 @@ const char RuntimeError::kRenderViewIdKey[] = "renderViewId"; RuntimeError::RuntimeError(const std::string& extension_id, bool from_incognito, - const string16& source, - const string16& message, + const base::string16& source, + const base::string16& message, const StackTrace& stack_trace, const GURL& context_url, logging::LogSeverity level, diff --git a/extensions/browser/extension_error_test_util.cc b/extensions/browser/extension_error_test_util.cc new file mode 100644 index 0000000000..cdf4917dd0 --- /dev/null +++ b/extensions/browser/extension_error_test_util.cc @@ -0,0 +1,64 @@ +// Copyright 2014 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_test_util.h" + +#include "base/logging.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "content/public/common/url_constants.h" +#include "extensions/browser/extension_error.h" +#include "extensions/common/constants.h" +#include "extensions/common/stack_frame.h" +#include "url/gurl.h" + +namespace extensions { +namespace error_test_util { + +namespace { +const char kDefaultStackTrace[] = "function_name (https://url.com:1:1)"; +} + +scoped_ptr<ExtensionError> CreateNewRuntimeError( + const std::string& extension_id, + const std::string& message, + bool from_incognito) { + StackTrace stack_trace; + scoped_ptr<StackFrame> frame = + StackFrame::CreateFromText(base::UTF8ToUTF16(kDefaultStackTrace)); + CHECK(frame.get()); + stack_trace.push_back(*frame); + + base::string16 source = + base::UTF8ToUTF16(std::string(kExtensionScheme) + + content::kStandardSchemeSeparator + + extension_id); + + return scoped_ptr<ExtensionError>(new RuntimeError( + extension_id, + from_incognito, + source, + base::UTF8ToUTF16(message), + stack_trace, + GURL::EmptyGURL(), // no context url + logging::LOG_INFO, + 0, 0 /* Render [View|Process] ID */ )); +} + +scoped_ptr<ExtensionError> CreateNewRuntimeError( + const std::string& extension_id, const std::string& message) { + return CreateNewRuntimeError(extension_id, message, false); +} + +scoped_ptr<ExtensionError> CreateNewManifestError( + const std::string& extension_id, const std::string& message) { + return scoped_ptr<ExtensionError>( + new ManifestError(extension_id, + base::UTF8ToUTF16(message), + base::EmptyString16(), + base::EmptyString16())); +} + +} // namespace error_test_util +} // namespace extensions diff --git a/extensions/browser/extension_error_test_util.h b/extensions/browser/extension_error_test_util.h new file mode 100644 index 0000000000..4866cefe25 --- /dev/null +++ b/extensions/browser/extension_error_test_util.h @@ -0,0 +1,35 @@ +// Copyright 2014 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_TEST_UTIL_H_ +#define EXTENSIONS_BROWSER_EXTENSION_ERROR_TEST_UTIL_H_ + +#include <string> + +#include "base/memory/scoped_ptr.h" + +namespace extensions { + +class ExtensionError; + +namespace error_test_util { + +// Create a new RuntimeError. +scoped_ptr<ExtensionError> CreateNewRuntimeError( + const std::string& extension_id, + const std::string& message, + bool from_incognito); + +// Create a new RuntimeError; incognito defaults to "false". +scoped_ptr<ExtensionError> CreateNewRuntimeError( + const std::string& extension_id, const std::string& message); + +// Create a new ManifestError. +scoped_ptr<ExtensionError> CreateNewManifestError( + const std::string& extension_id, const std::string& mnessage); + +} // namespace error_test_util +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_EXTENSION_ERROR_TEST_UTIL_H_ diff --git a/extensions/browser/extension_function.cc b/extensions/browser/extension_function.cc index a06d2c496d..8c7c7527d6 100644 --- a/extensions/browser/extension_function.cc +++ b/extensions/browser/extension_function.cc @@ -12,6 +12,7 @@ #include "chrome/common/extensions/extension_messages.h" #include "content/public/browser/notification_source.h" #include "content/public/browser/notification_types.h" +#include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_observer.h" @@ -28,15 +29,18 @@ void ExtensionFunctionDeleteTraits::Destruct(const ExtensionFunction* x) { x->Destruct(); } -// Helper class to track the lifetime of ExtensionFunction's RenderViewHost -// pointer and NULL it out when it dies. It also allows us to filter IPC -// messages coming from the RenderViewHost. -class UIThreadExtensionFunction::RenderViewHostTracker +// Helper class to track the lifetime of ExtensionFunction's RenderViewHost or +// RenderFrameHost pointer and NULL it out when it dies. It also allows us to +// filter IPC messages coming from the RenderViewHost/RenderFrameHost. +class UIThreadExtensionFunction::RenderHostTracker : public content::WebContentsObserver { public: - explicit RenderViewHostTracker(UIThreadExtensionFunction* function) + explicit RenderHostTracker(UIThreadExtensionFunction* function) : content::WebContentsObserver( - WebContents::FromRenderViewHost(function->render_view_host())), + function->render_view_host() ? + WebContents::FromRenderViewHost(function->render_view_host()) : + WebContents::FromRenderFrameHost( + function->render_frame_host())), function_(function) { } @@ -49,14 +53,21 @@ class UIThreadExtensionFunction::RenderViewHostTracker function_->SetRenderViewHost(NULL); } + virtual void RenderFrameDeleted( + content::RenderFrameHost* render_frame_host) OVERRIDE { + if (render_frame_host != function_->render_frame_host()) + return; + + function_->SetRenderFrameHost(NULL); + } virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { - return function_->OnMessageReceivedFromRenderView(message); + return function_->OnMessageReceived(message); } UIThreadExtensionFunction* function_; - DISALLOW_COPY_AND_ASSIGN(RenderViewHostTracker); + DISALLOW_COPY_AND_ASSIGN(RenderHostTracker); }; ExtensionFunction::ExtensionFunction() @@ -66,7 +77,9 @@ ExtensionFunction::ExtensionFunction() include_incognito_(false), user_gesture_(false), bad_message_(false), - histogram_value_(extensions::functions::UNKNOWN) {} + histogram_value_(extensions::functions::UNKNOWN), + source_tab_id_(-1) { +} ExtensionFunction::~ExtensionFunction() { } @@ -125,8 +138,8 @@ bool ExtensionFunction::ShouldSkipQuotaLimiting() const { } bool ExtensionFunction::HasOptionalArgument(size_t index) { - Value* value; - return args_->Get(index, &value) && !value->IsType(Value::TYPE_NULL); + base::Value* value; + return args_->Get(index, &value) && !value->IsType(base::Value::TYPE_NULL); } void ExtensionFunction::SendResponseImpl(bool success) { @@ -146,7 +159,11 @@ void ExtensionFunction::SendResponseImpl(bool success) { } UIThreadExtensionFunction::UIThreadExtensionFunction() - : render_view_host_(NULL), context_(NULL), delegate_(NULL) {} + : render_view_host_(NULL), + render_frame_host_(NULL), + context_(NULL), + delegate_(NULL) { +} UIThreadExtensionFunction::~UIThreadExtensionFunction() { if (dispatcher() && render_view_host()) @@ -158,8 +175,7 @@ UIThreadExtensionFunction::AsUIThreadExtensionFunction() { return this; } -bool UIThreadExtensionFunction::OnMessageReceivedFromRenderView( - const IPC::Message& message) { +bool UIThreadExtensionFunction::OnMessageReceived(const IPC::Message& message) { return false; } @@ -169,8 +185,16 @@ void UIThreadExtensionFunction::Destruct() const { void UIThreadExtensionFunction::SetRenderViewHost( RenderViewHost* render_view_host) { + DCHECK(!render_frame_host_); render_view_host_ = render_view_host; - tracker_.reset(render_view_host ? new RenderViewHostTracker(this) : NULL); + tracker_.reset(render_view_host ? new RenderHostTracker(this) : NULL); +} + +void UIThreadExtensionFunction::SetRenderFrameHost( + content::RenderFrameHost* render_frame_host) { + DCHECK(!render_view_host_); + render_frame_host_ = render_frame_host; + tracker_.reset(render_frame_host ? new RenderHostTracker(this) : NULL); } content::WebContents* UIThreadExtensionFunction::GetAssociatedWebContents() { @@ -191,8 +215,13 @@ void UIThreadExtensionFunction::SendResponse(bool success) { void UIThreadExtensionFunction::WriteToConsole( content::ConsoleMessageLevel level, const std::string& message) { - render_view_host_->Send(new ExtensionMsg_AddMessageToConsole( - render_view_host_->GetRoutingID(), level, message)); + if (render_view_host_) { + render_view_host_->Send(new ExtensionMsg_AddMessageToConsole( + render_view_host_->GetRoutingID(), level, message)); + } else { + render_frame_host_->Send(new ExtensionMsg_AddMessageToConsole( + render_frame_host_->GetRoutingID(), level, message)); + } } IOThreadExtensionFunction::IOThreadExtensionFunction() diff --git a/extensions/browser/extension_function.h b/extensions/browser/extension_function.h index aa0ed038b0..099dff1661 100644 --- a/extensions/browser/extension_function.h +++ b/extensions/browser/extension_function.h @@ -35,6 +35,7 @@ class Value; namespace content { class BrowserContext; +class RenderFrameHost; class RenderViewHost; class WebContents; } @@ -191,6 +192,9 @@ class ExtensionFunction response_callback_ = callback; } + void set_source_tab_id(int source_tab_id) { source_tab_id_ = source_tab_id; } + int source_tab_id() const { return source_tab_id_; } + protected: friend struct ExtensionFunctionDeleteTraits; @@ -263,6 +267,10 @@ class ExtensionFunction // The callback to run once the function has done execution. ResponseCallback response_callback_; + // The ID of the tab triggered this function call, or -1 if there is no tab. + int source_tab_id_; + + private: DISALLOW_COPY_AND_ASSIGN(ExtensionFunction); }; @@ -290,7 +298,7 @@ class UIThreadExtensionFunction : public ExtensionFunction { // Called when a message was received. // Should return true if it processed the message. - virtual bool OnMessageReceivedFromRenderView(const IPC::Message& message); + virtual bool OnMessageReceived(const IPC::Message& message); // Set the browser context which contains the extension that has originated // this function call. @@ -301,6 +309,10 @@ class UIThreadExtensionFunction : public ExtensionFunction { content::RenderViewHost* render_view_host() const { return render_view_host_; } + void SetRenderFrameHost(content::RenderFrameHost* render_frame_host); + content::RenderFrameHost* render_frame_host() const { + return render_frame_host_; + } void set_dispatcher( const base::WeakPtr<ExtensionFunctionDispatcher>& dispatcher) { @@ -330,18 +342,23 @@ class UIThreadExtensionFunction : public ExtensionFunction { // The dispatcher that will service this extension function call. base::WeakPtr<ExtensionFunctionDispatcher> dispatcher_; - // The RenderViewHost we will send responses too. + // The RenderViewHost we will send responses to. content::RenderViewHost* render_view_host_; + // The RenderFrameHost we will send responses to. + // NOTE: either render_view_host_ or render_frame_host_ will be set, as we + // port code to use RenderFrames for OOPIF. See http://crbug.com/304341. + content::RenderFrameHost* render_frame_host_; + // The content::BrowserContext of this function's extension. content::BrowserContext* context_; private: - class RenderViewHostTracker; + class RenderHostTracker; virtual void Destruct() const OVERRIDE; - scoped_ptr<RenderViewHostTracker> tracker_; + scoped_ptr<RenderHostTracker> tracker_; DelegateForTests* delegate_; }; diff --git a/extensions/browser/extension_message_filter.cc b/extensions/browser/extension_message_filter.cc new file mode 100644 index 0000000000..ea5bfa7f80 --- /dev/null +++ b/extensions/browser/extension_message_filter.cc @@ -0,0 +1,175 @@ +// Copyright 2014 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_message_filter.h" + +#include "chrome/common/extensions/extension_messages.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/resource_dispatcher_host.h" +#include "extensions/browser/event_router.h" +#include "extensions/browser/extension_system.h" +#include "extensions/browser/process_manager.h" +#include "ipc/ipc_message_macros.h" + +using content::BrowserThread; +using content::RenderProcessHost; + +namespace extensions { + +ExtensionMessageFilter::ExtensionMessageFilter(int render_process_id, + content::BrowserContext* context) + : BrowserMessageFilter(ExtensionMsgStart), + render_process_id_(render_process_id), + browser_context_(context) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); +} + +ExtensionMessageFilter::~ExtensionMessageFilter() {} + +void ExtensionMessageFilter::OverrideThreadForMessage( + const IPC::Message& message, + BrowserThread::ID* thread) { + switch (message.type()) { + case ExtensionHostMsg_AddListener::ID: + case ExtensionHostMsg_RemoveListener::ID: + case ExtensionHostMsg_AddLazyListener::ID: + case ExtensionHostMsg_RemoveLazyListener::ID: + case ExtensionHostMsg_AddFilteredListener::ID: + case ExtensionHostMsg_RemoveFilteredListener::ID: + case ExtensionHostMsg_ShouldSuspendAck::ID: + case ExtensionHostMsg_SuspendAck::ID: + *thread = BrowserThread::UI; + break; + default: + break; + } +} + +bool ExtensionMessageFilter::OnMessageReceived(const IPC::Message& message, + bool* message_was_ok) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP_EX(ExtensionMessageFilter, message, *message_was_ok) + IPC_MESSAGE_HANDLER(ExtensionHostMsg_AddListener, + OnExtensionAddListener) + IPC_MESSAGE_HANDLER(ExtensionHostMsg_RemoveListener, + OnExtensionRemoveListener) + IPC_MESSAGE_HANDLER(ExtensionHostMsg_AddLazyListener, + OnExtensionAddLazyListener) + IPC_MESSAGE_HANDLER(ExtensionHostMsg_RemoveLazyListener, + OnExtensionRemoveLazyListener) + IPC_MESSAGE_HANDLER(ExtensionHostMsg_AddFilteredListener, + OnExtensionAddFilteredListener) + IPC_MESSAGE_HANDLER(ExtensionHostMsg_RemoveFilteredListener, + OnExtensionRemoveFilteredListener) + IPC_MESSAGE_HANDLER(ExtensionHostMsg_ShouldSuspendAck, + OnExtensionShouldSuspendAck) + IPC_MESSAGE_HANDLER(ExtensionHostMsg_SuspendAck, + OnExtensionSuspendAck) + IPC_MESSAGE_HANDLER(ExtensionHostMsg_GenerateUniqueID, + OnExtensionGenerateUniqueID) + IPC_MESSAGE_HANDLER(ExtensionHostMsg_ResumeRequests, + OnExtensionResumeRequests); + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void ExtensionMessageFilter::OnExtensionAddListener( + const std::string& extension_id, + const std::string& event_name) { + RenderProcessHost* process = RenderProcessHost::FromID(render_process_id_); + if (!process) + return; + EventRouter* router = ExtensionSystem::Get(browser_context_)->event_router(); + if (!router) + return; + router->AddEventListener(event_name, process, extension_id); +} + +void ExtensionMessageFilter::OnExtensionRemoveListener( + const std::string& extension_id, + const std::string& event_name) { + RenderProcessHost* process = RenderProcessHost::FromID(render_process_id_); + if (!process) + return; + EventRouter* router = ExtensionSystem::Get(browser_context_)->event_router(); + if (!router) + return; + router->RemoveEventListener(event_name, process, extension_id); +} + +void ExtensionMessageFilter::OnExtensionAddLazyListener( + const std::string& extension_id, const std::string& event_name) { + EventRouter* router = ExtensionSystem::Get(browser_context_)->event_router(); + if (!router) + return; + router->AddLazyEventListener(event_name, extension_id); +} + +void ExtensionMessageFilter::OnExtensionRemoveLazyListener( + const std::string& extension_id, const std::string& event_name) { + EventRouter* router = ExtensionSystem::Get(browser_context_)->event_router(); + if (!router) + return; + router->RemoveLazyEventListener(event_name, extension_id); +} + +void ExtensionMessageFilter::OnExtensionAddFilteredListener( + const std::string& extension_id, + const std::string& event_name, + const base::DictionaryValue& filter, + bool lazy) { + RenderProcessHost* process = RenderProcessHost::FromID(render_process_id_); + if (!process) + return; + EventRouter* router = ExtensionSystem::Get(browser_context_)->event_router(); + if (!router) + return; + router->AddFilteredEventListener( + event_name, process, extension_id, filter, lazy); +} + +void ExtensionMessageFilter::OnExtensionRemoveFilteredListener( + const std::string& extension_id, + const std::string& event_name, + const base::DictionaryValue& filter, + bool lazy) { + RenderProcessHost* process = RenderProcessHost::FromID(render_process_id_); + if (!process) + return; + EventRouter* router = ExtensionSystem::Get(browser_context_)->event_router(); + if (!router) + return; + router->RemoveFilteredEventListener( + event_name, process, extension_id, filter, lazy); +} + +void ExtensionMessageFilter::OnExtensionShouldSuspendAck( + const std::string& extension_id, int sequence_id) { + ProcessManager* process_manager = + ExtensionSystem::Get(browser_context_)->process_manager(); + if (process_manager) + process_manager->OnShouldSuspendAck(extension_id, sequence_id); +} + +void ExtensionMessageFilter::OnExtensionSuspendAck( + const std::string& extension_id) { + ProcessManager* process_manager = + ExtensionSystem::Get(browser_context_)->process_manager(); + if (process_manager) + process_manager->OnSuspendAck(extension_id); +} + +void ExtensionMessageFilter::OnExtensionGenerateUniqueID(int* unique_id) { + static int next_unique_id = 0; + *unique_id = ++next_unique_id; +} + +void ExtensionMessageFilter::OnExtensionResumeRequests(int route_id) { + content::ResourceDispatcherHost::Get()->ResumeBlockedRequestsForRoute( + render_process_id_, route_id); +} + +} // namespace extensions diff --git a/extensions/browser/extension_message_filter.h b/extensions/browser/extension_message_filter.h new file mode 100644 index 0000000000..8518bf9e81 --- /dev/null +++ b/extensions/browser/extension_message_filter.h @@ -0,0 +1,77 @@ +// Copyright 2014 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_RENDER_MESSAGE_FILTER_H_ +#define EXTENSIONS_BROWSER_EXTENSION_RENDER_MESSAGE_FILTER_H_ + +#include <string> + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "content/public/browser/browser_message_filter.h" + +namespace base { +class DictionaryValue; +} + +namespace content { +class BrowserContext; +} + +namespace extensions { + +// This class filters out incoming extension-specific IPC messages from the +// renderer process. It is created on the UI thread. Messages may be handled on +// the IO thread or the UI thread. +class ExtensionMessageFilter : public content::BrowserMessageFilter { + public: + ExtensionMessageFilter(int render_process_id, + content::BrowserContext* context); + + private: + virtual ~ExtensionMessageFilter(); + + // content::BrowserMessageFilter implementation. + virtual void OverrideThreadForMessage( + const IPC::Message& message, + content::BrowserThread::ID* thread) OVERRIDE; + virtual bool OnMessageReceived(const IPC::Message& message, + bool* message_was_ok) OVERRIDE; + + // Message handlers on the UI thread. + void OnExtensionAddListener(const std::string& extension_id, + const std::string& event_name); + void OnExtensionRemoveListener(const std::string& extension_id, + const std::string& event_name); + void OnExtensionAddLazyListener(const std::string& extension_id, + const std::string& event_name); + void OnExtensionRemoveLazyListener(const std::string& extension_id, + const std::string& event_name); + void OnExtensionAddFilteredListener(const std::string& extension_id, + const std::string& event_name, + const base::DictionaryValue& filter, + bool lazy); + void OnExtensionRemoveFilteredListener(const std::string& extension_id, + const std::string& event_name, + const base::DictionaryValue& filter, + bool lazy); + void OnExtensionShouldSuspendAck(const std::string& extension_id, + int sequence_id); + void OnExtensionSuspendAck(const std::string& extension_id); + + // Message handlers on the IO thread. + void OnExtensionGenerateUniqueID(int* unique_id); + void OnExtensionResumeRequests(int route_id); + + const int render_process_id_; + + // Should only be accessed on the UI thread. + content::BrowserContext* browser_context_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionMessageFilter); +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_EXTENSION_RENDER_MESSAGE_FILTER_H_ diff --git a/extensions/browser/extension_pref_store.cc b/extensions/browser/extension_pref_store.cc new file mode 100644 index 0000000000..3f26cbb3e3 --- /dev/null +++ b/extensions/browser/extension_pref_store.cc @@ -0,0 +1,43 @@ +// Copyright 2014 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_pref_store.h" + +#include "base/values.h" +#include "extensions/browser/extension_pref_value_map.h" + +ExtensionPrefStore::ExtensionPrefStore( + ExtensionPrefValueMap* extension_pref_value_map, + bool incognito_pref_store) + : extension_pref_value_map_(extension_pref_value_map), + incognito_pref_store_(incognito_pref_store) { + extension_pref_value_map_->AddObserver(this); +} + +void ExtensionPrefStore::OnInitializationCompleted() { + NotifyInitializationCompleted(); +} + +void ExtensionPrefStore::OnPrefValueChanged(const std::string& key) { + CHECK(extension_pref_value_map_); + const base::Value *winner = + extension_pref_value_map_->GetEffectivePrefValue(key, + incognito_pref_store_, + NULL); + if (winner) + SetValue(key, winner->DeepCopy()); + else + RemoveValue(key); +} + +void ExtensionPrefStore::OnExtensionPrefValueMapDestruction() { + CHECK(extension_pref_value_map_); + extension_pref_value_map_->RemoveObserver(this); + extension_pref_value_map_ = NULL; +} + +ExtensionPrefStore::~ExtensionPrefStore() { + if (extension_pref_value_map_) + extension_pref_value_map_->RemoveObserver(this); +} diff --git a/extensions/browser/extension_pref_store.h b/extensions/browser/extension_pref_store.h new file mode 100644 index 0000000000..d93018c90d --- /dev/null +++ b/extensions/browser/extension_pref_store.h @@ -0,0 +1,38 @@ +// Copyright 2014 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_PREF_STORE_H_ +#define EXTENSIONS_BROWSER_EXTENSION_PREF_STORE_H_ + +#include <string> + +#include "base/prefs/value_map_pref_store.h" +#include "extensions/browser/extension_pref_value_map.h" + +// A (non-persistent) PrefStore implementation that holds effective preferences +// set by extensions. These preferences are managed by and fetched from an +// ExtensionPrefValueMap. +class ExtensionPrefStore : public ValueMapPrefStore, + public ExtensionPrefValueMap::Observer { + public: + // Constructs an ExtensionPrefStore for a regular or an incognito profile. + ExtensionPrefStore(ExtensionPrefValueMap* extension_pref_value_map, + bool incognito_pref_store); + + // Overrides for ExtensionPrefValueMap::Observer: + virtual void OnInitializationCompleted() OVERRIDE; + virtual void OnPrefValueChanged(const std::string& key) OVERRIDE; + virtual void OnExtensionPrefValueMapDestruction() OVERRIDE; + + protected: + virtual ~ExtensionPrefStore(); + + private: + ExtensionPrefValueMap* extension_pref_value_map_; // Weak pointer. + bool incognito_pref_store_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionPrefStore); +}; + +#endif // EXTENSIONS_BROWSER_EXTENSION_PREF_STORE_H_ diff --git a/extensions/browser/extension_pref_value_map.cc b/extensions/browser/extension_pref_value_map.cc new file mode 100644 index 0000000000..5cba7e510a --- /dev/null +++ b/extensions/browser/extension_pref_value_map.cc @@ -0,0 +1,376 @@ +// Copyright 2014 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_pref_value_map.h" + +#include "base/prefs/pref_value_map.h" +#include "base/stl_util.h" +#include "base/values.h" + +using extensions::ExtensionPrefsScope; + +struct ExtensionPrefValueMap::ExtensionEntry { + // Installation time of the extension. + base::Time install_time; + // Whether extension is enabled in the profile. + bool enabled; + // Extension controlled preferences for the regular profile. + PrefValueMap regular_profile_preferences; + // Extension controlled preferences that should *only* apply to the regular + // profile. + PrefValueMap regular_only_profile_preferences; + // Persistent extension controlled preferences for the incognito profile, + // empty for regular profile ExtensionPrefStore. + PrefValueMap incognito_profile_preferences_persistent; + // Session only extension controlled preferences for the incognito profile. + // These preferences are deleted when the incognito profile is destroyed. + PrefValueMap incognito_profile_preferences_session_only; +}; + +ExtensionPrefValueMap::ExtensionPrefValueMap() : destroyed_(false) { +} + +ExtensionPrefValueMap::~ExtensionPrefValueMap() { + if (!destroyed_) { + NotifyOfDestruction(); + destroyed_ = true; + } + STLDeleteValues(&entries_); + entries_.clear(); +} + +void ExtensionPrefValueMap::Shutdown() { + NotifyOfDestruction(); + destroyed_ = true; +} + +void ExtensionPrefValueMap::SetExtensionPref(const std::string& ext_id, + const std::string& key, + ExtensionPrefsScope scope, + base::Value* value) { + PrefValueMap* prefs = GetExtensionPrefValueMap(ext_id, scope); + + if (prefs->SetValue(key, value)) + NotifyPrefValueChanged(key); +} + +void ExtensionPrefValueMap::RemoveExtensionPref( + const std::string& ext_id, + const std::string& key, + ExtensionPrefsScope scope) { + PrefValueMap* prefs = GetExtensionPrefValueMap(ext_id, scope); + if (prefs->RemoveValue(key)) + NotifyPrefValueChanged(key); +} + +bool ExtensionPrefValueMap::CanExtensionControlPref( + const std::string& extension_id, + const std::string& pref_key, + bool incognito) const { + ExtensionEntryMap::const_iterator ext = entries_.find(extension_id); + if (ext == entries_.end()) { + NOTREACHED(); + return false; + } + + ExtensionEntryMap::const_iterator winner = + GetEffectivePrefValueController(pref_key, incognito, NULL); + if (winner == entries_.end()) + return true; + + return winner->second->install_time <= ext->second->install_time; +} + +void ExtensionPrefValueMap::ClearAllIncognitoSessionOnlyPreferences() { + typedef std::set<std::string> KeySet; + KeySet deleted_keys; + + ExtensionEntryMap::iterator i; + for (i = entries_.begin(); i != entries_.end(); ++i) { + PrefValueMap& inc_prefs = + i->second->incognito_profile_preferences_session_only; + PrefValueMap::iterator j; + for (j = inc_prefs.begin(); j != inc_prefs.end(); ++j) + deleted_keys.insert(j->first); + inc_prefs.Clear(); + } + + KeySet::iterator k; + for (k = deleted_keys.begin(); k != deleted_keys.end(); ++k) + NotifyPrefValueChanged(*k); +} + +bool ExtensionPrefValueMap::DoesExtensionControlPref( + const std::string& extension_id, + const std::string& pref_key, + bool* from_incognito) const { + bool incognito = (from_incognito != NULL); + ExtensionEntryMap::const_iterator winner = + GetEffectivePrefValueController(pref_key, incognito, from_incognito); + if (winner == entries_.end()) + return false; + return winner->first == extension_id; +} + +void ExtensionPrefValueMap::RegisterExtension(const std::string& ext_id, + const base::Time& install_time, + bool is_enabled) { + if (entries_.find(ext_id) == entries_.end()) { + entries_[ext_id] = new ExtensionEntry; + + // Only update the install time if the extension is newly installed. + entries_[ext_id]->install_time = install_time; + } + + entries_[ext_id]->enabled = is_enabled; +} + +void ExtensionPrefValueMap::UnregisterExtension(const std::string& ext_id) { + ExtensionEntryMap::iterator i = entries_.find(ext_id); + if (i == entries_.end()) + return; + std::set<std::string> keys; // keys set by this extension + GetExtensionControlledKeys(*(i->second), &keys); + + delete i->second; + entries_.erase(i); + + NotifyPrefValueChanged(keys); +} + +void ExtensionPrefValueMap::SetExtensionState(const std::string& ext_id, + bool is_enabled) { + ExtensionEntryMap::const_iterator i = entries_.find(ext_id); + // This may happen when sync sets the extension state for an + // extension that is not installed. + if (i == entries_.end()) + return; + if (i->second->enabled == is_enabled) + return; + std::set<std::string> keys; // keys set by this extension + GetExtensionControlledKeys(*(i->second), &keys); + i->second->enabled = is_enabled; + NotifyPrefValueChanged(keys); +} + +PrefValueMap* ExtensionPrefValueMap::GetExtensionPrefValueMap( + const std::string& ext_id, + ExtensionPrefsScope scope) { + ExtensionEntryMap::const_iterator i = entries_.find(ext_id); + CHECK(i != entries_.end()); + switch (scope) { + case extensions::kExtensionPrefsScopeRegular: + return &(i->second->regular_profile_preferences); + case extensions::kExtensionPrefsScopeRegularOnly: + return &(i->second->regular_only_profile_preferences); + case extensions::kExtensionPrefsScopeIncognitoPersistent: + return &(i->second->incognito_profile_preferences_persistent); + case extensions::kExtensionPrefsScopeIncognitoSessionOnly: + return &(i->second->incognito_profile_preferences_session_only); + } + NOTREACHED(); + return NULL; +} + +const PrefValueMap* ExtensionPrefValueMap::GetExtensionPrefValueMap( + const std::string& ext_id, + ExtensionPrefsScope scope) const { + ExtensionEntryMap::const_iterator i = entries_.find(ext_id); + CHECK(i != entries_.end()); + switch (scope) { + case extensions::kExtensionPrefsScopeRegular: + return &(i->second->regular_profile_preferences); + case extensions::kExtensionPrefsScopeRegularOnly: + return &(i->second->regular_only_profile_preferences); + case extensions::kExtensionPrefsScopeIncognitoPersistent: + return &(i->second->incognito_profile_preferences_persistent); + case extensions::kExtensionPrefsScopeIncognitoSessionOnly: + return &(i->second->incognito_profile_preferences_session_only); + } + NOTREACHED(); + return NULL; +} + +void ExtensionPrefValueMap::GetExtensionControlledKeys( + const ExtensionEntry& entry, + std::set<std::string>* out) const { + PrefValueMap::const_iterator i; + + const PrefValueMap& regular_prefs = entry.regular_profile_preferences; + for (i = regular_prefs.begin(); i != regular_prefs.end(); ++i) + out->insert(i->first); + + const PrefValueMap& regular_only_prefs = + entry.regular_only_profile_preferences; + for (i = regular_only_prefs.begin(); i != regular_only_prefs.end(); ++i) + out->insert(i->first); + + const PrefValueMap& inc_prefs_pers = + entry.incognito_profile_preferences_persistent; + for (i = inc_prefs_pers.begin(); i != inc_prefs_pers.end(); ++i) + out->insert(i->first); + + const PrefValueMap& inc_prefs_session = + entry.incognito_profile_preferences_session_only; + for (i = inc_prefs_session.begin(); i != inc_prefs_session.end(); ++i) + out->insert(i->first); +} + +const base::Value* ExtensionPrefValueMap::GetEffectivePrefValue( + const std::string& key, + bool incognito, + bool* from_incognito) const { + ExtensionEntryMap::const_iterator winner = + GetEffectivePrefValueController(key, incognito, from_incognito); + if (winner == entries_.end()) + return NULL; + + const base::Value* value = NULL; + const std::string& ext_id = winner->first; + + // First search for incognito session only preferences. + if (incognito) { + const PrefValueMap* prefs = GetExtensionPrefValueMap( + ext_id, extensions::kExtensionPrefsScopeIncognitoSessionOnly); + prefs->GetValue(key, &value); + if (value) + return value; + + // If no incognito session only preference exists, fall back to persistent + // incognito preference. + prefs = GetExtensionPrefValueMap( + ext_id, + extensions::kExtensionPrefsScopeIncognitoPersistent); + prefs->GetValue(key, &value); + if (value) + return value; + } else { + // Regular-only preference. + const PrefValueMap* prefs = GetExtensionPrefValueMap( + ext_id, extensions::kExtensionPrefsScopeRegularOnly); + prefs->GetValue(key, &value); + if (value) + return value; + } + + // Regular preference. + const PrefValueMap* prefs = GetExtensionPrefValueMap( + ext_id, extensions::kExtensionPrefsScopeRegular); + prefs->GetValue(key, &value); + return value; +} + +ExtensionPrefValueMap::ExtensionEntryMap::const_iterator +ExtensionPrefValueMap::GetEffectivePrefValueController( + const std::string& key, + bool incognito, + bool* from_incognito) const { + ExtensionEntryMap::const_iterator winner = entries_.end(); + base::Time winners_install_time; + + ExtensionEntryMap::const_iterator i; + for (i = entries_.begin(); i != entries_.end(); ++i) { + const std::string& ext_id = i->first; + const base::Time& install_time = i->second->install_time; + const bool enabled = i->second->enabled; + + if (!enabled) + continue; + if (install_time < winners_install_time) + continue; + + const base::Value* value = NULL; + const PrefValueMap* prefs = GetExtensionPrefValueMap( + ext_id, extensions::kExtensionPrefsScopeRegular); + if (prefs->GetValue(key, &value)) { + winner = i; + winners_install_time = install_time; + if (from_incognito) + *from_incognito = false; + } + + if (!incognito) { + const PrefValueMap* prefs = GetExtensionPrefValueMap( + ext_id, extensions::kExtensionPrefsScopeRegularOnly); + if (prefs->GetValue(key, &value)) { + winner = i; + winners_install_time = install_time; + if (from_incognito) + *from_incognito = false; + } + // Ignore the following prefs, because they're incognito-only. + continue; + } + + prefs = GetExtensionPrefValueMap( + ext_id, extensions::kExtensionPrefsScopeIncognitoPersistent); + if (prefs->GetValue(key, &value)) { + winner = i; + winners_install_time = install_time; + if (from_incognito) + *from_incognito = true; + } + + prefs = GetExtensionPrefValueMap( + ext_id, extensions::kExtensionPrefsScopeIncognitoSessionOnly); + if (prefs->GetValue(key, &value)) { + winner = i; + winners_install_time = install_time; + if (from_incognito) + *from_incognito = true; + } + } + return winner; +} + +void ExtensionPrefValueMap::AddObserver( + ExtensionPrefValueMap::Observer* observer) { + observers_.AddObserver(observer); + + // Collect all currently used keys and notify the new observer. + std::set<std::string> keys; + ExtensionEntryMap::const_iterator i; + for (i = entries_.begin(); i != entries_.end(); ++i) + GetExtensionControlledKeys(*(i->second), &keys); + + std::set<std::string>::const_iterator j; + for (j = keys.begin(); j != keys.end(); ++j) + observer->OnPrefValueChanged(*j); +} + +void ExtensionPrefValueMap::RemoveObserver( + ExtensionPrefValueMap::Observer* observer) { + observers_.RemoveObserver(observer); +} + +std::string ExtensionPrefValueMap::GetExtensionControllingPref( + const std::string& pref_key) const { + ExtensionEntryMap::const_iterator winner = + GetEffectivePrefValueController(pref_key, false, NULL); + if (winner == entries_.end()) + return std::string(); + return winner->first; +} + +void ExtensionPrefValueMap::NotifyInitializationCompleted() { + FOR_EACH_OBSERVER(ExtensionPrefValueMap::Observer, observers_, + OnInitializationCompleted()); +} + +void ExtensionPrefValueMap::NotifyPrefValueChanged( + const std::set<std::string>& keys) { + std::set<std::string>::const_iterator i; + for (i = keys.begin(); i != keys.end(); ++i) + NotifyPrefValueChanged(*i); +} + +void ExtensionPrefValueMap::NotifyPrefValueChanged(const std::string& key) { + FOR_EACH_OBSERVER(ExtensionPrefValueMap::Observer, observers_, + OnPrefValueChanged(key)); +} + +void ExtensionPrefValueMap::NotifyOfDestruction() { + FOR_EACH_OBSERVER(ExtensionPrefValueMap::Observer, observers_, + OnExtensionPrefValueMapDestruction()); +} diff --git a/extensions/browser/extension_pref_value_map.h b/extensions/browser/extension_pref_value_map.h new file mode 100644 index 0000000000..48f30d9918 --- /dev/null +++ b/extensions/browser/extension_pref_value_map.h @@ -0,0 +1,198 @@ +// Copyright 2014 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_PREF_VALUE_MAP_H_ +#define EXTENSIONS_BROWSER_EXTENSION_PREF_VALUE_MAP_H_ + +#include <map> +#include <set> +#include <string> + +#include "base/observer_list.h" +#include "base/prefs/pref_value_map.h" +#include "base/time/time.h" +#include "base/values.h" +#include "components/browser_context_keyed_service/browser_context_keyed_service.h" +#include "extensions/browser/extension_prefs_scope.h" + +// Non-persistent data container that is shared by ExtensionPrefStores. All +// extension pref values (incognito and regular) are stored herein and +// provided to ExtensionPrefStores. +// +// The semantics of the ExtensionPrefValueMap are: +// - A regular setting applies to regular browsing sessions as well as incognito +// browsing sessions. +// - An incognito setting applies only to incognito browsing sessions, not to +// regular ones. It takes precedence over a regular setting set by the same +// extension. +// - A regular-only setting applies only to regular browsing sessions, not to +// incognito ones. It takes precedence over a regular setting set by the same +// extension. +// - If two different extensions set a value for the same preference (and both +// values apply to the regular/incognito browsing session), the extension that +// was installed later takes precedence, regardless of whether the settings +// are regular, incognito or regular-only. +// +// The following table illustrates the behavior: +// A.reg | A.reg_only | A.inc | B.reg | B.reg_only | B.inc | E.reg | E.inc +// 1 | - | - | - | - | - | 1 | 1 +// 1 | 2 | - | - | - | - | 2 | 1 +// 1 | - | 3 | - | - | - | 1 | 3 +// 1 | 2 | 3 | - | - | - | 2 | 3 +// 1 | - | - | 4 | - | - | 4 | 4 +// 1 | 2 | 3 | 4 | - | - | 4 | 4 +// 1 | - | - | - | 5 | - | 5 | 1 +// 1 | - | 3 | 4 | 5 | - | 5 | 4 +// 1 | - | - | - | - | 6 | 1 | 6 +// 1 | 2 | - | 4 | - | 6 | 4 | 6 +// 1 | 2 | 3 | - | 5 | 6 | 5 | 6 +// +// A = extension A, B = extension B, E = effective value +// .reg = regular value +// .reg_only = regular-only value +// .inc = incognito value +// Extension B has higher precedence than A. +class ExtensionPrefValueMap : public BrowserContextKeyedService { + public: + // Observer interface for monitoring ExtensionPrefValueMap. + class Observer { + public: + // Called when the value for the given |key| set by one of the extensions + // changes. This does not necessarily mean that the effective value has + // changed. + virtual void OnPrefValueChanged(const std::string& key) = 0; + // Notification about the ExtensionPrefValueMap being fully initialized. + virtual void OnInitializationCompleted() = 0; + // Called when the ExtensionPrefValueMap is being destroyed. When called, + // observers must unsubscribe. + virtual void OnExtensionPrefValueMapDestruction() = 0; + + protected: + virtual ~Observer() {} + }; + + ExtensionPrefValueMap(); + virtual ~ExtensionPrefValueMap(); + + // BrowserContextKeyedService implementation. + virtual void Shutdown() OVERRIDE; + + // Set an extension preference |value| for |key| of extension |ext_id|. + // Takes ownership of |value|. + // Note that regular extension pref values need to be reported to + // incognito and to regular ExtensionPrefStores. + // Precondition: the extension must be registered. + void SetExtensionPref(const std::string& ext_id, + const std::string& key, + extensions::ExtensionPrefsScope scope, + base::Value* value); + + // Remove the extension preference value for |key| of extension |ext_id|. + // Precondition: the extension must be registered. + void RemoveExtensionPref(const std::string& ext_id, + const std::string& key, + extensions::ExtensionPrefsScope scope); + + // Returns true if currently no extension with higher precedence controls the + // preference. + // Note that the this function does does not consider the existence of + // policies. An extension is only really able to control a preference if + // PrefService::Preference::IsExtensionModifiable() returns true as well. + bool CanExtensionControlPref(const std::string& extension_id, + const std::string& pref_key, + bool incognito) const; + + // Removes all "incognito session only" preference values. + void ClearAllIncognitoSessionOnlyPreferences(); + + // Returns true if an extension identified by |extension_id| controls the + // preference. This means this extension has set a preference value and no + // other extension with higher precedence overrides it. If |from_incognito| + // is not NULL, looks at incognito preferences first, and |from_incognito| is + // set to true if the effective pref value is coming from the incognito + // preferences, false if it is coming from the normal ones. + // Note that the this function does does not consider the existence of + // policies. An extension is only really able to control a preference if + // PrefService::Preference::IsExtensionModifiable() returns true as well. + bool DoesExtensionControlPref(const std::string& extension_id, + const std::string& pref_key, + bool* from_incognito) const; + + // Returns the ID of the extension that currently controls this preference. + // Returns an empty string if this preference is not controlled by an + // extension. + std::string GetExtensionControllingPref(const std::string& pref_key) const; + + // Tell the store it's now fully initialized. + void NotifyInitializationCompleted(); + + // Registers the time when an extension |ext_id| is installed. + void RegisterExtension(const std::string& ext_id, + const base::Time& install_time, + bool is_enabled); + + // Deletes all entries related to extension |ext_id|. + void UnregisterExtension(const std::string& ext_id); + + // Hides or makes the extension preference values of the specified extension + // visible. + void SetExtensionState(const std::string& ext_id, bool is_enabled); + + // Adds an observer and notifies it about the currently stored keys. + void AddObserver(Observer* observer); + + void RemoveObserver(Observer* observer); + + const base::Value* GetEffectivePrefValue(const std::string& key, + bool incognito, + bool* from_incognito) const; + + private: + struct ExtensionEntry; + + typedef std::map<std::string, ExtensionEntry*> ExtensionEntryMap; + + const PrefValueMap* GetExtensionPrefValueMap( + const std::string& ext_id, + extensions::ExtensionPrefsScope scope) const; + + PrefValueMap* GetExtensionPrefValueMap( + const std::string& ext_id, + extensions::ExtensionPrefsScope scope); + + // Returns all keys of pref values that are set by the extension of |entry|, + // regardless whether they are set for incognito or regular pref values. + void GetExtensionControlledKeys(const ExtensionEntry& entry, + std::set<std::string>* out) const; + + // Returns an iterator to the extension which controls the preference |key|. + // If |incognito| is true, looks at incognito preferences first. In that case, + // if |from_incognito| is not NULL, it is set to true if the effective pref + // value is coming from the incognito preferences, false if it is coming from + // the normal ones. + ExtensionEntryMap::const_iterator GetEffectivePrefValueController( + const std::string& key, + bool incognito, + bool* from_incognito) const; + + void NotifyOfDestruction(); + void NotifyPrefValueChanged(const std::string& key); + void NotifyPrefValueChanged(const std::set<std::string>& keys); + + // Mapping of which extension set which preference value. The effective + // preferences values (i.e. the ones with the highest precedence) + // are stored in ExtensionPrefStores. + ExtensionEntryMap entries_; + + // In normal Profile shutdown, Shutdown() notifies observers that we are + // being destroyed. In tests, it isn't called, so the notification must + // be done in the destructor. This bit tracks whether it has been done yet. + bool destroyed_; + + ObserverList<Observer, true> observers_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionPrefValueMap); +}; + +#endif // EXTENSIONS_BROWSER_EXTENSION_PREF_VALUE_MAP_H_ diff --git a/extensions/browser/extension_pref_value_map_factory.cc b/extensions/browser/extension_pref_value_map_factory.cc new file mode 100644 index 0000000000..c8d3bfbac5 --- /dev/null +++ b/extensions/browser/extension_pref_value_map_factory.cc @@ -0,0 +1,35 @@ +// Copyright 2014 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_pref_value_map_factory.h" + +#include "components/browser_context_keyed_service/browser_context_dependency_manager.h" +#include "extensions/browser/extension_pref_value_map.h" + +ExtensionPrefValueMapFactory::ExtensionPrefValueMapFactory() + : BrowserContextKeyedServiceFactory( + "ExtensionPrefValueMap", + BrowserContextDependencyManager::GetInstance()) { +} + +ExtensionPrefValueMapFactory::~ExtensionPrefValueMapFactory() { +} + +// static +ExtensionPrefValueMap* ExtensionPrefValueMapFactory::GetForBrowserContext( + content::BrowserContext* context) { + return static_cast<ExtensionPrefValueMap*>( + GetInstance()->GetServiceForBrowserContext(context, true)); +} + +// static +ExtensionPrefValueMapFactory* ExtensionPrefValueMapFactory::GetInstance() { + return Singleton<ExtensionPrefValueMapFactory>::get(); +} + +BrowserContextKeyedService* +ExtensionPrefValueMapFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + return new ExtensionPrefValueMap(); +} diff --git a/extensions/browser/extension_pref_value_map_factory.h b/extensions/browser/extension_pref_value_map_factory.h new file mode 100644 index 0000000000..84537d3038 --- /dev/null +++ b/extensions/browser/extension_pref_value_map_factory.h @@ -0,0 +1,31 @@ +// Copyright 2014 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_PREF_VALUE_MAP_FACTORY_H_ +#define EXTENSIONS_BROWSER_EXTENSION_PREF_VALUE_MAP_FACTORY_H_ + +#include "base/memory/singleton.h" +#include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h" + +class ExtensionPrefValueMap; + +// The usual factory boilerplate for ExtensionPrefValueMap. +class ExtensionPrefValueMapFactory : public BrowserContextKeyedServiceFactory { + public: + static ExtensionPrefValueMap* GetForBrowserContext( + content::BrowserContext* context); + + static ExtensionPrefValueMapFactory* GetInstance(); + + private: + friend struct DefaultSingletonTraits<ExtensionPrefValueMapFactory>; + + ExtensionPrefValueMapFactory(); + virtual ~ExtensionPrefValueMapFactory(); + + virtual BrowserContextKeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const OVERRIDE; +}; + +#endif // EXTENSIONS_BROWSER_EXTENSION_PREF_VALUE_MAP_FACTORY_H_ diff --git a/extensions/browser/extension_pref_value_map_unittest.cc b/extensions/browser/extension_pref_value_map_unittest.cc new file mode 100644 index 0000000000..35fa3c6b4b --- /dev/null +++ b/extensions/browser/extension_pref_value_map_unittest.cc @@ -0,0 +1,400 @@ +// Copyright 2014 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 "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/prefs/pref_store_observer_mock.h" +#include "base/values.h" +#include "extensions/browser/extension_pref_value_map.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { +const char kExt1[] = "ext1"; +const char kExt2[] = "ext2"; +const char kExt3[] = "ext3"; + +const char kPref1[] = "path1.subpath"; +const char kPref2[] = "path2"; +const char kPref3[] = "path3"; +const char kPref4[] = "path4"; +} // namespace + +static base::Value* CreateVal(const char* str) { + return new base::StringValue(str); +} + +static base::Time CreateTime(int64 t) { + return base::Time::FromInternalValue(t); +} + +template <typename BASECLASS> +class ExtensionPrefValueMapTestBase : public BASECLASS { + public: + static const extensions::ExtensionPrefsScope kRegular = + extensions::kExtensionPrefsScopeRegular; + static const extensions::ExtensionPrefsScope kRegularOnly = + extensions::kExtensionPrefsScopeRegularOnly; + static const extensions::ExtensionPrefsScope kIncognitoPersistent = + extensions::kExtensionPrefsScopeIncognitoPersistent; + static const extensions::ExtensionPrefsScope kIncognitoSessionOnly = + extensions::kExtensionPrefsScopeIncognitoSessionOnly; + + // Returns an empty string if the key is not set. + std::string GetValue(const char * key, bool incognito) const { + const base::Value *value = + epvm_.GetEffectivePrefValue(key, incognito, NULL); + std::string string_value; + if (value) + value->GetAsString(&string_value); + return string_value; + } + + protected: + ExtensionPrefValueMap epvm_; +}; + +class ExtensionPrefValueMapTest + : public ExtensionPrefValueMapTestBase<testing::Test> { +}; + +// A gmock-ified implementation of PrefStore::Observer. +class ExtensionPrefValueMapObserverMock + : public ExtensionPrefValueMap::Observer { + public: + ExtensionPrefValueMapObserverMock() {} + virtual ~ExtensionPrefValueMapObserverMock() {} + + MOCK_METHOD1(OnPrefValueChanged, void(const std::string&)); + MOCK_METHOD0(OnInitializationCompleted, void()); + MOCK_METHOD0(OnExtensionPrefValueMapDestruction, void()); + + private: + DISALLOW_COPY_AND_ASSIGN(ExtensionPrefValueMapObserverMock); +}; + +TEST_F(ExtensionPrefValueMapTest, SetAndGetPrefValue) { + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val1")); + EXPECT_EQ("val1", GetValue(kPref1, false)); +}; + +TEST_F(ExtensionPrefValueMapTest, GetNotSetPrefValue) { + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + EXPECT_EQ("", GetValue(kPref1, false)); +}; + +// Make sure the last-installed extension wins for each preference. +TEST_F(ExtensionPrefValueMapTest, Override) { + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + epvm_.RegisterExtension(kExt2, CreateTime(20), true); + epvm_.RegisterExtension(kExt3, CreateTime(30), true); + + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val1")); + epvm_.SetExtensionPref(kExt2, kPref1, kRegular, CreateVal("val2")); + epvm_.SetExtensionPref(kExt3, kPref1, kRegular, CreateVal("val3")); + + epvm_.SetExtensionPref(kExt1, kPref2, kRegular, CreateVal("val4")); + epvm_.SetExtensionPref(kExt2, kPref2, kRegular, CreateVal("val5")); + + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val6")); + epvm_.SetExtensionPref(kExt1, kPref2, kRegular, CreateVal("val7")); + epvm_.SetExtensionPref(kExt1, kPref3, kRegular, CreateVal("val8")); + + EXPECT_EQ("val3", GetValue(kPref1, false)); + EXPECT_EQ("val5", GetValue(kPref2, false)); + EXPECT_EQ("val8", GetValue(kPref3, false)); +} + +TEST_F(ExtensionPrefValueMapTest, OverrideChecks) { + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + epvm_.RegisterExtension(kExt2, CreateTime(20), true); + epvm_.RegisterExtension(kExt3, CreateTime(30), true); + + EXPECT_FALSE(epvm_.DoesExtensionControlPref(kExt1, kPref1, NULL)); + EXPECT_FALSE(epvm_.DoesExtensionControlPref(kExt2, kPref1, NULL)); + EXPECT_FALSE(epvm_.DoesExtensionControlPref(kExt3, kPref1, NULL)); + EXPECT_TRUE(epvm_.CanExtensionControlPref(kExt1, kPref1, false)); + EXPECT_TRUE(epvm_.CanExtensionControlPref(kExt2, kPref1, false)); + EXPECT_TRUE(epvm_.CanExtensionControlPref(kExt3, kPref1, false)); + + epvm_.SetExtensionPref(kExt2, kPref1, kRegular, CreateVal("val1")); + + EXPECT_FALSE(epvm_.DoesExtensionControlPref(kExt1, kPref1, NULL)); + EXPECT_TRUE(epvm_.DoesExtensionControlPref(kExt2, kPref1, NULL)); + EXPECT_FALSE(epvm_.DoesExtensionControlPref(kExt3, kPref1, NULL)); + EXPECT_FALSE(epvm_.CanExtensionControlPref(kExt1, kPref1, false)); + EXPECT_TRUE(epvm_.CanExtensionControlPref(kExt2, kPref1, false)); + EXPECT_TRUE(epvm_.CanExtensionControlPref(kExt3, kPref1, false)); +} + +TEST_F(ExtensionPrefValueMapTest, SetAndGetPrefValueIncognito) { + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val1")); + EXPECT_EQ("val1", GetValue(kPref1, true)); +} + +TEST_F(ExtensionPrefValueMapTest, UninstallOnlyExtension) { + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val1")); + epvm_.UnregisterExtension(kExt1); + + EXPECT_EQ("", GetValue(kPref1, false)); +} + +// Tests uninstalling an extension that wasn't winning for any preferences. +TEST_F(ExtensionPrefValueMapTest, UninstallIrrelevantExtension) { + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + epvm_.RegisterExtension(kExt2, CreateTime(10), true); + + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val1")); + epvm_.SetExtensionPref(kExt2, kPref1, kRegular, CreateVal("val2")); + + epvm_.SetExtensionPref(kExt1, kPref2, kRegular, CreateVal("val3")); + epvm_.SetExtensionPref(kExt2, kPref2, kRegular, CreateVal("val4")); + + epvm_.UnregisterExtension(kExt1); + + EXPECT_EQ("val2", GetValue(kPref1, false)); + EXPECT_EQ("val4", GetValue(kPref2, false)); +} + +// Tests uninstalling an extension that was winning for all preferences. +TEST_F(ExtensionPrefValueMapTest, UninstallExtensionFromTop) { + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + epvm_.RegisterExtension(kExt2, CreateTime(20), true); + epvm_.RegisterExtension(kExt3, CreateTime(30), true); + + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val1")); + epvm_.SetExtensionPref(kExt2, kPref1, kRegular, CreateVal("val2")); + epvm_.SetExtensionPref(kExt3, kPref1, kRegular, CreateVal("val3")); + + epvm_.SetExtensionPref(kExt1, kPref2, kRegular, CreateVal("val4")); + epvm_.SetExtensionPref(kExt3, kPref2, kRegular, CreateVal("val5")); + + epvm_.UnregisterExtension(kExt3); + + EXPECT_EQ("val2", GetValue(kPref1, false)); + EXPECT_EQ("val4", GetValue(kPref2, false)); +} + +// Tests uninstalling an extension that was winning for only some preferences. +TEST_F(ExtensionPrefValueMapTest, UninstallExtensionFromMiddle) { + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + epvm_.RegisterExtension(kExt2, CreateTime(20), true); + epvm_.RegisterExtension(kExt3, CreateTime(30), true); + + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val1")); + epvm_.SetExtensionPref(kExt2, kPref1, kRegular, CreateVal("val2")); + epvm_.SetExtensionPref(kExt3, kPref1, kRegular, CreateVal("val3")); + + epvm_.SetExtensionPref(kExt1, kPref2, kRegular, CreateVal("val4")); + epvm_.SetExtensionPref(kExt2, kPref2, kRegular, CreateVal("val5")); + + epvm_.SetExtensionPref(kExt1, kPref3, kRegular, CreateVal("val6")); + + epvm_.SetExtensionPref(kExt2, kPref4, kRegular, CreateVal("val7")); + + epvm_.UnregisterExtension(kExt2); + + EXPECT_EQ("val3", GetValue(kPref1, false)); + EXPECT_EQ("val4", GetValue(kPref2, false)); + EXPECT_EQ("val6", GetValue(kPref3, false)); + EXPECT_EQ("", GetValue(kPref4, false)); +} + +// Tests triggering of notifications to registered observers. +TEST_F(ExtensionPrefValueMapTest, NotifyWhenNeeded) { + using testing::Mock; + using testing::StrEq; + + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + + ExtensionPrefValueMapObserverMock observer; + epvm_.AddObserver(&observer); + + EXPECT_CALL(observer, OnPrefValueChanged(std::string(kPref1))); + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val1")); + Mock::VerifyAndClearExpectations(&observer); + + // Write the same value again. + EXPECT_CALL(observer, OnPrefValueChanged(std::string(kPref1))).Times(0); + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val1")); + Mock::VerifyAndClearExpectations(&observer); + + // Override incognito value. + EXPECT_CALL(observer, OnPrefValueChanged(std::string(kPref1))); + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val2")); + Mock::VerifyAndClearExpectations(&observer); + + // Override non-incognito value. + EXPECT_CALL(observer, OnPrefValueChanged(std::string(kPref1))); + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val3")); + Mock::VerifyAndClearExpectations(&observer); + + // Disable. + EXPECT_CALL(observer, OnPrefValueChanged(std::string(kPref1))); + epvm_.SetExtensionState(kExt1, false); + Mock::VerifyAndClearExpectations(&observer); + + // Enable. + EXPECT_CALL(observer, OnPrefValueChanged(std::string(kPref1))); + epvm_.SetExtensionState(kExt1, true); + Mock::VerifyAndClearExpectations(&observer); + + // Uninstall + EXPECT_CALL(observer, OnPrefValueChanged(std::string(kPref1))); + epvm_.UnregisterExtension(kExt1); + Mock::VerifyAndClearExpectations(&observer); + + epvm_.RemoveObserver(&observer); + + // Write new value --> no notification after removing observer. + EXPECT_CALL(observer, OnPrefValueChanged(std::string(kPref1))).Times(0); + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val4")); + Mock::VerifyAndClearExpectations(&observer); +} + +// Tests disabling an extension. +TEST_F(ExtensionPrefValueMapTest, DisableExt) { + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val1")); + epvm_.SetExtensionState(kExt1, false); + EXPECT_EQ("", GetValue(kPref1, false)); +} + +// Tests disabling and reenabling an extension. +TEST_F(ExtensionPrefValueMapTest, ReenableExt) { + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, CreateVal("val1")); + epvm_.SetExtensionState(kExt1, false); + epvm_.SetExtensionState(kExt1, true); + EXPECT_EQ("val1", GetValue(kPref1, false)); +} + +struct OverrideIncognitoTestCase { + OverrideIncognitoTestCase(int val_ext1_regular, + int val_ext1_regular_only, + int val_ext1_incognito_pers, + int val_ext1_incognito_sess, + int val_ext2_regular, + int val_ext2_regular_only, + int val_ext2_incognito_pers, + int val_ext2_incognito_sess, + int effective_value_regular, + int effective_value_incognito) + : val_ext1_regular_(val_ext1_regular), + val_ext1_regular_only_(val_ext1_regular_only), + val_ext1_incognito_pers_(val_ext1_incognito_pers), + val_ext1_incognito_sess_(val_ext1_incognito_sess), + val_ext2_regular_(val_ext2_regular), + val_ext2_regular_only_(val_ext2_regular_only), + val_ext2_incognito_pers_(val_ext2_incognito_pers), + val_ext2_incognito_sess_(val_ext2_incognito_sess), + effective_value_regular_(effective_value_regular), + effective_value_incognito_(effective_value_incognito) {} + + // pers. = persistent + // sess. = session only + int val_ext1_regular_; // pref value of extension 1 + int val_ext1_regular_only_; // pref value of extension 1 regular-only. + int val_ext1_incognito_pers_; // pref value of extension 1 incognito pers. + int val_ext1_incognito_sess_; // pref value of extension 1 incognito sess. + int val_ext2_regular_; // pref value of extension 2 + int val_ext2_regular_only_; // pref value of extension 2 regular-only. + int val_ext2_incognito_pers_; // pref value of extension 2 incognito pers. + int val_ext2_incognito_sess_; // pref value of extension 2 incognito sess. + int effective_value_regular_; // desired winner regular + int effective_value_incognito_; // desired winner incognito +}; + +class ExtensionPrefValueMapTestIncognitoTests + : public ExtensionPrefValueMapTestBase< + testing::TestWithParam<OverrideIncognitoTestCase> > { +}; + +TEST_P(ExtensionPrefValueMapTestIncognitoTests, OverrideIncognito) { + OverrideIncognitoTestCase test = GetParam(); + const char* strings[] = { + "undefined", + "val1", + "val2", + "val3", + "val4", + "val5", + "val6", + "val7", + "val8", + }; + + epvm_.RegisterExtension(kExt1, CreateTime(10), true); + epvm_.RegisterExtension(kExt2, CreateTime(20), true); + if (test.val_ext1_regular_) { + epvm_.SetExtensionPref(kExt1, kPref1, kRegular, + CreateVal(strings[test.val_ext1_regular_])); + } + if (test.val_ext1_regular_only_) { + epvm_.SetExtensionPref(kExt1, kPref1, kRegularOnly, + CreateVal(strings[test.val_ext1_regular_only_])); + } + if (test.val_ext1_incognito_pers_) { + epvm_.SetExtensionPref(kExt1, kPref1, kIncognitoPersistent, + CreateVal(strings[test.val_ext1_incognito_pers_])); + } + if (test.val_ext1_incognito_sess_) { + epvm_.SetExtensionPref(kExt1, kPref1, kIncognitoSessionOnly, + CreateVal(strings[test.val_ext1_incognito_sess_])); + } + if (test.val_ext2_regular_) { + epvm_.SetExtensionPref(kExt2, kPref1, kRegular, + CreateVal(strings[test.val_ext2_regular_])); + } + if (test.val_ext2_regular_only_) { + epvm_.SetExtensionPref(kExt2, kPref1, kRegularOnly, + CreateVal(strings[test.val_ext2_regular_only_])); + } + if (test.val_ext2_incognito_pers_) { + epvm_.SetExtensionPref(kExt2, kPref1, kIncognitoPersistent, + CreateVal(strings[test.val_ext2_incognito_pers_])); + } + if (test.val_ext2_incognito_sess_) { + epvm_.SetExtensionPref(kExt2, kPref1, kIncognitoSessionOnly, + CreateVal(strings[test.val_ext2_incognito_sess_])); + } + std::string actual; + EXPECT_EQ(strings[test.effective_value_regular_], GetValue(kPref1, false)); + EXPECT_EQ(strings[test.effective_value_incognito_], GetValue(kPref1, true)); + epvm_.UnregisterExtension(kExt1); + epvm_.UnregisterExtension(kExt2); +} + +INSTANTIATE_TEST_CASE_P( + ExtensionPrefValueMapTestIncognitoTestsInstance, + ExtensionPrefValueMapTestIncognitoTests, + testing::Values( + // e.g. (1, 0, 0, 0, 0, 0, 7, 0, 1, 7), means: + // ext1 regular is set to "val1", ext2 incognito persistent is set to + // "val7" + // --> the winning regular value is "val1", the winning incognito + // value is "val7". + OverrideIncognitoTestCase(1, 0, 0, 0, 0, 0, 0, 0, 1, 1), + OverrideIncognitoTestCase(1, 2, 0, 0, 0, 0, 0, 0, 2, 1), + OverrideIncognitoTestCase(1, 0, 3, 0, 0, 0, 0, 0, 1, 3), + OverrideIncognitoTestCase(1, 0, 0, 4, 0, 0, 0, 0, 1, 4), + OverrideIncognitoTestCase(1, 0, 3, 4, 0, 0, 0, 0, 1, 4), + OverrideIncognitoTestCase(1, 2, 3, 0, 0, 0, 0, 0, 2, 3), + OverrideIncognitoTestCase(1, 0, 0, 0, 5, 0, 0, 0, 5, 5), + OverrideIncognitoTestCase(1, 2, 3, 0, 5, 0, 0, 0, 5, 5), + OverrideIncognitoTestCase(1, 0, 0, 0, 0, 6, 0, 0, 6, 1), + OverrideIncognitoTestCase(1, 0, 3, 0, 5, 6, 0, 0, 6, 5), + OverrideIncognitoTestCase(1, 0, 0, 4, 5, 6, 0, 0, 6, 5), + OverrideIncognitoTestCase(1, 0, 0, 0, 0, 0, 7, 0, 1, 7), + OverrideIncognitoTestCase(1, 2, 0, 0, 5, 0, 7, 0, 5, 7), + OverrideIncognitoTestCase(1, 2, 0, 0, 5, 0, 0, 8, 5, 8), + OverrideIncognitoTestCase(1, 2, 0, 0, 5, 0, 7, 8, 5, 8), + OverrideIncognitoTestCase(1, 2, 3, 0, 0, 6, 7, 0, 6, 7))); diff --git a/extensions/browser/extension_prefs.cc b/extensions/browser/extension_prefs.cc new file mode 100644 index 0000000000..8c26aa339d --- /dev/null +++ b/extensions/browser/extension_prefs.cc @@ -0,0 +1,1911 @@ +// Copyright 2014 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_prefs.h" + +#include <iterator> + +#include "base/command_line.h" +#include "base/prefs/pref_notifier.h" +#include "base/prefs/pref_service.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/value_conversions.h" +#include "chrome/browser/extensions/api/content_settings/content_settings_store.h" +#include "chrome/browser/extensions/api/preference/preference_api.h" +#include "components/user_prefs/pref_registry_syncable.h" +#include "extensions/browser/admin_policy.h" +#include "extensions/browser/app_sorting.h" +#include "extensions/browser/event_router.h" +#include "extensions/browser/extension_pref_store.h" +#include "extensions/browser/extension_prefs_factory.h" +#include "extensions/browser/pref_names.h" +#include "extensions/common/feature_switch.h" +#include "extensions/common/manifest.h" +#include "extensions/common/permissions/permission_set.h" +#include "extensions/common/permissions/permissions_info.h" +#include "extensions/common/url_pattern.h" +#include "extensions/common/user_script.h" +#include "ui/base/l10n/l10n_util.h" + +using base::Value; +using base::DictionaryValue; +using base::ListValue; + +namespace extensions { + +namespace { + +// Additional preferences keys, which are not needed by external clients. + +// True if this extension is running. Note this preference stops getting updated +// during Chrome shutdown (and won't be updated on a browser crash) and so can +// be used at startup to determine whether the extension was running when Chrome +// was last terminated. +const char kPrefRunning[] = "running"; + +// Whether this extension had windows when it was last running. +const char kIsActive[] = "is_active"; + +// Where an extension was installed from. (see Manifest::Location) +const char kPrefLocation[] = "location"; + +// Enabled, disabled, killed, etc. (see Extension::State) +const char kPrefState[] = "state"; + +// The path to the current version's manifest file. +const char kPrefPath[] = "path"; + +// The dictionary containing the extension's manifest. +const char kPrefManifest[] = "manifest"; + +// The version number. +const char kPrefVersion[] = "manifest.version"; + +// Indicates whether an extension is blacklisted. +const char kPrefBlacklist[] = "blacklist"; + +// If extension is greylisted. +const char kPrefBlacklistState[] = "blacklist_state"; + +// The count of how many times we prompted the user to acknowledge an +// extension. +const char kPrefAcknowledgePromptCount[] = "ack_prompt_count"; + +// Indicates whether the user has acknowledged various types of extensions. +const char kPrefExternalAcknowledged[] = "ack_external"; +const char kPrefBlacklistAcknowledged[] = "ack_blacklist"; +const char kPrefWipeoutAcknowledged[] = "ack_wiped"; + +// Indicates whether the external extension was installed during the first +// run of this profile. +const char kPrefExternalInstallFirstRun[] = "external_first_run"; + +// Indicates whether to show an install warning when the user enables. +const char kExtensionDidEscalatePermissions[] = "install_warning_on_enable"; + +// DO NOT USE, use kPrefDisableReasons instead. +// Indicates whether the extension was updated while it was disabled. +const char kDeprecatedPrefDisableReason[] = "disable_reason"; + +// A bitmask of all the reasons an extension is disabled. +const char kPrefDisableReasons[] = "disable_reasons"; + +// The key for a serialized Time value indicating the start of the day (from the +// server's perspective) an extension last included a "ping" parameter during +// its update check. +const char kLastPingDay[] = "lastpingday"; + +// Similar to kLastPingDay, but for "active" instead of "rollcall" pings. +const char kLastActivePingDay[] = "last_active_pingday"; + +// A bit we use to keep track of whether we need to do an "active" ping. +const char kActiveBit[] = "active_bit"; + +// Path for settings specific to blacklist update. +const char kExtensionsBlacklistUpdate[] = "extensions.blacklistupdate"; + +// Path for the delayed install info dictionary preference. The actual string +// value is a legacy artifact for when delayed installs only pertained to +// updates that were waiting for idle. +const char kDelayedInstallInfo[] = "idle_install_info"; + +// Reason why the extension's install was delayed. +const char kDelayedInstallReason[] = "delay_install_reason"; + +// Path for the suggested page ordinal of a delayed extension install. +const char kPrefSuggestedPageOrdinal[] = "suggested_page_ordinal"; + +// A preference that, if true, will allow this extension to run in incognito +// mode. +const char kPrefIncognitoEnabled[] = "incognito"; + +// A preference to control whether an extension is allowed to inject script in +// pages with file URLs. +const char kPrefAllowFileAccess[] = "newAllowFileAccess"; +// TODO(jstritar): As part of fixing http://crbug.com/91577, we revoked all +// extension file access by renaming the pref. We should eventually clean up +// the old flag and possibly go back to that name. +// const char kPrefAllowFileAccessOld[] = "allowFileAccess"; + +// A preference specifying if the user dragged the app on the NTP. +const char kPrefUserDraggedApp[] = "user_dragged_app_ntp"; + +// Preferences that hold which permissions the user has granted the extension. +// We explicitly keep track of these so that extensions can contain unknown +// permissions, for backwards compatibility reasons, and we can still prompt +// the user to accept them once recognized. We store the active permission +// permissions because they may differ from those defined in the manifest. +const char kPrefActivePermissions[] = "active_permissions"; +const char kPrefGrantedPermissions[] = "granted_permissions"; + +// The preference names for PermissionSet values. +const char kPrefAPIs[] = "api"; +const char kPrefManifestPermissions[] = "manifest_permissions"; +const char kPrefExplicitHosts[] = "explicit_host"; +const char kPrefScriptableHosts[] = "scriptable_host"; + +// The preference names for the old granted permissions scheme. +const char kPrefOldGrantedFullAccess[] = "granted_permissions.full"; +const char kPrefOldGrantedHosts[] = "granted_permissions.host"; +const char kPrefOldGrantedAPIs[] = "granted_permissions.api"; + +// A preference that indicates when an extension was installed. +const char kPrefInstallTime[] = "install_time"; + +// A preference which saves the creation flags for extensions. +const char kPrefCreationFlags[] = "creation_flags"; + +// A preference that indicates whether the extension was installed from the +// Chrome Web Store. +const char kPrefFromWebStore[] = "from_webstore"; + +// A preference that indicates whether the extension was installed from a +// mock App created from a bookmark. +const char kPrefFromBookmark[] = "from_bookmark"; + +// A preference that indicates whether the extension was installed as +// default apps. +const char kPrefWasInstalledByDefault[] = "was_installed_by_default"; + +// Key for Geometry Cache preference. +const char kPrefGeometryCache[] = "geometry_cache"; + +// A preference that indicates when an extension is last launched. +const char kPrefLastLaunchTime[] = "last_launch_time"; + +// A list of installed ids and a signature. +const char kInstallSignature[] = "extensions.install_signature"; + +// Provider of write access to a dictionary storing extension prefs. +class ScopedExtensionPrefUpdate : public DictionaryPrefUpdate { + public: + ScopedExtensionPrefUpdate(PrefService* service, + const std::string& extension_id) : + DictionaryPrefUpdate(service, pref_names::kExtensions), + extension_id_(extension_id) {} + + virtual ~ScopedExtensionPrefUpdate() { + } + + // DictionaryPrefUpdate overrides: + virtual base::DictionaryValue* Get() OVERRIDE { + base::DictionaryValue* dict = DictionaryPrefUpdate::Get(); + base::DictionaryValue* extension = NULL; + if (!dict->GetDictionary(extension_id_, &extension)) { + // Extension pref does not exist, create it. + extension = new base::DictionaryValue(); + dict->SetWithoutPathExpansion(extension_id_, extension); + } + return extension; + } + + private: + const std::string extension_id_; + + DISALLOW_COPY_AND_ASSIGN(ScopedExtensionPrefUpdate); +}; + +std::string JoinPrefs(const std::string& parent, const char* child) { + return parent + "." + child; +} + +// Checks if kPrefBlacklist is set to true in the base::DictionaryValue. +// Return false if the value is false or kPrefBlacklist does not exist. +// This is used to decide if an extension is blacklisted. +bool IsBlacklistBitSet(const base::DictionaryValue* ext) { + bool bool_value; + return ext->GetBoolean(kPrefBlacklist, &bool_value) && bool_value; +} + +} // namespace + +// +// TimeProvider +// + +ExtensionPrefs::TimeProvider::TimeProvider() { +} + +ExtensionPrefs::TimeProvider::~TimeProvider() { +} + +base::Time ExtensionPrefs::TimeProvider::GetCurrentTime() const { + return base::Time::Now(); +} + +// +// ScopedUpdate +// +template <typename T, base::Value::Type type_enum_value> +ExtensionPrefs::ScopedUpdate<T, type_enum_value>::ScopedUpdate( + ExtensionPrefs* prefs, + const std::string& extension_id, + const std::string& key) + : update_(prefs->pref_service(), pref_names::kExtensions), + extension_id_(extension_id), + key_(key) { + DCHECK(Extension::IdIsValid(extension_id_)); +} + +template <typename T, base::Value::Type type_enum_value> +ExtensionPrefs::ScopedUpdate<T, type_enum_value>::~ScopedUpdate() { +} + +template <typename T, base::Value::Type type_enum_value> +T* ExtensionPrefs::ScopedUpdate<T, type_enum_value>::Get() { + base::DictionaryValue* dict = update_.Get(); + base::DictionaryValue* extension = NULL; + base::Value* key_value = NULL; + if (!dict->GetDictionary(extension_id_, &extension) || + !extension->Get(key_, &key_value)) { + return NULL; + } + return key_value->GetType() == type_enum_value ? + static_cast<T*>(key_value) : + NULL; +} + +template <typename T, base::Value::Type type_enum_value> +T* ExtensionPrefs::ScopedUpdate<T, type_enum_value>::Create() { + base::DictionaryValue* dict = update_.Get(); + base::DictionaryValue* extension = NULL; + base::Value* key_value = NULL; + T* value_as_t = NULL; + if (!dict->GetDictionary(extension_id_, &extension)) { + extension = new base::DictionaryValue; + dict->SetWithoutPathExpansion(extension_id_, extension); + } + if (!extension->Get(key_, &key_value)) { + value_as_t = new T; + extension->SetWithoutPathExpansion(key_, value_as_t); + } else { + CHECK(key_value->GetType() == type_enum_value); + value_as_t = static_cast<T*>(key_value); + } + return value_as_t; +} + +// Explicit instantiations for Dictionary and List value types. +template class ExtensionPrefs::ScopedUpdate<base::DictionaryValue, + base::Value::TYPE_DICTIONARY>; +template class ExtensionPrefs::ScopedUpdate<base::ListValue, + base::Value::TYPE_LIST>; + +// +// ExtensionPrefs +// + +// static +ExtensionPrefs* ExtensionPrefs::Create( + PrefService* prefs, + const base::FilePath& root_dir, + ExtensionPrefValueMap* extension_pref_value_map, + scoped_ptr<AppSorting> app_sorting, + bool extensions_disabled) { + return ExtensionPrefs::Create(prefs, + root_dir, + extension_pref_value_map, + app_sorting.Pass(), + extensions_disabled, + make_scoped_ptr(new TimeProvider())); +} + +// static +ExtensionPrefs* ExtensionPrefs::Create( + PrefService* pref_service, + const base::FilePath& root_dir, + ExtensionPrefValueMap* extension_pref_value_map, + scoped_ptr<AppSorting> app_sorting, + bool extensions_disabled, + scoped_ptr<TimeProvider> time_provider) { + return new ExtensionPrefs(pref_service, + root_dir, + extension_pref_value_map, + app_sorting.Pass(), + time_provider.Pass(), + extensions_disabled); +} + +ExtensionPrefs::~ExtensionPrefs() { +} + +// static +ExtensionPrefs* ExtensionPrefs::Get(content::BrowserContext* context) { + return ExtensionPrefsFactory::GetInstance()->GetForBrowserContext(context); +} + +static base::FilePath::StringType MakePathRelative(const base::FilePath& parent, + const base::FilePath& child) { + if (!parent.IsParent(child)) + return child.value(); + + base::FilePath::StringType retval = child.value().substr( + parent.value().length()); + if (base::FilePath::IsSeparator(retval[0])) + return retval.substr(1); + else + return retval; +} + +void ExtensionPrefs::MakePathsRelative() { + const base::DictionaryValue* dict = + prefs_->GetDictionary(pref_names::kExtensions); + if (!dict || dict->empty()) + return; + + // Collect all extensions ids with absolute paths in |absolute_keys|. + std::set<std::string> absolute_keys; + for (base::DictionaryValue::Iterator i(*dict); !i.IsAtEnd(); i.Advance()) { + const base::DictionaryValue* extension_dict = NULL; + if (!i.value().GetAsDictionary(&extension_dict)) + continue; + int location_value; + if (extension_dict->GetInteger(kPrefLocation, &location_value) && + Manifest::IsUnpackedLocation( + static_cast<Manifest::Location>(location_value))) { + // Unpacked extensions can have absolute paths. + continue; + } + base::FilePath::StringType path_string; + if (!extension_dict->GetString(kPrefPath, &path_string)) + continue; + base::FilePath path(path_string); + if (path.IsAbsolute()) + absolute_keys.insert(i.key()); + } + if (absolute_keys.empty()) + return; + + // Fix these paths. + DictionaryPrefUpdate update(prefs_, pref_names::kExtensions); + base::DictionaryValue* update_dict = update.Get(); + for (std::set<std::string>::iterator i = absolute_keys.begin(); + i != absolute_keys.end(); ++i) { + base::DictionaryValue* extension_dict = NULL; + if (!update_dict->GetDictionaryWithoutPathExpansion(*i, &extension_dict)) { + NOTREACHED() << "Control should never reach here for extension " << *i; + continue; + } + base::FilePath::StringType path_string; + extension_dict->GetString(kPrefPath, &path_string); + base::FilePath path(path_string); + extension_dict->SetString(kPrefPath, + MakePathRelative(install_directory_, path)); + } +} + +const base::DictionaryValue* ExtensionPrefs::GetExtensionPref( + const std::string& extension_id) const { + const base::DictionaryValue* extensions = + prefs_->GetDictionary(pref_names::kExtensions); + const base::DictionaryValue* extension_dict = NULL; + if (!extensions || + !extensions->GetDictionary(extension_id, &extension_dict)) { + return NULL; + } + return extension_dict; +} + +void ExtensionPrefs::UpdateExtensionPref(const std::string& extension_id, + const std::string& key, + base::Value* data_value) { + if (!Extension::IdIsValid(extension_id)) { + NOTREACHED() << "Invalid extension_id " << extension_id; + return; + } + ScopedExtensionPrefUpdate update(prefs_, extension_id); + if (data_value) + update->Set(key, data_value); + else + update->Remove(key, NULL); +} + +void ExtensionPrefs::DeleteExtensionPrefs(const std::string& extension_id) { + extension_pref_value_map_->UnregisterExtension(extension_id); + content_settings_store_->UnregisterExtension(extension_id); + DictionaryPrefUpdate update(prefs_, pref_names::kExtensions); + base::DictionaryValue* dict = update.Get(); + dict->Remove(extension_id, NULL); +} + +bool ExtensionPrefs::ReadPrefAsBoolean(const std::string& extension_id, + const std::string& pref_key, + bool* out_value) const { + const base::DictionaryValue* ext = GetExtensionPref(extension_id); + if (!ext || !ext->GetBoolean(pref_key, out_value)) + return false; + + return true; +} + +bool ExtensionPrefs::ReadPrefAsInteger(const std::string& extension_id, + const std::string& pref_key, + int* out_value) const { + const base::DictionaryValue* ext = GetExtensionPref(extension_id); + if (!ext || !ext->GetInteger(pref_key, out_value)) + return false; + + return true; +} + +bool ExtensionPrefs::ReadPrefAsString(const std::string& extension_id, + const std::string& pref_key, + std::string* out_value) const { + const base::DictionaryValue* ext = GetExtensionPref(extension_id); + if (!ext || !ext->GetString(pref_key, out_value)) + return false; + + return true; +} + +bool ExtensionPrefs::ReadPrefAsList(const std::string& extension_id, + const std::string& pref_key, + const base::ListValue** out_value) const { + const base::DictionaryValue* ext = GetExtensionPref(extension_id); + const base::ListValue* out = NULL; + if (!ext || !ext->GetList(pref_key, &out)) + return false; + if (out_value) + *out_value = out; + + return true; +} + +bool ExtensionPrefs::ReadPrefAsDictionary( + const std::string& extension_id, + const std::string& pref_key, + const base::DictionaryValue** out_value) const { + const base::DictionaryValue* ext = GetExtensionPref(extension_id); + const base::DictionaryValue* out = NULL; + if (!ext || !ext->GetDictionary(pref_key, &out)) + return false; + if (out_value) + *out_value = out; + + return true; +} + +bool ExtensionPrefs::HasPrefForExtension( + const std::string& extension_id) const { + return GetExtensionPref(extension_id) != NULL; +} + +bool ExtensionPrefs::ReadPrefAsURLPatternSet(const std::string& extension_id, + const std::string& pref_key, + URLPatternSet* result, + int valid_schemes) { + const base::ListValue* value = NULL; + if (!ReadPrefAsList(extension_id, pref_key, &value)) + return false; + + bool allow_file_access = AllowFileAccess(extension_id); + return result->Populate(*value, valid_schemes, allow_file_access, NULL); +} + +void ExtensionPrefs::SetExtensionPrefURLPatternSet( + const std::string& extension_id, + const std::string& pref_key, + const URLPatternSet& new_value) { + UpdateExtensionPref(extension_id, pref_key, new_value.ToValue().release()); +} + +bool ExtensionPrefs::ReadPrefAsBooleanAndReturn( + const std::string& extension_id, + const std::string& pref_key) const { + bool out_value = false; + return ReadPrefAsBoolean(extension_id, pref_key, &out_value) && out_value; +} + +PermissionSet* ExtensionPrefs::ReadPrefAsPermissionSet( + const std::string& extension_id, + const std::string& pref_key) { + if (!GetExtensionPref(extension_id)) + return NULL; + + // Retrieve the API permissions. Please refer SetExtensionPrefPermissionSet() + // for api_values format. + APIPermissionSet apis; + const base::ListValue* api_values = NULL; + std::string api_pref = JoinPrefs(pref_key, kPrefAPIs); + if (ReadPrefAsList(extension_id, api_pref, &api_values)) { + APIPermissionSet::ParseFromJSON(api_values, + APIPermissionSet::kAllowInternalPermissions, + &apis, NULL, NULL); + } + + // Retrieve the Manifest Keys permissions. Please refer to + // |SetExtensionPrefPermissionSet| for manifest_permissions_values format. + ManifestPermissionSet manifest_permissions; + const base::ListValue* manifest_permissions_values = NULL; + std::string manifest_permission_pref = + JoinPrefs(pref_key, kPrefManifestPermissions); + if (ReadPrefAsList(extension_id, manifest_permission_pref, + &manifest_permissions_values)) { + ManifestPermissionSet::ParseFromJSON( + manifest_permissions_values, &manifest_permissions, NULL, NULL); + } + + // Retrieve the explicit host permissions. + URLPatternSet explicit_hosts; + ReadPrefAsURLPatternSet( + extension_id, JoinPrefs(pref_key, kPrefExplicitHosts), + &explicit_hosts, Extension::kValidHostPermissionSchemes); + + // Retrieve the scriptable host permissions. + URLPatternSet scriptable_hosts; + ReadPrefAsURLPatternSet( + extension_id, JoinPrefs(pref_key, kPrefScriptableHosts), + &scriptable_hosts, UserScript::ValidUserScriptSchemes()); + + return new PermissionSet( + apis, manifest_permissions, explicit_hosts, scriptable_hosts); +} + +// Set the API or Manifest permissions. +// The format of api_values is: +// [ "permission_name1", // permissions do not support detail. +// "permission_name2", +// {"permission_name3": value }, +// // permission supports detail, permission detail will be stored in value. +// ... +// ] +template<typename T> +static base::ListValue* CreatePermissionList(const T& permissions) { + base::ListValue* values = new base::ListValue(); + for (typename T::const_iterator i = permissions.begin(); + i != permissions.end(); ++i) { + scoped_ptr<base::Value> detail(i->ToValue()); + if (detail) { + base::DictionaryValue* tmp = new base::DictionaryValue(); + tmp->Set(i->name(), detail.release()); + values->Append(tmp); + } else { + values->Append(new base::StringValue(i->name())); + } + } + return values; +} + +void ExtensionPrefs::SetExtensionPrefPermissionSet( + const std::string& extension_id, + const std::string& pref_key, + const PermissionSet* new_value) { + std::string api_pref = JoinPrefs(pref_key, kPrefAPIs); + base::ListValue* api_values = CreatePermissionList(new_value->apis()); + UpdateExtensionPref(extension_id, api_pref, api_values); + + std::string manifest_permissions_pref = + JoinPrefs(pref_key, kPrefManifestPermissions); + base::ListValue* manifest_permissions_values = CreatePermissionList( + new_value->manifest_permissions()); + UpdateExtensionPref(extension_id, + manifest_permissions_pref, + manifest_permissions_values); + + // Set the explicit host permissions. + if (!new_value->explicit_hosts().is_empty()) { + SetExtensionPrefURLPatternSet(extension_id, + JoinPrefs(pref_key, kPrefExplicitHosts), + new_value->explicit_hosts()); + } + + // Set the scriptable host permissions. + if (!new_value->scriptable_hosts().is_empty()) { + SetExtensionPrefURLPatternSet(extension_id, + JoinPrefs(pref_key, kPrefScriptableHosts), + new_value->scriptable_hosts()); + } +} + +int ExtensionPrefs::IncrementAcknowledgePromptCount( + const std::string& extension_id) { + int count = 0; + ReadPrefAsInteger(extension_id, kPrefAcknowledgePromptCount, &count); + ++count; + UpdateExtensionPref(extension_id, kPrefAcknowledgePromptCount, + new base::FundamentalValue(count)); + return count; +} + +bool ExtensionPrefs::IsExternalExtensionAcknowledged( + const std::string& extension_id) { + return ReadPrefAsBooleanAndReturn(extension_id, kPrefExternalAcknowledged); +} + +void ExtensionPrefs::AcknowledgeExternalExtension( + const std::string& extension_id) { + DCHECK(Extension::IdIsValid(extension_id)); + UpdateExtensionPref(extension_id, kPrefExternalAcknowledged, + new base::FundamentalValue(true)); + UpdateExtensionPref(extension_id, kPrefAcknowledgePromptCount, NULL); +} + +bool ExtensionPrefs::IsBlacklistedExtensionAcknowledged( + const std::string& extension_id) { + return ReadPrefAsBooleanAndReturn(extension_id, kPrefBlacklistAcknowledged); +} + +void ExtensionPrefs::AcknowledgeBlacklistedExtension( + const std::string& extension_id) { + DCHECK(Extension::IdIsValid(extension_id)); + UpdateExtensionPref(extension_id, kPrefBlacklistAcknowledged, + new base::FundamentalValue(true)); + UpdateExtensionPref(extension_id, kPrefAcknowledgePromptCount, NULL); +} + +bool ExtensionPrefs::IsExternalInstallFirstRun( + const std::string& extension_id) { + return ReadPrefAsBooleanAndReturn(extension_id, kPrefExternalInstallFirstRun); +} + +void ExtensionPrefs::SetExternalInstallFirstRun( + const std::string& extension_id) { + DCHECK(Extension::IdIsValid(extension_id)); + UpdateExtensionPref(extension_id, kPrefExternalInstallFirstRun, + new base::FundamentalValue(true)); +} + +bool ExtensionPrefs::HasWipeoutBeenAcknowledged( + const std::string& extension_id) { + return ReadPrefAsBooleanAndReturn(extension_id, kPrefWipeoutAcknowledged); +} + +void ExtensionPrefs::SetWipeoutAcknowledged( + const std::string& extension_id, + bool value) { + UpdateExtensionPref(extension_id, kPrefWipeoutAcknowledged, + value ? base::Value::CreateBooleanValue(value) : NULL); +} + +bool ExtensionPrefs::SetAlertSystemFirstRun() { + if (prefs_->GetBoolean(pref_names::kAlertsInitialized)) { + return true; + } + prefs_->SetBoolean(pref_names::kAlertsInitialized, true); + return false; +} + +bool ExtensionPrefs::ExtensionsBlacklistedByDefault() const { + return admin_policy::BlacklistedByDefault( + prefs_->GetList(pref_names::kInstallDenyList)); +} + +bool ExtensionPrefs::DidExtensionEscalatePermissions( + const std::string& extension_id) { + return ReadPrefAsBooleanAndReturn(extension_id, + kExtensionDidEscalatePermissions); +} + +void ExtensionPrefs::SetDidExtensionEscalatePermissions( + const Extension* extension, bool did_escalate) { + UpdateExtensionPref(extension->id(), kExtensionDidEscalatePermissions, + new base::FundamentalValue(did_escalate)); +} + +int ExtensionPrefs::GetDisableReasons(const std::string& extension_id) const { + int value = -1; + if (ReadPrefAsInteger(extension_id, kPrefDisableReasons, &value) && + value >= 0) { + return value; + } + return Extension::DISABLE_NONE; +} + +void ExtensionPrefs::AddDisableReason(const std::string& extension_id, + Extension::DisableReason disable_reason) { + int new_value = GetDisableReasons(extension_id) | + static_cast<int>(disable_reason); + UpdateExtensionPref(extension_id, kPrefDisableReasons, + new base::FundamentalValue(new_value)); +} + +void ExtensionPrefs::RemoveDisableReason( + const std::string& extension_id, + Extension::DisableReason disable_reason) { + int new_value = GetDisableReasons(extension_id) & + ~static_cast<int>(disable_reason); + if (new_value == Extension::DISABLE_NONE) { + UpdateExtensionPref(extension_id, kPrefDisableReasons, NULL); + } else { + UpdateExtensionPref(extension_id, kPrefDisableReasons, + new base::FundamentalValue(new_value)); + } +} + +void ExtensionPrefs::ClearDisableReasons(const std::string& extension_id) { + UpdateExtensionPref(extension_id, kPrefDisableReasons, NULL); +} + +std::set<std::string> ExtensionPrefs::GetBlacklistedExtensions() { + std::set<std::string> ids; + + const base::DictionaryValue* extensions = + prefs_->GetDictionary(pref_names::kExtensions); + if (!extensions) + return ids; + + for (base::DictionaryValue::Iterator it(*extensions); + !it.IsAtEnd(); it.Advance()) { + if (!it.value().IsType(base::Value::TYPE_DICTIONARY)) { + NOTREACHED() << "Invalid pref for extension " << it.key(); + continue; + } + if (IsBlacklistBitSet( + static_cast<const base::DictionaryValue*>(&it.value()))) { + ids.insert(it.key()); + } + } + + return ids; +} + +void ExtensionPrefs::SetExtensionBlacklisted(const std::string& extension_id, + bool is_blacklisted) { + bool currently_blacklisted = IsExtensionBlacklisted(extension_id); + if (is_blacklisted == currently_blacklisted) + return; + + // Always make sure the "acknowledged" bit is cleared since the blacklist bit + // is changing. + UpdateExtensionPref(extension_id, kPrefBlacklistAcknowledged, NULL); + + if (is_blacklisted) { + UpdateExtensionPref(extension_id, + kPrefBlacklist, + new base::FundamentalValue(true)); + } else { + UpdateExtensionPref(extension_id, kPrefBlacklist, NULL); + const base::DictionaryValue* dict = GetExtensionPref(extension_id); + if (dict && dict->empty()) + DeleteExtensionPrefs(extension_id); + } +} + +bool ExtensionPrefs::IsExtensionBlacklisted(const std::string& id) const { + const base::DictionaryValue* ext_prefs = GetExtensionPref(id); + return ext_prefs && IsBlacklistBitSet(ext_prefs); +} + +namespace { + +// Serializes |time| as a string value mapped to |key| in |dictionary|. +void SaveTime(base::DictionaryValue* dictionary, + const char* key, + const base::Time& time) { + if (!dictionary) + return; + std::string string_value = base::Int64ToString(time.ToInternalValue()); + dictionary->SetString(key, string_value); +} + +// The opposite of SaveTime. If |key| is not found, this returns an empty Time +// (is_null() will return true). +base::Time ReadTime(const base::DictionaryValue* dictionary, const char* key) { + if (!dictionary) + return base::Time(); + std::string string_value; + int64 value; + if (dictionary->GetString(key, &string_value)) { + if (base::StringToInt64(string_value, &value)) { + return base::Time::FromInternalValue(value); + } + } + return base::Time(); +} + +} // namespace + +base::Time ExtensionPrefs::LastPingDay(const std::string& extension_id) const { + DCHECK(Extension::IdIsValid(extension_id)); + return ReadTime(GetExtensionPref(extension_id), kLastPingDay); +} + +void ExtensionPrefs::SetLastPingDay(const std::string& extension_id, + const base::Time& time) { + DCHECK(Extension::IdIsValid(extension_id)); + ScopedExtensionPrefUpdate update(prefs_, extension_id); + SaveTime(update.Get(), kLastPingDay, time); +} + +base::Time ExtensionPrefs::BlacklistLastPingDay() const { + return ReadTime(prefs_->GetDictionary(kExtensionsBlacklistUpdate), + kLastPingDay); +} + +void ExtensionPrefs::SetBlacklistLastPingDay(const base::Time& time) { + DictionaryPrefUpdate update(prefs_, kExtensionsBlacklistUpdate); + SaveTime(update.Get(), kLastPingDay, time); +} + +base::Time ExtensionPrefs::LastActivePingDay(const std::string& extension_id) { + DCHECK(Extension::IdIsValid(extension_id)); + return ReadTime(GetExtensionPref(extension_id), kLastActivePingDay); +} + +void ExtensionPrefs::SetLastActivePingDay(const std::string& extension_id, + const base::Time& time) { + DCHECK(Extension::IdIsValid(extension_id)); + ScopedExtensionPrefUpdate update(prefs_, extension_id); + SaveTime(update.Get(), kLastActivePingDay, time); +} + +bool ExtensionPrefs::GetActiveBit(const std::string& extension_id) { + const base::DictionaryValue* dictionary = GetExtensionPref(extension_id); + bool result = false; + if (dictionary && dictionary->GetBoolean(kActiveBit, &result)) + return result; + return false; +} + +void ExtensionPrefs::SetActiveBit(const std::string& extension_id, + bool active) { + UpdateExtensionPref(extension_id, kActiveBit, + new base::FundamentalValue(active)); +} + +void ExtensionPrefs::MigratePermissions(const ExtensionIdList& extension_ids) { + PermissionsInfo* info = PermissionsInfo::GetInstance(); + for (ExtensionIdList::const_iterator ext_id = + extension_ids.begin(); ext_id != extension_ids.end(); ++ext_id) { + // An extension's granted permissions need to be migrated if the + // full_access bit is present. This bit was always present in the previous + // scheme and is never present now. + bool full_access; + const base::DictionaryValue* ext = GetExtensionPref(*ext_id); + if (!ext || !ext->GetBoolean(kPrefOldGrantedFullAccess, &full_access)) + continue; + + // Remove the full access bit (empty list will get trimmed). + UpdateExtensionPref( + *ext_id, kPrefOldGrantedFullAccess, new base::ListValue()); + + // Add the plugin permission if the full access bit was set. + if (full_access) { + const base::ListValue* apis = NULL; + base::ListValue* new_apis = NULL; + + std::string granted_apis = JoinPrefs(kPrefGrantedPermissions, kPrefAPIs); + if (ext->GetList(kPrefOldGrantedAPIs, &apis)) + new_apis = apis->DeepCopy(); + else + new_apis = new base::ListValue(); + + std::string plugin_name = info->GetByID(APIPermission::kPlugin)->name(); + new_apis->Append(new base::StringValue(plugin_name)); + UpdateExtensionPref(*ext_id, granted_apis, new_apis); + } + + // The granted permissions originally only held the effective hosts, + // which are a combination of host and user script host permissions. + // We now maintain these lists separately. For migration purposes, it + // does not matter how we treat the old effective hosts as long as the + // new effective hosts will be the same, so we move them to explicit + // host permissions. + const base::ListValue* hosts = NULL; + std::string explicit_hosts = + JoinPrefs(kPrefGrantedPermissions, kPrefExplicitHosts); + if (ext->GetList(kPrefOldGrantedHosts, &hosts)) { + UpdateExtensionPref( + *ext_id, explicit_hosts, hosts->DeepCopy()); + + // We can get rid of the old one by setting it to an empty list. + UpdateExtensionPref(*ext_id, kPrefOldGrantedHosts, new base::ListValue()); + } + } +} + +void ExtensionPrefs::MigrateDisableReasons( + const ExtensionIdList& extension_ids) { + for (ExtensionIdList::const_iterator ext_id = + extension_ids.begin(); ext_id != extension_ids.end(); ++ext_id) { + int value = -1; + if (ReadPrefAsInteger(*ext_id, kDeprecatedPrefDisableReason, &value)) { + int new_value = Extension::DISABLE_NONE; + switch (value) { + case Extension::DEPRECATED_DISABLE_USER_ACTION: + new_value = Extension::DISABLE_USER_ACTION; + break; + case Extension::DEPRECATED_DISABLE_PERMISSIONS_INCREASE: + new_value = Extension::DISABLE_PERMISSIONS_INCREASE; + break; + case Extension::DEPRECATED_DISABLE_RELOAD: + new_value = Extension::DISABLE_RELOAD; + break; + } + + UpdateExtensionPref(*ext_id, kPrefDisableReasons, + new base::FundamentalValue(new_value)); + // Remove the old disable reason. + UpdateExtensionPref(*ext_id, kDeprecatedPrefDisableReason, NULL); + } + } +} + +PermissionSet* ExtensionPrefs::GetGrantedPermissions( + const std::string& extension_id) { + CHECK(Extension::IdIsValid(extension_id)); + return ReadPrefAsPermissionSet(extension_id, kPrefGrantedPermissions); +} + +void ExtensionPrefs::AddGrantedPermissions( + const std::string& extension_id, + const PermissionSet* permissions) { + CHECK(Extension::IdIsValid(extension_id)); + + scoped_refptr<PermissionSet> granted_permissions( + GetGrantedPermissions(extension_id)); + + // The new granted permissions are the union of the already granted + // permissions and the newly granted permissions. + scoped_refptr<PermissionSet> new_perms( + PermissionSet::CreateUnion( + permissions, granted_permissions.get())); + + SetExtensionPrefPermissionSet( + extension_id, kPrefGrantedPermissions, new_perms.get()); +} + +void ExtensionPrefs::RemoveGrantedPermissions( + const std::string& extension_id, + const PermissionSet* permissions) { + CHECK(Extension::IdIsValid(extension_id)); + + scoped_refptr<PermissionSet> granted_permissions( + GetGrantedPermissions(extension_id)); + + // The new granted permissions are the difference of the already granted + // permissions and the newly ungranted permissions. + scoped_refptr<PermissionSet> new_perms( + PermissionSet::CreateDifference( + granted_permissions.get(), permissions)); + + SetExtensionPrefPermissionSet( + extension_id, kPrefGrantedPermissions, new_perms.get()); +} + +PermissionSet* ExtensionPrefs::GetActivePermissions( + const std::string& extension_id) { + CHECK(Extension::IdIsValid(extension_id)); + return ReadPrefAsPermissionSet(extension_id, kPrefActivePermissions); +} + +void ExtensionPrefs::SetActivePermissions( + const std::string& extension_id, + const PermissionSet* permissions) { + SetExtensionPrefPermissionSet( + extension_id, kPrefActivePermissions, permissions); +} + +void ExtensionPrefs::SetExtensionRunning(const std::string& extension_id, + bool is_running) { + base::Value* value = new base::FundamentalValue(is_running); + UpdateExtensionPref(extension_id, kPrefRunning, value); +} + +bool ExtensionPrefs::IsExtensionRunning(const std::string& extension_id) { + const base::DictionaryValue* extension = GetExtensionPref(extension_id); + if (!extension) + return false; + bool running = false; + extension->GetBoolean(kPrefRunning, &running); + return running; +} + +void ExtensionPrefs::SetIsActive(const std::string& extension_id, + bool is_active) { + base::Value* value = new base::FundamentalValue(is_active); + UpdateExtensionPref(extension_id, kIsActive, value); +} + +bool ExtensionPrefs::IsActive(const std::string& extension_id) { + const base::DictionaryValue* extension = GetExtensionPref(extension_id); + if (!extension) + return false; + bool is_active = false; + extension->GetBoolean(kIsActive, &is_active); + return is_active; +} + +bool ExtensionPrefs::IsIncognitoEnabled(const std::string& extension_id) const { + return ReadPrefAsBooleanAndReturn(extension_id, kPrefIncognitoEnabled); +} + +void ExtensionPrefs::SetIsIncognitoEnabled(const std::string& extension_id, + bool enabled) { + UpdateExtensionPref(extension_id, kPrefIncognitoEnabled, + new base::FundamentalValue(enabled)); +} + +bool ExtensionPrefs::AllowFileAccess(const std::string& extension_id) const { + return ReadPrefAsBooleanAndReturn(extension_id, kPrefAllowFileAccess); +} + +void ExtensionPrefs::SetAllowFileAccess(const std::string& extension_id, + bool allow) { + UpdateExtensionPref(extension_id, kPrefAllowFileAccess, + new base::FundamentalValue(allow)); +} + +bool ExtensionPrefs::HasAllowFileAccessSetting( + const std::string& extension_id) const { + const base::DictionaryValue* ext = GetExtensionPref(extension_id); + return ext && ext->HasKey(kPrefAllowFileAccess); +} + +bool ExtensionPrefs::DoesExtensionHaveState( + const std::string& id, Extension::State check_state) const { + const base::DictionaryValue* extension = GetExtensionPref(id); + int state = -1; + if (!extension || !extension->GetInteger(kPrefState, &state)) + return false; + + if (state < 0 || state >= Extension::NUM_STATES) { + LOG(ERROR) << "Bad pref 'state' for extension '" << id << "'"; + return false; + } + + return state == check_state; +} + +bool ExtensionPrefs::IsExternalExtensionUninstalled( + const std::string& id) const { + return DoesExtensionHaveState(id, Extension::EXTERNAL_EXTENSION_UNINSTALLED); +} + +bool ExtensionPrefs::IsExtensionDisabled( + const std::string& id) const { + return DoesExtensionHaveState(id, Extension::DISABLED); +} + +ExtensionIdList ExtensionPrefs::GetToolbarOrder() { + ExtensionIdList id_list_out; + GetUserExtensionPrefIntoContainer(pref_names::kToolbar, &id_list_out); + return id_list_out; +} + +void ExtensionPrefs::SetToolbarOrder(const ExtensionIdList& extension_ids) { + SetExtensionPrefFromContainer(pref_names::kToolbar, extension_ids); +} + +bool ExtensionPrefs::GetKnownDisabled(ExtensionIdSet* id_set_out) { + return GetUserExtensionPrefIntoContainer(pref_names::kKnownDisabled, + id_set_out); +} + +void ExtensionPrefs::SetKnownDisabled(const ExtensionIdSet& extension_ids) { + SetExtensionPrefFromContainer(pref_names::kKnownDisabled, extension_ids); +} + +void ExtensionPrefs::OnExtensionInstalled( + const Extension* extension, + Extension::State initial_state, + bool blacklisted_for_malware, + const syncer::StringOrdinal& page_ordinal) { + ScopedExtensionPrefUpdate update(prefs_, extension->id()); + base::DictionaryValue* extension_dict = update.Get(); + const base::Time install_time = time_provider_->GetCurrentTime(); + PopulateExtensionInfoPrefs(extension, install_time, initial_state, + blacklisted_for_malware, extension_dict); + FinishExtensionInfoPrefs(extension->id(), install_time, + extension->RequiresSortOrdinal(), + page_ordinal, extension_dict); +} + +void ExtensionPrefs::OnExtensionUninstalled(const std::string& extension_id, + const Manifest::Location& location, + bool external_uninstall) { + app_sorting_->ClearOrdinals(extension_id); + + // For external extensions, we save a preference reminding ourself not to try + // and install the extension anymore (except when |external_uninstall| is + // true, which signifies that the registry key was deleted or the pref file + // no longer lists the extension). + if (!external_uninstall && Manifest::IsExternalLocation(location)) { + UpdateExtensionPref(extension_id, kPrefState, + new base::FundamentalValue( + Extension::EXTERNAL_EXTENSION_UNINSTALLED)); + extension_pref_value_map_->SetExtensionState(extension_id, false); + content_settings_store_->SetExtensionState(extension_id, false); + } else { + DeleteExtensionPrefs(extension_id); + } +} + +void ExtensionPrefs::SetExtensionState(const std::string& extension_id, + Extension::State state) { + UpdateExtensionPref(extension_id, kPrefState, + new base::FundamentalValue(state)); + bool enabled = (state == Extension::ENABLED); + extension_pref_value_map_->SetExtensionState(extension_id, enabled); + content_settings_store_->SetExtensionState(extension_id, enabled); +} + +void ExtensionPrefs::SetExtensionBlacklistState(const std::string& extension_id, + BlacklistState state) { + SetExtensionBlacklisted(extension_id, state == BLACKLISTED_MALWARE); + UpdateExtensionPref(extension_id, kPrefBlacklistState, + new base::FundamentalValue(state)); +} + +BlacklistState ExtensionPrefs::GetExtensionBlacklistState( + const std::string& extension_id) { + if (IsExtensionBlacklisted(extension_id)) + return BLACKLISTED_MALWARE; + const base::DictionaryValue* ext_prefs = GetExtensionPref(extension_id); + int int_value; + if (ext_prefs && ext_prefs->GetInteger(kPrefBlacklistState, &int_value)) + return static_cast<BlacklistState>(int_value); + + return NOT_BLACKLISTED; +} + +std::string ExtensionPrefs::GetVersionString(const std::string& extension_id) { + const base::DictionaryValue* extension = GetExtensionPref(extension_id); + if (!extension) + return std::string(); + + std::string version; + extension->GetString(kPrefVersion, &version); + + return version; +} + +void ExtensionPrefs::UpdateManifest(const Extension* extension) { + if (!Manifest::IsUnpackedLocation(extension->location())) { + const base::DictionaryValue* extension_dict = + GetExtensionPref(extension->id()); + if (!extension_dict) + return; + const base::DictionaryValue* old_manifest = NULL; + bool update_required = + !extension_dict->GetDictionary(kPrefManifest, &old_manifest) || + !extension->manifest()->value()->Equals(old_manifest); + if (update_required) { + UpdateExtensionPref(extension->id(), kPrefManifest, + extension->manifest()->value()->DeepCopy()); + } + } +} + +base::FilePath ExtensionPrefs::GetExtensionPath( + const std::string& extension_id) { + const base::DictionaryValue* dict = GetExtensionPref(extension_id); + if (!dict) + return base::FilePath(); + + std::string path; + if (!dict->GetString(kPrefPath, &path)) + return base::FilePath(); + + return install_directory_.Append(base::FilePath::FromUTF8Unsafe(path)); +} + +scoped_ptr<ExtensionInfo> ExtensionPrefs::GetInstalledInfoHelper( + const std::string& extension_id, + const base::DictionaryValue* extension) const { + int location_value; + if (!extension->GetInteger(kPrefLocation, &location_value)) + return scoped_ptr<ExtensionInfo>(); + + base::FilePath::StringType path; + if (!extension->GetString(kPrefPath, &path)) + return scoped_ptr<ExtensionInfo>(); + + // Make path absolute. Unpacked extensions will already have absolute paths, + // otherwise make it so. + Manifest::Location location = static_cast<Manifest::Location>(location_value); + if (!Manifest::IsUnpackedLocation(location)) { + DCHECK(location == Manifest::COMPONENT || + !base::FilePath(path).IsAbsolute()); + path = install_directory_.Append(path).value(); + } + + // Only the following extension types have data saved in the preferences. + if (location != Manifest::INTERNAL && + !Manifest::IsUnpackedLocation(location) && + !Manifest::IsExternalLocation(location)) { + NOTREACHED(); + return scoped_ptr<ExtensionInfo>(); + } + + const base::DictionaryValue* manifest = NULL; + if (!Manifest::IsUnpackedLocation(location) && + !extension->GetDictionary(kPrefManifest, &manifest)) { + LOG(WARNING) << "Missing manifest for extension " << extension_id; + // Just a warning for now. + } + + return scoped_ptr<ExtensionInfo>(new ExtensionInfo( + manifest, extension_id, base::FilePath(path), location)); +} + +scoped_ptr<ExtensionInfo> ExtensionPrefs::GetInstalledExtensionInfo( + const std::string& extension_id) const { + const base::DictionaryValue* ext = NULL; + const base::DictionaryValue* extensions = + prefs_->GetDictionary(pref_names::kExtensions); + if (!extensions || + !extensions->GetDictionaryWithoutPathExpansion(extension_id, &ext)) + return scoped_ptr<ExtensionInfo>(); + int state_value; + if (!ext->GetInteger(kPrefState, &state_value) || + state_value == Extension::ENABLED_COMPONENT) { + // Old preferences files may not have kPrefState for component extensions. + return scoped_ptr<ExtensionInfo>(); + } + + if (state_value == Extension::EXTERNAL_EXTENSION_UNINSTALLED) { + LOG(WARNING) << "External extension with id " << extension_id + << " has been uninstalled by the user"; + return scoped_ptr<ExtensionInfo>(); + } + + return GetInstalledInfoHelper(extension_id, ext); +} + +scoped_ptr<ExtensionPrefs::ExtensionsInfo> +ExtensionPrefs::GetInstalledExtensionsInfo() const { + scoped_ptr<ExtensionsInfo> extensions_info(new ExtensionsInfo); + + const base::DictionaryValue* extensions = + prefs_->GetDictionary(pref_names::kExtensions); + for (base::DictionaryValue::Iterator extension_id(*extensions); + !extension_id.IsAtEnd(); extension_id.Advance()) { + if (!Extension::IdIsValid(extension_id.key())) + continue; + + scoped_ptr<ExtensionInfo> info = + GetInstalledExtensionInfo(extension_id.key()); + if (info) + extensions_info->push_back(linked_ptr<ExtensionInfo>(info.release())); + } + + return extensions_info.Pass(); +} + +scoped_ptr<ExtensionPrefs::ExtensionsInfo> +ExtensionPrefs::GetUninstalledExtensionsInfo() const { + scoped_ptr<ExtensionsInfo> extensions_info(new ExtensionsInfo); + + const base::DictionaryValue* extensions = + prefs_->GetDictionary(pref_names::kExtensions); + for (base::DictionaryValue::Iterator extension_id(*extensions); + !extension_id.IsAtEnd(); extension_id.Advance()) { + const base::DictionaryValue* ext = NULL; + if (!Extension::IdIsValid(extension_id.key()) || + !IsExternalExtensionUninstalled(extension_id.key()) || + !extension_id.value().GetAsDictionary(&ext)) + continue; + + scoped_ptr<ExtensionInfo> info = + GetInstalledInfoHelper(extension_id.key(), ext); + if (info) + extensions_info->push_back(linked_ptr<ExtensionInfo>(info.release())); + } + + return extensions_info.Pass(); +} + +void ExtensionPrefs::SetDelayedInstallInfo( + const Extension* extension, + Extension::State initial_state, + bool blacklisted_for_malware, + DelayReason delay_reason, + const syncer::StringOrdinal& page_ordinal) { + base::DictionaryValue* extension_dict = new base::DictionaryValue(); + PopulateExtensionInfoPrefs(extension, time_provider_->GetCurrentTime(), + initial_state, blacklisted_for_malware, + extension_dict); + + // Add transient data that is needed by FinishDelayedInstallInfo(), but + // should not be in the final extension prefs. All entries here should have + // a corresponding Remove() call in FinishDelayedInstallInfo(). + if (extension->RequiresSortOrdinal()) { + extension_dict->SetString( + kPrefSuggestedPageOrdinal, + page_ordinal.IsValid() ? page_ordinal.ToInternalValue() + : std::string()); + } + extension_dict->SetInteger(kDelayedInstallReason, + static_cast<int>(delay_reason)); + + UpdateExtensionPref(extension->id(), kDelayedInstallInfo, extension_dict); +} + +bool ExtensionPrefs::RemoveDelayedInstallInfo( + const std::string& extension_id) { + if (!GetExtensionPref(extension_id)) + return false; + ScopedExtensionPrefUpdate update(prefs_, extension_id); + bool result = update->Remove(kDelayedInstallInfo, NULL); + return result; +} + +bool ExtensionPrefs::FinishDelayedInstallInfo( + const std::string& extension_id) { + CHECK(Extension::IdIsValid(extension_id)); + ScopedExtensionPrefUpdate update(prefs_, extension_id); + base::DictionaryValue* extension_dict = update.Get(); + base::DictionaryValue* pending_install_dict = NULL; + if (!extension_dict->GetDictionary(kDelayedInstallInfo, + &pending_install_dict)) { + return false; + } + + // Retrieve and clear transient values populated by SetDelayedInstallInfo(). + // Also do any other data cleanup that makes sense. + std::string serialized_ordinal; + syncer::StringOrdinal suggested_page_ordinal; + bool needs_sort_ordinal = false; + if (pending_install_dict->GetString(kPrefSuggestedPageOrdinal, + &serialized_ordinal)) { + suggested_page_ordinal = syncer::StringOrdinal(serialized_ordinal); + needs_sort_ordinal = true; + pending_install_dict->Remove(kPrefSuggestedPageOrdinal, NULL); + } + pending_install_dict->Remove(kDelayedInstallReason, NULL); + + const base::Time install_time = time_provider_->GetCurrentTime(); + pending_install_dict->Set( + kPrefInstallTime, + new base::StringValue( + base::Int64ToString(install_time.ToInternalValue()))); + + // Commit the delayed install data. + for (base::DictionaryValue::Iterator it(*pending_install_dict); !it.IsAtEnd(); + it.Advance()) { + extension_dict->Set(it.key(), it.value().DeepCopy()); + } + FinishExtensionInfoPrefs(extension_id, install_time, needs_sort_ordinal, + suggested_page_ordinal, extension_dict); + return true; +} + +scoped_ptr<ExtensionInfo> ExtensionPrefs::GetDelayedInstallInfo( + const std::string& extension_id) const { + const base::DictionaryValue* extension_prefs = + GetExtensionPref(extension_id); + if (!extension_prefs) + return scoped_ptr<ExtensionInfo>(); + + const base::DictionaryValue* ext = NULL; + if (!extension_prefs->GetDictionary(kDelayedInstallInfo, &ext)) + return scoped_ptr<ExtensionInfo>(); + + return GetInstalledInfoHelper(extension_id, ext); +} + +ExtensionPrefs::DelayReason ExtensionPrefs::GetDelayedInstallReason( + const std::string& extension_id) const { + const base::DictionaryValue* extension_prefs = + GetExtensionPref(extension_id); + if (!extension_prefs) + return DELAY_REASON_NONE; + + const base::DictionaryValue* ext = NULL; + if (!extension_prefs->GetDictionary(kDelayedInstallInfo, &ext)) + return DELAY_REASON_NONE; + + int delay_reason; + if (!ext->GetInteger(kDelayedInstallReason, &delay_reason)) + return DELAY_REASON_NONE; + + return static_cast<DelayReason>(delay_reason); +} + +scoped_ptr<ExtensionPrefs::ExtensionsInfo> ExtensionPrefs:: + GetAllDelayedInstallInfo() const { + scoped_ptr<ExtensionsInfo> extensions_info(new ExtensionsInfo); + + const base::DictionaryValue* extensions = + prefs_->GetDictionary(pref_names::kExtensions); + for (base::DictionaryValue::Iterator extension_id(*extensions); + !extension_id.IsAtEnd(); extension_id.Advance()) { + if (!Extension::IdIsValid(extension_id.key())) + continue; + + scoped_ptr<ExtensionInfo> info = GetDelayedInstallInfo(extension_id.key()); + if (info) + extensions_info->push_back(linked_ptr<ExtensionInfo>(info.release())); + } + + return extensions_info.Pass(); +} + +bool ExtensionPrefs::WasAppDraggedByUser(const std::string& extension_id) { + return ReadPrefAsBooleanAndReturn(extension_id, kPrefUserDraggedApp); +} + +void ExtensionPrefs::SetAppDraggedByUser(const std::string& extension_id) { + UpdateExtensionPref(extension_id, kPrefUserDraggedApp, + new base::FundamentalValue(true)); +} + +bool ExtensionPrefs::IsFromWebStore( + const std::string& extension_id) const { + const base::DictionaryValue* dictionary = GetExtensionPref(extension_id); + bool result = false; + if (dictionary && dictionary->GetBoolean(kPrefFromWebStore, &result)) + return result; + return false; +} + +bool ExtensionPrefs::IsFromBookmark( + const std::string& extension_id) const { + const base::DictionaryValue* dictionary = GetExtensionPref(extension_id); + bool result = false; + if (dictionary && dictionary->GetBoolean(kPrefFromBookmark, &result)) + return result; + return false; +} + +int ExtensionPrefs::GetCreationFlags(const std::string& extension_id) const { + int creation_flags = Extension::NO_FLAGS; + if (!ReadPrefAsInteger(extension_id, kPrefCreationFlags, &creation_flags)) { + // Since kPrefCreationFlags was added later, it will be missing for + // previously installed extensions. + if (IsFromBookmark(extension_id)) + creation_flags |= Extension::FROM_BOOKMARK; + if (IsFromWebStore(extension_id)) + creation_flags |= Extension::FROM_WEBSTORE; + if (WasInstalledByDefault(extension_id)) + creation_flags |= Extension::WAS_INSTALLED_BY_DEFAULT; + } + return creation_flags; +} + +int ExtensionPrefs::GetDelayedInstallCreationFlags( + const std::string& extension_id) const { + int creation_flags = Extension::NO_FLAGS; + const base::DictionaryValue* delayed_info = NULL; + if (ReadPrefAsDictionary(extension_id, kDelayedInstallInfo, &delayed_info)) { + delayed_info->GetInteger(kPrefCreationFlags, &creation_flags); + } + return creation_flags; +} + +bool ExtensionPrefs::WasInstalledByDefault( + const std::string& extension_id) const { + const base::DictionaryValue* dictionary = GetExtensionPref(extension_id); + bool result = false; + if (dictionary && + dictionary->GetBoolean(kPrefWasInstalledByDefault, &result)) + return result; + return false; +} + +base::Time ExtensionPrefs::GetInstallTime( + const std::string& extension_id) const { + const base::DictionaryValue* extension = GetExtensionPref(extension_id); + if (!extension) { + NOTREACHED(); + return base::Time(); + } + std::string install_time_str; + if (!extension->GetString(kPrefInstallTime, &install_time_str)) + return base::Time(); + int64 install_time_i64 = 0; + if (!base::StringToInt64(install_time_str, &install_time_i64)) + return base::Time(); + return base::Time::FromInternalValue(install_time_i64); +} + +base::Time ExtensionPrefs::GetLastLaunchTime( + const std::string& extension_id) const { + const base::DictionaryValue* extension = GetExtensionPref(extension_id); + if (!extension) + return base::Time(); + + std::string launch_time_str; + if (!extension->GetString(kPrefLastLaunchTime, &launch_time_str)) + return base::Time(); + int64 launch_time_i64 = 0; + if (!base::StringToInt64(launch_time_str, &launch_time_i64)) + return base::Time(); + return base::Time::FromInternalValue(launch_time_i64); +} + +void ExtensionPrefs::SetLastLaunchTime(const std::string& extension_id, + const base::Time& time) { + DCHECK(Extension::IdIsValid(extension_id)); + ScopedExtensionPrefUpdate update(prefs_, extension_id); + SaveTime(update.Get(), kPrefLastLaunchTime, time); +} + +void ExtensionPrefs::GetExtensions(ExtensionIdList* out) { + CHECK(out); + + scoped_ptr<ExtensionsInfo> extensions_info(GetInstalledExtensionsInfo()); + + for (size_t i = 0; i < extensions_info->size(); ++i) { + ExtensionInfo* info = extensions_info->at(i).get(); + out->push_back(info->extension_id); + } +} + +// static +ExtensionIdList ExtensionPrefs::GetExtensionsFrom( + const PrefService* pref_service) { + ExtensionIdList result; + + const base::DictionaryValue* extension_prefs = NULL; + const base::Value* extension_prefs_value = + pref_service->GetUserPrefValue(pref_names::kExtensions); + if (!extension_prefs_value || + !extension_prefs_value->GetAsDictionary(&extension_prefs)) { + return result; // Empty set + } + + for (base::DictionaryValue::Iterator it(*extension_prefs); !it.IsAtEnd(); + it.Advance()) { + const base::DictionaryValue* ext = NULL; + if (!it.value().GetAsDictionary(&ext)) { + NOTREACHED() << "Invalid pref for extension " << it.key(); + continue; + } + if (!IsBlacklistBitSet(ext)) + result.push_back(it.key()); + } + return result; +} + +void ExtensionPrefs::FixMissingPrefs(const ExtensionIdList& extension_ids) { + // Fix old entries that did not get an installation time entry when they + // were installed or don't have a preferences field. + for (ExtensionIdList::const_iterator ext_id = extension_ids.begin(); + ext_id != extension_ids.end(); ++ext_id) { + if (GetInstallTime(*ext_id) == base::Time()) { + VLOG(1) << "Could not parse installation time of extension " + << *ext_id << ". It was probably installed before setting " + << kPrefInstallTime << " was introduced. Updating " + << kPrefInstallTime << " to the current time."; + const base::Time install_time = time_provider_->GetCurrentTime(); + UpdateExtensionPref(*ext_id, + kPrefInstallTime, + new base::StringValue(base::Int64ToString( + install_time.ToInternalValue()))); + } + } +} + +void ExtensionPrefs::InitPrefStore() { + if (extensions_disabled_) { + extension_pref_value_map_->NotifyInitializationCompleted(); + return; + } + + // When this is called, the PrefService is initialized and provides access + // to the user preferences stored in a JSON file. + ExtensionIdList extension_ids; + GetExtensions(&extension_ids); + // Create empty preferences dictionary for each extension (these dictionaries + // are pruned when persisting the preferences to disk). + for (ExtensionIdList::iterator ext_id = extension_ids.begin(); + ext_id != extension_ids.end(); ++ext_id) { + ScopedExtensionPrefUpdate update(prefs_, *ext_id); + // This creates an empty dictionary if none is stored. + update.Get(); + } + + FixMissingPrefs(extension_ids); + MigratePermissions(extension_ids); + MigrateDisableReasons(extension_ids); + app_sorting_->Initialize(extension_ids); + + PreferenceAPI::InitExtensionControlledPrefs(this, extension_pref_value_map_); + + extension_pref_value_map_->NotifyInitializationCompleted(); +} + +bool ExtensionPrefs::HasIncognitoPrefValue(const std::string& pref_key) { + bool has_incognito_pref_value = false; + extension_pref_value_map_->GetEffectivePrefValue(pref_key, + true, + &has_incognito_pref_value); + return has_incognito_pref_value; +} + +URLPatternSet ExtensionPrefs::GetAllowedInstallSites() { + URLPatternSet result; + const base::ListValue* list = + prefs_->GetList(pref_names::kAllowedInstallSites); + CHECK(list); + + for (size_t i = 0; i < list->GetSize(); ++i) { + std::string entry_string; + URLPattern entry(URLPattern::SCHEME_ALL); + if (!list->GetString(i, &entry_string) || + entry.Parse(entry_string) != URLPattern::PARSE_SUCCESS) { + LOG(ERROR) << "Invalid value for preference: " + << pref_names::kAllowedInstallSites << "." << i; + continue; + } + result.AddPattern(entry); + } + + return result; +} + +const base::DictionaryValue* ExtensionPrefs::GetGeometryCache( + const std::string& extension_id) const { + const base::DictionaryValue* extension_prefs = GetExtensionPref(extension_id); + if (!extension_prefs) + return NULL; + + const base::DictionaryValue* ext = NULL; + if (!extension_prefs->GetDictionary(kPrefGeometryCache, &ext)) + return NULL; + + return ext; +} + +void ExtensionPrefs::SetGeometryCache( + const std::string& extension_id, + scoped_ptr<base::DictionaryValue> cache) { + UpdateExtensionPref(extension_id, kPrefGeometryCache, cache.release()); +} + +const base::DictionaryValue* ExtensionPrefs::GetInstallSignature() { + return prefs_->GetDictionary(kInstallSignature); +} + +void ExtensionPrefs::SetInstallSignature( + const base::DictionaryValue* signature) { + if (signature) { + prefs_->Set(kInstallSignature, *signature); + DVLOG(1) << "SetInstallSignature - saving"; + } else { + DVLOG(1) << "SetInstallSignature - clearing"; + prefs_->ClearPref(kInstallSignature); + } +} + + +ExtensionPrefs::ExtensionPrefs( + PrefService* prefs, + const base::FilePath& root_dir, + ExtensionPrefValueMap* extension_pref_value_map, + scoped_ptr<AppSorting> app_sorting, + scoped_ptr<TimeProvider> time_provider, + bool extensions_disabled) + : prefs_(prefs), + install_directory_(root_dir), + extension_pref_value_map_(extension_pref_value_map), + app_sorting_(app_sorting.Pass()), + content_settings_store_(new ContentSettingsStore()), + time_provider_(time_provider.Pass()), + extensions_disabled_(extensions_disabled) { + app_sorting_->SetExtensionScopedPrefs(this); + MakePathsRelative(); + InitPrefStore(); +} + +void ExtensionPrefs::SetNeedsStorageGarbageCollection(bool value) { + prefs_->SetBoolean(pref_names::kStorageGarbageCollect, value); +} + +bool ExtensionPrefs::NeedsStorageGarbageCollection() { + return prefs_->GetBoolean(pref_names::kStorageGarbageCollect); +} + +// static +void ExtensionPrefs::RegisterProfilePrefs( + user_prefs::PrefRegistrySyncable* registry) { + registry->RegisterDictionaryPref( + pref_names::kExtensions, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterListPref(pref_names::kToolbar, + user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); + registry->RegisterIntegerPref( + pref_names::kToolbarSize, + -1, // default value + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterDictionaryPref( + kExtensionsBlacklistUpdate, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterListPref(pref_names::kInstallAllowList, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterListPref(pref_names::kInstallDenyList, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterDictionaryPref( + pref_names::kInstallForceList, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterListPref(pref_names::kAllowedTypes, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterBooleanPref( + pref_names::kStorageGarbageCollect, + false, // default value + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterInt64Pref( + pref_names::kLastUpdateCheck, + 0, // default value + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterInt64Pref( + pref_names::kNextUpdateCheck, + 0, // default value + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterListPref(pref_names::kAllowedInstallSites, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterStringPref( + pref_names::kLastChromeVersion, + std::string(), // default value + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterListPref(pref_names::kKnownDisabled, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); +#if defined(TOOLKIT_VIEWS) + registry->RegisterIntegerPref( + pref_names::kBrowserActionContainerWidth, + 0, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); +#endif + registry->RegisterDictionaryPref( + kInstallSignature, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + + registry->RegisterListPref(pref_names::kNativeMessagingBlacklist, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterListPref(pref_names::kNativeMessagingWhitelist, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterBooleanPref( + pref_names::kNativeMessagingUserLevelHosts, + true, // default value + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); +} + +template <class ExtensionIdContainer> +bool ExtensionPrefs::GetUserExtensionPrefIntoContainer( + const char* pref, + ExtensionIdContainer* id_container_out) { + DCHECK(id_container_out->empty()); + + const base::Value* user_pref_value = prefs_->GetUserPrefValue(pref); + const base::ListValue* user_pref_as_list; + if (!user_pref_value || !user_pref_value->GetAsList(&user_pref_as_list)) + return false; + + std::insert_iterator<ExtensionIdContainer> insert_iterator( + *id_container_out, id_container_out->end()); + std::string extension_id; + for (base::ListValue::const_iterator value_it = user_pref_as_list->begin(); + value_it != user_pref_as_list->end(); ++value_it) { + if (!(*value_it)->GetAsString(&extension_id)) { + NOTREACHED(); + continue; + } + insert_iterator = extension_id; + } + return true; +} + +template <class ExtensionIdContainer> +void ExtensionPrefs::SetExtensionPrefFromContainer( + const char* pref, + const ExtensionIdContainer& strings) { + ListPrefUpdate update(prefs_, pref); + base::ListValue* list_of_values = update.Get(); + list_of_values->Clear(); + for (typename ExtensionIdContainer::const_iterator iter = strings.begin(); + iter != strings.end(); ++iter) { + list_of_values->Append(new base::StringValue(*iter)); + } +} + +void ExtensionPrefs::PopulateExtensionInfoPrefs( + const Extension* extension, + const base::Time install_time, + Extension::State initial_state, + bool blacklisted_for_malware, + base::DictionaryValue* extension_dict) { + // Leave the state blank for component extensions so that old chrome versions + // loading new profiles do not fail in GetInstalledExtensionInfo. Older + // Chrome versions would only check for an omitted state. + if (initial_state != Extension::ENABLED_COMPONENT) + extension_dict->Set(kPrefState, new base::FundamentalValue(initial_state)); + + extension_dict->Set(kPrefLocation, + new base::FundamentalValue(extension->location())); + extension_dict->Set(kPrefCreationFlags, + new base::FundamentalValue(extension->creation_flags())); + extension_dict->Set(kPrefFromWebStore, + new base::FundamentalValue(extension->from_webstore())); + extension_dict->Set(kPrefFromBookmark, + new base::FundamentalValue(extension->from_bookmark())); + extension_dict->Set( + kPrefWasInstalledByDefault, + new base::FundamentalValue(extension->was_installed_by_default())); + extension_dict->Set(kPrefInstallTime, + new base::StringValue( + base::Int64ToString(install_time.ToInternalValue()))); + if (blacklisted_for_malware) + extension_dict->Set(kPrefBlacklist, new base::FundamentalValue(true)); + + base::FilePath::StringType path = MakePathRelative(install_directory_, + extension->path()); + extension_dict->Set(kPrefPath, new base::StringValue(path)); + // We store prefs about LOAD extensions, but don't cache their manifest + // since it may change on disk. + if (!Manifest::IsUnpackedLocation(extension->location())) { + extension_dict->Set(kPrefManifest, + extension->manifest()->value()->DeepCopy()); + } +} + +void ExtensionPrefs::FinishExtensionInfoPrefs( + const std::string& extension_id, + const base::Time install_time, + bool needs_sort_ordinal, + const syncer::StringOrdinal& suggested_page_ordinal, + base::DictionaryValue* extension_dict) { + // Reinitializes various preferences with empty dictionaries. + if (!extension_dict->HasKey(pref_names::kPrefPreferences)) { + extension_dict->Set(pref_names::kPrefPreferences, + new base::DictionaryValue); + } + + if (!extension_dict->HasKey(pref_names::kPrefIncognitoPreferences)) { + extension_dict->Set(pref_names::kPrefIncognitoPreferences, + new base::DictionaryValue); + } + + if (!extension_dict->HasKey(pref_names::kPrefRegularOnlyPreferences)) { + extension_dict->Set(pref_names::kPrefRegularOnlyPreferences, + new base::DictionaryValue); + } + + if (!extension_dict->HasKey(pref_names::kPrefContentSettings)) + extension_dict->Set(pref_names::kPrefContentSettings, new base::ListValue); + + if (!extension_dict->HasKey(pref_names::kPrefIncognitoContentSettings)) { + extension_dict->Set(pref_names::kPrefIncognitoContentSettings, + new base::ListValue); + } + + // If this point has been reached, any pending installs should be considered + // out of date. + extension_dict->Remove(kDelayedInstallInfo, NULL); + + // Clear state that may be registered from a previous install. + extension_dict->Remove(EventRouter::kRegisteredEvents, NULL); + + // FYI, all code below here races on sudden shutdown because |extension_dict|, + // |app_sorting_|, |extension_pref_value_map_|, and |content_settings_store_| + // are updated non-transactionally. This is probably not fixable without + // nested transactional updates to pref dictionaries. + if (needs_sort_ordinal) + app_sorting_->EnsureValidOrdinals(extension_id, suggested_page_ordinal); + + bool is_enabled = false; + int initial_state; + if (extension_dict->GetInteger(kPrefState, &initial_state)) { + is_enabled = initial_state == Extension::ENABLED; + } + + extension_pref_value_map_->RegisterExtension(extension_id, install_time, + is_enabled); + content_settings_store_->RegisterExtension(extension_id, install_time, + is_enabled); +} + +} // namespace extensions diff --git a/extensions/browser/extension_prefs.h b/extensions/browser/extension_prefs.h new file mode 100644 index 0000000000..89258ae8d0 --- /dev/null +++ b/extensions/browser/extension_prefs.h @@ -0,0 +1,652 @@ +// Copyright 2014 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_H_ +#define EXTENSIONS_BROWSER_EXTENSION_PREFS_H_ + +#include <set> +#include <string> +#include <vector> + +#include "base/memory/linked_ptr.h" +#include "base/memory/scoped_ptr.h" +#include "base/prefs/scoped_user_pref_update.h" +#include "base/time/time.h" +#include "base/values.h" +#include "components/browser_context_keyed_service/browser_context_keyed_service.h" +#include "extensions/browser/app_sorting.h" +#include "extensions/browser/blacklist_state.h" +#include "extensions/browser/extension_scoped_prefs.h" +#include "extensions/common/constants.h" +#include "extensions/common/extension.h" +#include "extensions/common/url_pattern_set.h" +#include "sync/api/string_ordinal.h" + +class ExtensionPrefValueMap; +class PrefService; + +namespace content { +class BrowserContext; +} + +namespace user_prefs { +class PrefRegistrySyncable; +} + +namespace extensions { + +class AppSorting; +class ContentSettingsStore; +class ExtensionPrefsUninstallExtension; +class URLPatternSet; + +// Class for managing global and per-extension preferences. +// +// This class distinguishes the following kinds of preferences: +// - global preferences: +// internal state for the extension system in general, not associated +// with an individual extension, such as lastUpdateTime. +// - per-extension preferences: +// meta-preferences describing properties of the extension like +// installation time, whether the extension is enabled, etc. +// - extension controlled preferences: +// browser preferences that an extension controls. For example, an +// extension could use the proxy API to specify the browser's proxy +// preference. Extension-controlled preferences are stored in +// PrefValueStore::extension_prefs(), which this class populates and +// maintains as the underlying extensions change. +class ExtensionPrefs : public ExtensionScopedPrefs, + public BrowserContextKeyedService { + public: + typedef std::vector<linked_ptr<ExtensionInfo> > ExtensionsInfo; + + // Vector containing identifiers for preferences. + typedef std::set<std::string> PrefKeySet; + + // This enum is used to store the reason an extension's install has been + // delayed. Do not remove items or re-order this enum as it is used in + // preferences. + enum DelayReason { + DELAY_REASON_NONE = 0, + DELAY_REASON_GC = 1, + DELAY_REASON_WAIT_FOR_IDLE = 2, + DELAY_REASON_WAIT_FOR_IMPORTS = 3, + }; + + + // Creates base::Time classes. The default implementation is just to return + // the current time, but tests can inject alternative implementations. + class TimeProvider { + public: + TimeProvider(); + + virtual ~TimeProvider(); + + // By default, returns the current time (base::Time::Now()). + virtual base::Time GetCurrentTime() const; + + private: + DISALLOW_COPY_AND_ASSIGN(TimeProvider); + }; + + // A wrapper around a ScopedUserPrefUpdate, which allows us to access the + // entry of a particular key for an extension. Use this if you need a mutable + // record of a dictionary or list in the current settings. Otherwise, prefer + // ReadPrefAsT() and UpdateExtensionPref() methods. + template <typename T, base::Value::Type type_enum_value> + class ScopedUpdate { + public: + ScopedUpdate(ExtensionPrefs* prefs, + const std::string& extension_id, + const std::string& key); + virtual ~ScopedUpdate(); + + // Returns a mutable value for the key (ownership remains with the prefs), + // if one exists. Otherwise, returns NULL. + virtual T* Get(); + + // Creates and returns a mutable value for the key (the prefs own the new + // value), if one does not already exist. Otherwise, returns the current + // value. + virtual T* Create(); + + private: + DISALLOW_COPY_AND_ASSIGN(ScopedUpdate); + + DictionaryPrefUpdate update_; + const std::string extension_id_; + const std::string key_; + }; + typedef ScopedUpdate<base::DictionaryValue, base::Value::TYPE_DICTIONARY> + ScopedDictionaryUpdate; + typedef ScopedUpdate<base::ListValue, base::Value::TYPE_LIST> + ScopedListUpdate; + + // Creates and initializes an ExtensionPrefs object. + // Does not take ownership of |prefs| and |extension_pref_value_map|. + // If |extensions_disabled| is true, extension controlled preferences and + // content settings do not become effective. + static ExtensionPrefs* Create( + PrefService* prefs, + const base::FilePath& root_dir, + ExtensionPrefValueMap* extension_pref_value_map, + scoped_ptr<AppSorting> app_sorting, + bool extensions_disabled); + + // A version of Create which allows injection of a custom base::Time provider. + // Use this as needed for testing. + static ExtensionPrefs* Create( + PrefService* prefs, + const base::FilePath& root_dir, + ExtensionPrefValueMap* extension_pref_value_map, + scoped_ptr<AppSorting> app_sorting, + bool extensions_disabled, + scoped_ptr<TimeProvider> time_provider); + + virtual ~ExtensionPrefs(); + + // Convenience function to get the ExtensionPrefs for a BrowserContext. + static ExtensionPrefs* Get(content::BrowserContext* context); + + // Returns all installed extensions from extension preferences provided by + // |pref_service|. This is exposed for ProtectedPrefsWatcher because it needs + // access to the extension ID list before the ExtensionService is initialized. + static ExtensionIdList GetExtensionsFrom(const PrefService* pref_service); + + // Returns true if the specified external extension was uninstalled by the + // user. + bool IsExternalExtensionUninstalled(const std::string& id) const; + + // Checks whether |extension_id| is disabled. If there's no state pref for + // the extension, this will return false. Generally you should use + // ExtensionService::IsExtensionEnabled instead. + bool IsExtensionDisabled(const std::string& id) const; + + // Get/Set the order that the browser actions appear in the toolbar. + ExtensionIdList GetToolbarOrder(); + void SetToolbarOrder(const ExtensionIdList& extension_ids); + + // Gets the set of known disabled extension IDs into |id_set_out|. Returns + // false iff the set of known disabled extension IDs hasn't been set yet. + bool GetKnownDisabled(ExtensionIdSet* id_set_out); + + // Sets the set of known disabled extension IDs. + void SetKnownDisabled(const ExtensionIdSet& extension_ids); + + // Called when an extension is installed, so that prefs get created. + // |blacklisted_for_malware| should be set if the extension was included in a + // blacklist due to being malware. If |page_ordinal| is an invalid ordinal, + // then a page will be found for the App. + void OnExtensionInstalled(const Extension* extension, + Extension::State initial_state, + bool blacklisted_for_malware, + const syncer::StringOrdinal& page_ordinal); + + // Called when an extension is uninstalled, so that prefs get cleaned up. + void OnExtensionUninstalled(const std::string& extension_id, + const Manifest::Location& location, + bool external_uninstall); + + // Called to change the extension's state when it is enabled/disabled. + void SetExtensionState(const std::string& extension_id, Extension::State); + + // Called to change the extension's BlacklistState. Currently only used for + // non-malicious extensions. + // TODO(oleg): replace SetExtensionBlacklisted by this function. + void SetExtensionBlacklistState(const std::string& extension_id, + BlacklistState state); + + // Checks whether |extension_id| is marked as greylisted. + // TODO(oleg): Replace IsExtensionBlacklisted by this method. + BlacklistState GetExtensionBlacklistState(const std::string& extension_id); + + // Populates |out| with the ids of all installed extensions. + void GetExtensions(ExtensionIdList* out); + + // ExtensionScopedPrefs methods: + virtual void UpdateExtensionPref(const std::string& id, + const std::string& key, + base::Value* value) OVERRIDE; + + virtual void DeleteExtensionPrefs(const std::string& id) OVERRIDE; + + virtual bool ReadPrefAsBoolean(const std::string& extension_id, + const std::string& pref_key, + bool* out_value) const OVERRIDE; + + virtual bool ReadPrefAsInteger(const std::string& extension_id, + const std::string& pref_key, + int* out_value) const OVERRIDE; + + virtual bool ReadPrefAsString(const std::string& extension_id, + const std::string& pref_key, + std::string* out_value) const OVERRIDE; + + virtual bool ReadPrefAsList(const std::string& extension_id, + const std::string& pref_key, + const base::ListValue** out_value) const OVERRIDE; + + virtual bool ReadPrefAsDictionary( + const std::string& extension_id, + const std::string& pref_key, + const base::DictionaryValue** out_value) const OVERRIDE; + + virtual bool HasPrefForExtension(const std::string& extension_id) const + OVERRIDE; + + // Did the extension ask to escalate its permission during an upgrade? + bool DidExtensionEscalatePermissions(const std::string& id); + + // If |did_escalate| is true, the preferences for |extension| will be set to + // require the install warning when the user tries to enable. + void SetDidExtensionEscalatePermissions( + const Extension* extension, + bool did_escalate); + + // Getter and setters for disabled reason. + int GetDisableReasons(const std::string& extension_id) const; + void AddDisableReason(const std::string& extension_id, + Extension::DisableReason disable_reason); + void RemoveDisableReason(const std::string& extension_id, + Extension::DisableReason disable_reason); + void ClearDisableReasons(const std::string& extension_id); + + // Gets the set of extensions that have been blacklisted in prefs. This will + // return only the blocked extensions, not the "greylist" extensions. + // TODO(oleg): Make method names consistent here, in extension service and in + // blacklist. + std::set<std::string> GetBlacklistedExtensions(); + + // Sets whether the extension with |id| is blacklisted. + void SetExtensionBlacklisted(const std::string& extension_id, + bool is_blacklisted); + + // Returns the version string for the currently installed extension, or + // the empty string if not found. + std::string GetVersionString(const std::string& extension_id); + + // Re-writes the extension manifest into the prefs. + // Called to change the extension's manifest when it's re-localized. + void UpdateManifest(const Extension* extension); + + // Returns extension path based on extension ID, or empty FilePath on error. + base::FilePath GetExtensionPath(const std::string& extension_id); + + // Returns base extensions install directory. + const base::FilePath& install_directory() const { return install_directory_; } + + // Returns whether the extension with |id| has its blacklist bit set. + // + // WARNING: this only checks the extension's entry in prefs, so by definition + // can only check extensions that prefs knows about. There may be other + // sources of blacklist information, such as safebrowsing. You probably want + // to use Blacklist::GetBlacklistedIDs rather than this method. + bool IsExtensionBlacklisted(const std::string& id) const; + + // Increment the count of how many times we prompted the user to acknowledge + // the given extension, and return the new count. + int IncrementAcknowledgePromptCount(const std::string& extension_id); + + // Whether the user has acknowledged an external extension. + bool IsExternalExtensionAcknowledged(const std::string& extension_id); + void AcknowledgeExternalExtension(const std::string& extension_id); + + // Whether the user has acknowledged a blacklisted extension. + bool IsBlacklistedExtensionAcknowledged(const std::string& extension_id); + void AcknowledgeBlacklistedExtension(const std::string& extension_id); + + // Whether the external extension was installed during the first run + // of this profile. + bool IsExternalInstallFirstRun(const std::string& extension_id); + void SetExternalInstallFirstRun(const std::string& extension_id); + + // Whether the user has been notified about extension with |extension_id| + // being wiped out. + bool HasWipeoutBeenAcknowledged(const std::string& extension_id); + void SetWipeoutAcknowledged(const std::string& extension_id, bool value); + + // Returns true if the extension notification code has already run for the + // first time for this profile. Currently we use this flag to mean that any + // extensions that would trigger notifications should get silently + // acknowledged. This is a fuse. Calling it the first time returns false. + // Subsequent calls return true. It's not possible through an API to ever + // reset it. Don't call it unless you mean it! + bool SetAlertSystemFirstRun(); + + // Checks if extensions are blacklisted by default, by policy. + // The ManagementPolicy::Provider methods also take this into account, and + // should be used instead when the extension ID is known. + bool ExtensionsBlacklistedByDefault() const; + + // Returns the last value set via SetLastPingDay. If there isn't such a + // pref, the returned Time will return true for is_null(). + base::Time LastPingDay(const std::string& extension_id) const; + + // The time stored is based on the server's perspective of day start time, not + // the client's. + void SetLastPingDay(const std::string& extension_id, const base::Time& time); + + // Similar to the 2 above, but for the extensions blacklist. + base::Time BlacklistLastPingDay() const; + void SetBlacklistLastPingDay(const base::Time& time); + + // Similar to LastPingDay/SetLastPingDay, but for sending "days since active" + // ping. + base::Time LastActivePingDay(const std::string& extension_id); + void SetLastActivePingDay(const std::string& extension_id, + const base::Time& time); + + // A bit we use for determining if we should send the "days since active" + // ping. A value of true means the item has been active (launched) since the + // last update check. + bool GetActiveBit(const std::string& extension_id); + void SetActiveBit(const std::string& extension_id, bool active); + + // Returns the granted permission set for the extension with |extension_id|, + // and NULL if no preferences were found for |extension_id|. + // This passes ownership of the returned set to the caller. + PermissionSet* GetGrantedPermissions(const std::string& extension_id); + + // Adds |permissions| to the granted permissions set for the extension with + // |extension_id|. The new granted permissions set will be the union of + // |permissions| and the already granted permissions. + void AddGrantedPermissions(const std::string& extension_id, + const PermissionSet* permissions); + + // As above, but subtracts the given |permissions| from the granted set. + void RemoveGrantedPermissions(const std::string& extension_id, + const PermissionSet* permissions); + + // Gets the active permission set for the specified extension. This may + // differ from the permissions in the manifest due to the optional + // permissions API. This passes ownership of the set to the caller. + PermissionSet* GetActivePermissions(const std::string& extension_id); + + // Sets the active |permissions| for the extension with |extension_id|. + void SetActivePermissions(const std::string& extension_id, + const PermissionSet* permissions); + + // Records whether or not this extension is currently running. + void SetExtensionRunning(const std::string& extension_id, bool is_running); + + // Returns whether or not this extension is marked as running. This is used to + // restart apps across browser restarts. + bool IsExtensionRunning(const std::string& extension_id); + + // Set/Get whether or not the app is active. Used to force a launch of apps + // that don't handle onRestarted() on a restart. We can only safely do that if + // the app was active when it was last running. + void SetIsActive(const std::string& extension_id, bool is_active); + bool IsActive(const std::string& extension_id); + + // Returns true if the user enabled this extension to be loaded in incognito + // mode. + // + // IMPORTANT: you probably want to use extensions::util::IsIncognitoEnabled + // instead of this method. + bool IsIncognitoEnabled(const std::string& extension_id) const; + void SetIsIncognitoEnabled(const std::string& extension_id, bool enabled); + + // Returns true if the user has chosen to allow this extension to inject + // scripts into pages with file URLs. + // + // IMPORTANT: you probably want to use extensions::util::AllowFileAccess + // instead of this method. + bool AllowFileAccess(const std::string& extension_id) const; + void SetAllowFileAccess(const std::string& extension_id, bool allow); + bool HasAllowFileAccessSetting(const std::string& extension_id) const; + + // Saves ExtensionInfo for each installed extension with the path to the + // version directory and the location. Blacklisted extensions won't be saved + // and neither will external extensions the user has explicitly uninstalled. + // Caller takes ownership of returned structure. + scoped_ptr<ExtensionsInfo> GetInstalledExtensionsInfo() const; + + // Same as above, but only includes external extensions the user has + // explicitly uninstalled. + scoped_ptr<ExtensionsInfo> GetUninstalledExtensionsInfo() const; + + // Returns the ExtensionInfo from the prefs for the given extension. If the + // extension is not present, NULL is returned. + scoped_ptr<ExtensionInfo> GetInstalledExtensionInfo( + const std::string& extension_id) const; + + // We've downloaded an updated .crx file for the extension, but are waiting + // to install it. + void SetDelayedInstallInfo(const Extension* extension, + Extension::State initial_state, + bool blacklisted_for_malware, + DelayReason delay_reason, + const syncer::StringOrdinal& page_ordinal); + + // Removes any delayed install information we have for the given + // |extension_id|. Returns true if there was info to remove; false otherwise. + bool RemoveDelayedInstallInfo(const std::string& extension_id); + + // Update the prefs to finish the update for an extension. + bool FinishDelayedInstallInfo(const std::string& extension_id); + + // Returns the ExtensionInfo from the prefs for delayed install information + // for |extension_id|, if we have any. Otherwise returns NULL. + scoped_ptr<ExtensionInfo> GetDelayedInstallInfo( + const std::string& extension_id) const; + + DelayReason GetDelayedInstallReason(const std::string& extension_id) const; + + // Returns information about all the extensions that have delayed install + // information. + scoped_ptr<ExtensionsInfo> GetAllDelayedInstallInfo() const; + + // Returns true if the user repositioned the app on the app launcher via drag + // and drop. + bool WasAppDraggedByUser(const std::string& extension_id); + + // Sets a flag indicating that the user repositioned the app on the app + // launcher by drag and dropping it. + void SetAppDraggedByUser(const std::string& extension_id); + + // Returns true if there is an extension which controls the preference value + // for |pref_key| *and* it is specific to incognito mode. + bool HasIncognitoPrefValue(const std::string& pref_key); + + // Returns the creation flags mask for the extension. + int GetCreationFlags(const std::string& extension_id) const; + + // Returns the creation flags mask for a delayed install extension. + int GetDelayedInstallCreationFlags(const std::string& extension_id) const; + + // Returns true if the extension was installed from the Chrome Web Store. + bool IsFromWebStore(const std::string& extension_id) const; + + // Returns true if the extension was installed from an App generated from a + // bookmark. + bool IsFromBookmark(const std::string& extension_id) const; + + // Returns true if the extension was installed as a default app. + bool WasInstalledByDefault(const std::string& extension_id) const; + + // Helper method to acquire the installation time of an extension. + // Returns base::Time() if the installation time could not be parsed or + // found. + base::Time GetInstallTime(const std::string& extension_id) const; + + // Gets/sets the last launch time of an extension. + base::Time GetLastLaunchTime(const std::string& extension_id) const; + void SetLastLaunchTime(const std::string& extension_id, + const base::Time& time); + + static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); + + bool extensions_disabled() { return extensions_disabled_; } + + ContentSettingsStore* content_settings_store() { + return content_settings_store_.get(); + } + + // The underlying PrefService. + PrefService* pref_service() const { return prefs_; } + + // The underlying AppSorting. + AppSorting* app_sorting() const { return app_sorting_.get(); } + + // Describes the URLs that are able to install extensions. See + // pref_names::kAllowedInstallSites for more information. + URLPatternSet GetAllowedInstallSites(); + + // Schedules garbage collection of an extension's on-disk data on the next + // start of this ExtensionService. Applies only to extensions with isolated + // storage. + void SetNeedsStorageGarbageCollection(bool value); + bool NeedsStorageGarbageCollection(); + + // Used by AppWindowGeometryCache to persist its cache. These methods + // should not be called directly. + const base::DictionaryValue* GetGeometryCache( + const std::string& extension_id) const; + void SetGeometryCache(const std::string& extension_id, + scoped_ptr<base::DictionaryValue> cache); + + // Used for verification of installed extension ids. For the Set method, pass + // null to remove the preference. + const base::DictionaryValue* GetInstallSignature(); + void SetInstallSignature(const base::DictionaryValue* signature); + + private: + friend class ExtensionPrefsBlacklistedExtensions; // Unit test. + friend class ExtensionPrefsUninstallExtension; // Unit test. + + // See the Create methods. + ExtensionPrefs(PrefService* prefs, + const base::FilePath& root_dir, + ExtensionPrefValueMap* extension_pref_value_map, + scoped_ptr<AppSorting> app_sorting, + scoped_ptr<TimeProvider> time_provider, + bool extensions_disabled); + + // Converts absolute paths in the pref to paths relative to the + // install_directory_. + void MakePathsRelative(); + + // Converts internal relative paths to be absolute. Used for export to + // consumers who expect full paths. + void MakePathsAbsolute(base::DictionaryValue* dict); + + // Helper function used by GetInstalledExtensionInfo() and + // GetDelayedInstallInfo() to construct an ExtensionInfo from the provided + // |extension| dictionary. + scoped_ptr<ExtensionInfo> GetInstalledInfoHelper( + const std::string& extension_id, + const base::DictionaryValue* extension) const; + + // Interprets the list pref, |pref_key| in |extension_id|'s preferences, as a + // URLPatternSet. The |valid_schemes| specify how to parse the URLPatterns. + bool ReadPrefAsURLPatternSet(const std::string& extension_id, + const std::string& pref_key, + URLPatternSet* result, + int valid_schemes); + + // Converts |new_value| to a list of strings and sets the |pref_key| pref + // belonging to |extension_id|. + void SetExtensionPrefURLPatternSet(const std::string& extension_id, + const std::string& pref_key, + const URLPatternSet& new_value); + + // Read the boolean preference entry and return true if the preference exists + // and the preference's value is true; false otherwise. + bool ReadPrefAsBooleanAndReturn(const std::string& extension_id, + const std::string& key) const; + + // Interprets |pref_key| in |extension_id|'s preferences as an + // PermissionSet, and passes ownership of the set to the caller. + PermissionSet* ReadPrefAsPermissionSet(const std::string& extension_id, + const std::string& pref_key); + + // Converts the |new_value| to its value and sets the |pref_key| pref + // belonging to |extension_id|. + void SetExtensionPrefPermissionSet(const std::string& extension_id, + const std::string& pref_key, + const PermissionSet* new_value); + + // Returns an immutable dictionary for extension |id|'s prefs, or NULL if it + // doesn't exist. + const base::DictionaryValue* GetExtensionPref(const std::string& id) const; + + // Fix missing preference entries in the extensions that are were introduced + // in a later Chrome version. + void FixMissingPrefs(const ExtensionIdList& extension_ids); + + // Installs the persistent extension preferences into |prefs_|'s extension + // pref store. Does nothing if extensions_disabled_ is true. + void InitPrefStore(); + + // Migrates the permissions data in the pref store. + void MigratePermissions(const ExtensionIdList& extension_ids); + + // Migrates the disable reasons from a single enum to a bit mask. + void MigrateDisableReasons(const ExtensionIdList& extension_ids); + + // Checks whether there is a state pref for the extension and if so, whether + // it matches |check_state|. + bool DoesExtensionHaveState(const std::string& id, + Extension::State check_state) const; + + // Reads the list of strings for |pref| from user prefs into + // |id_container_out|. Returns false if the pref wasn't found in the user + // pref store. + template <class ExtensionIdContainer> + bool GetUserExtensionPrefIntoContainer( + const char* pref, + ExtensionIdContainer* id_container_out); + + // Writes the list of strings contained in |strings| to |pref| in prefs. + template <class ExtensionIdContainer> + void SetExtensionPrefFromContainer(const char* pref, + const ExtensionIdContainer& strings); + + // Helper function to populate |extension_dict| with the values needed + // by a newly installed extension. Work is broken up between this + // function and FinishExtensionInfoPrefs() to accomodate delayed + // installations. + void PopulateExtensionInfoPrefs(const Extension* extension, + const base::Time install_time, + Extension::State initial_state, + bool blacklisted_for_malware, + base::DictionaryValue* extension_dict); + + // Helper function to complete initialization of the values in + // |extension_dict| for an extension install. Also see + // PopulateExtensionInfoPrefs(). + void FinishExtensionInfoPrefs( + const std::string& extension_id, + const base::Time install_time, + bool needs_sort_ordinal, + const syncer::StringOrdinal& suggested_page_ordinal, + base::DictionaryValue* extension_dict); + + // The pref service specific to this set of extension prefs. Owned by the + // BrowserContext. + PrefService* prefs_; + + // Base extensions install directory. + base::FilePath install_directory_; + + // Weak pointer, owned by BrowserContext. + ExtensionPrefValueMap* extension_pref_value_map_; + + // Contains all the logic for handling the order for various extension + // properties. + scoped_ptr<AppSorting> app_sorting_; + + scoped_refptr<ContentSettingsStore> content_settings_store_; + + scoped_ptr<TimeProvider> time_provider_; + + bool extensions_disabled_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionPrefs); +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_EXTENSION_PREFS_H_ diff --git a/extensions/browser/extension_prefs_factory.cc b/extensions/browser/extension_prefs_factory.cc new file mode 100644 index 0000000000..e83e33b27a --- /dev/null +++ b/extensions/browser/extension_prefs_factory.cc @@ -0,0 +1,61 @@ +// Copyright 2014 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/command_line.h" +#include "base/prefs/pref_service.h" +#include "components/browser_context_keyed_service/browser_context_dependency_manager.h" +#include "content/public/browser/browser_context.h" +#include "extensions/browser/extension_pref_value_map.h" +#include "extensions/browser/extension_pref_value_map_factory.h" +#include "extensions/browser/extension_prefs.h" +#include "extensions/browser/extension_prefs_factory.h" +#include "extensions/browser/extensions_browser_client.h" +#include "extensions/common/constants.h" + +namespace extensions { + +// static +ExtensionPrefs* ExtensionPrefsFactory::GetForBrowserContext( + content::BrowserContext* context) { + return static_cast<ExtensionPrefs*>( + GetInstance()->GetServiceForBrowserContext(context, true)); +} + +// static +ExtensionPrefsFactory* ExtensionPrefsFactory::GetInstance() { + return Singleton<ExtensionPrefsFactory>::get(); +} + +void ExtensionPrefsFactory::SetInstanceForTesting( + content::BrowserContext* context, ExtensionPrefs* prefs) { + Associate(context, prefs); +} + +ExtensionPrefsFactory::ExtensionPrefsFactory() + : BrowserContextKeyedServiceFactory( + "ExtensionPrefs", + BrowserContextDependencyManager::GetInstance()) { +} + +ExtensionPrefsFactory::~ExtensionPrefsFactory() { +} + +BrowserContextKeyedService* ExtensionPrefsFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + ExtensionsBrowserClient* client = ExtensionsBrowserClient::Get(); + return ExtensionPrefs::Create( + client->GetPrefServiceForContext(context), + context->GetPath().AppendASCII(extensions::kInstallDirectoryName), + ExtensionPrefValueMapFactory::GetForBrowserContext(context), + client->CreateAppSorting().Pass(), + client->AreExtensionsDisabled( + *CommandLine::ForCurrentProcess(), context)); +} + +content::BrowserContext* ExtensionPrefsFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return ExtensionsBrowserClient::Get()->GetOriginalContext(context); +} + +} // namespace extensions diff --git a/extensions/browser/extension_prefs_factory.h b/extensions/browser/extension_prefs_factory.h new file mode 100644 index 0000000000..3aee29ace1 --- /dev/null +++ b/extensions/browser/extension_prefs_factory.h @@ -0,0 +1,39 @@ +// Copyright 2014 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_FACTORY_H_ +#define EXTENSIONS_BROWSER_EXTENSION_PREFS_FACTORY_H_ + +#include "base/memory/scoped_ptr.h" +#include "base/memory/singleton.h" +#include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h" + +namespace extensions { + +class ExtensionPrefs; + +class ExtensionPrefsFactory : public BrowserContextKeyedServiceFactory { + public: + static ExtensionPrefs* GetForBrowserContext(content::BrowserContext* context); + + static ExtensionPrefsFactory* GetInstance(); + + void SetInstanceForTesting( + content::BrowserContext* context, ExtensionPrefs* prefs); + + private: + friend struct DefaultSingletonTraits<ExtensionPrefsFactory>; + + ExtensionPrefsFactory(); + virtual ~ExtensionPrefsFactory(); + + virtual BrowserContextKeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const OVERRIDE; + virtual content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const OVERRIDE; +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_EXTENSION_PREFS_FACTORY_H_ diff --git a/extensions/browser/extension_registry.cc b/extensions/browser/extension_registry.cc new file mode 100644 index 0000000000..5c4cf5d4a9 --- /dev/null +++ b/extensions/browser/extension_registry.cc @@ -0,0 +1,114 @@ +// 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_registry.h" + +#include "base/strings/string_util.h" +#include "extensions/browser/extension_registry_factory.h" +#include "extensions/browser/extension_registry_observer.h" + +namespace extensions { + +ExtensionRegistry::ExtensionRegistry() {} +ExtensionRegistry::~ExtensionRegistry() {} + +// static +ExtensionRegistry* ExtensionRegistry::Get(content::BrowserContext* context) { + return ExtensionRegistryFactory::GetForBrowserContext(context); +} + +void ExtensionRegistry::AddObserver(ExtensionRegistryObserver* observer) { + observers_.AddObserver(observer); +} + +void ExtensionRegistry::RemoveObserver(ExtensionRegistryObserver* observer) { + observers_.RemoveObserver(observer); +} + +void ExtensionRegistry::TriggerOnUnloaded(const Extension* extension) { + DCHECK(!enabled_extensions_.Contains(extension->id())); + FOR_EACH_OBSERVER( + ExtensionRegistryObserver, observers_, OnExtensionUnloaded(extension)); +} + +const Extension* ExtensionRegistry::GetExtensionById(const std::string& id, + int include_mask) const { + std::string lowercase_id = StringToLowerASCII(id); + if (include_mask & ENABLED) { + const Extension* extension = enabled_extensions_.GetByID(lowercase_id); + if (extension) + return extension; + } + if (include_mask & DISABLED) { + const Extension* extension = disabled_extensions_.GetByID(lowercase_id); + if (extension) + return extension; + } + if (include_mask & TERMINATED) { + const Extension* extension = terminated_extensions_.GetByID(lowercase_id); + if (extension) + return extension; + } + if (include_mask & BLACKLISTED) { + const Extension* extension = blacklisted_extensions_.GetByID(lowercase_id); + if (extension) + return extension; + } + return NULL; +} + +bool ExtensionRegistry::AddEnabled( + const scoped_refptr<const Extension>& extension) { + return enabled_extensions_.Insert(extension); +} + +bool ExtensionRegistry::RemoveEnabled(const std::string& id) { + return enabled_extensions_.Remove(id); +} + +bool ExtensionRegistry::AddDisabled( + const scoped_refptr<const Extension>& extension) { + return disabled_extensions_.Insert(extension); +} + +bool ExtensionRegistry::RemoveDisabled(const std::string& id) { + return disabled_extensions_.Remove(id); +} + +bool ExtensionRegistry::AddTerminated( + const scoped_refptr<const Extension>& extension) { + return terminated_extensions_.Insert(extension); +} + +bool ExtensionRegistry::RemoveTerminated(const std::string& id) { + return terminated_extensions_.Remove(id); +} + +bool ExtensionRegistry::AddBlacklisted( + const scoped_refptr<const Extension>& extension) { + return blacklisted_extensions_.Insert(extension); +} + +bool ExtensionRegistry::RemoveBlacklisted(const std::string& id) { + return blacklisted_extensions_.Remove(id); +} + +void ExtensionRegistry::ClearAll() { + enabled_extensions_.Clear(); + disabled_extensions_.Clear(); + terminated_extensions_.Clear(); + blacklisted_extensions_.Clear(); +} + +void ExtensionRegistry::SetDisabledModificationCallback( + const ExtensionSet::ModificationCallback& callback) { + disabled_extensions_.set_modification_callback(callback); +} + +void ExtensionRegistry::Shutdown() { + // Release references to all Extension objects in the sets. + ClearAll(); +} + +} // namespace extensions diff --git a/extensions/browser/extension_registry.h b/extensions/browser/extension_registry.h new file mode 100644 index 0000000000..89256e1583 --- /dev/null +++ b/extensions/browser/extension_registry.h @@ -0,0 +1,137 @@ +// 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_REGISTRY_H_ +#define EXTENSIONS_BROWSER_EXTENSION_REGISTRY_H_ + +#include <string> + +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "base/observer_list.h" +#include "components/browser_context_keyed_service/browser_context_keyed_service.h" +#include "extensions/common/extension_set.h" + +namespace content { +class BrowserContext; +} + +namespace extensions { +class Extension; +class ExtensionRegistryObserver; + +// ExtensionRegistry holds sets of the installed extensions for a given +// BrowserContext. An incognito browser context and its master browser context +// share a single registry. +class ExtensionRegistry : public BrowserContextKeyedService { + public: + // Flags to pass to GetExtensionById() to select which sets to look in. + enum IncludeFlag { + NONE = 0, + ENABLED = 1 << 0, + DISABLED = 1 << 1, + TERMINATED = 1 << 2, + BLACKLISTED = 1 << 3, + EVERYTHING = (1 << 4) - 1, + }; + + ExtensionRegistry(); + virtual ~ExtensionRegistry(); + + // Returns the instance for the given |browser_context|. + static ExtensionRegistry* Get(content::BrowserContext* browser_context); + + // NOTE: These sets are *eventually* mututally exclusive, but an extension can + // appear in two sets for short periods of time. + const ExtensionSet& enabled_extensions() const { + return enabled_extensions_; + } + const ExtensionSet& disabled_extensions() const { + return disabled_extensions_; + } + const ExtensionSet& terminated_extensions() const { + return terminated_extensions_; + } + const ExtensionSet& blacklisted_extensions() const { + return blacklisted_extensions_; + } + + // The usual observer interface. + void AddObserver(ExtensionRegistryObserver* observer); + void RemoveObserver(ExtensionRegistryObserver* observer); + + // Invokes the observer method OnExtensionUnloaded(). The extension must not + // be enabled at the time of the call. + void TriggerOnUnloaded(const Extension* extension); + + // Find an extension by ID using |include_mask| to pick the sets to search: + // * enabled_extensions() --> ExtensionRegistry::ENABLED + // * disabled_extensions() --> ExtensionRegistry::DISABLED + // * terminated_extensions() --> ExtensionRegistry::TERMINATED + // * blacklisted_extensions() --> ExtensionRegistry::BLACKLISTED + // Returns NULL if the extension is not found in the selected sets. + const Extension* GetExtensionById(const std::string& id, + int include_mask) const; + + // Adds the specified extension to the enabled set. The registry becomes an + // owner. Any previous extension with the same ID is removed. + // Returns true if there is no previous extension. + // NOTE: You probably want to use ExtensionService instead of calling this + // method directly. + bool AddEnabled(const scoped_refptr<const Extension>& extension); + + // Removes the specified extension from the enabled set. + // Returns true if the set contained the specified extension. + // NOTE: You probably want to use ExtensionService instead of calling this + // method directly. + bool RemoveEnabled(const std::string& id); + + // As above, but for the disabled set. + bool AddDisabled(const scoped_refptr<const Extension>& extension); + bool RemoveDisabled(const std::string& id); + + // As above, but for the terminated set. + bool AddTerminated(const scoped_refptr<const Extension>& extension); + bool RemoveTerminated(const std::string& id); + + // As above, but for the blacklisted set. + bool AddBlacklisted(const scoped_refptr<const Extension>& extension); + bool RemoveBlacklisted(const std::string& id); + + // Removes all extensions from all sets. + void ClearAll(); + + // Sets a callback to run when the disabled extension set is modified. + // TODO(jamescook): This is too specific for a generic registry; find some + // other way to do this. + void SetDisabledModificationCallback( + const ExtensionSet::ModificationCallback& callback); + + // BrowserContextKeyedService implementation: + virtual void Shutdown() OVERRIDE; + + private: + // Extensions that are installed, enabled and not terminated. + ExtensionSet enabled_extensions_; + + // Extensions that are installed and disabled. + ExtensionSet disabled_extensions_; + + // Extensions that are installed and terminated. + ExtensionSet terminated_extensions_; + + // Extensions that are installed and blacklisted. Generally these shouldn't be + // considered as installed by the extension platform: we only keep them around + // so that if extensions are blacklisted by mistake they can easily be + // un-blacklisted. + ExtensionSet blacklisted_extensions_; + + ObserverList<ExtensionRegistryObserver> observers_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionRegistry); +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_EXTENSION_REGISTRY_H_ diff --git a/extensions/browser/extension_registry_factory.cc b/extensions/browser/extension_registry_factory.cc new file mode 100644 index 0000000000..9f02012dd5 --- /dev/null +++ b/extensions/browser/extension_registry_factory.cc @@ -0,0 +1,47 @@ +// 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_registry_factory.h" + +#include "components/browser_context_keyed_service/browser_context_dependency_manager.h" +#include "extensions/browser/extension_registry.h" +#include "extensions/browser/extensions_browser_client.h" + +using content::BrowserContext; + +namespace extensions { + +// static +ExtensionRegistry* ExtensionRegistryFactory::GetForBrowserContext( + BrowserContext* context) { + return static_cast<ExtensionRegistry*>( + GetInstance()->GetServiceForBrowserContext(context, true)); +} + +// static +ExtensionRegistryFactory* ExtensionRegistryFactory::GetInstance() { + return Singleton<ExtensionRegistryFactory>::get(); +} + +ExtensionRegistryFactory::ExtensionRegistryFactory() + : BrowserContextKeyedServiceFactory( + "ExtensionRegistry", + BrowserContextDependencyManager::GetInstance()) { + // No dependencies on other services. +} + +ExtensionRegistryFactory::~ExtensionRegistryFactory() {} + +BrowserContextKeyedService* ExtensionRegistryFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + return new ExtensionRegistry; +} + +BrowserContext* ExtensionRegistryFactory::GetBrowserContextToUse( + BrowserContext* context) const { + // Redirected in incognito. + return ExtensionsBrowserClient::Get()->GetOriginalContext(context); +} + +} // namespace extensions diff --git a/extensions/browser/extension_registry_factory.h b/extensions/browser/extension_registry_factory.h new file mode 100644 index 0000000000..5b06c4f986 --- /dev/null +++ b/extensions/browser/extension_registry_factory.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_EXTENSION_REGISTRY_FACTORY_H_ +#define EXTENSIONS_BROWSER_EXTENSION_REGISTRY_FACTORY_H_ + +#include "base/compiler_specific.h" +#include "base/memory/singleton.h" +#include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h" + +namespace extensions { + +class ExtensionRegistry; + +// Factory for ExtensionRegistry objects. ExtensionRegistry objects are shared +// between an incognito browser context and its master browser context. +class ExtensionRegistryFactory : public BrowserContextKeyedServiceFactory { + public: + static ExtensionRegistry* GetForBrowserContext( + content::BrowserContext* context); + + static ExtensionRegistryFactory* GetInstance(); + + private: + friend struct DefaultSingletonTraits<ExtensionRegistryFactory>; + + ExtensionRegistryFactory(); + virtual ~ExtensionRegistryFactory(); + + // BrowserContextKeyedServiceFactory implementation: + virtual BrowserContextKeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const OVERRIDE; + virtual content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(ExtensionRegistryFactory); +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_EXTENSION_REGISTRY_FACTORY_H_ diff --git a/extensions/browser/extension_registry_observer.h b/extensions/browser/extension_registry_observer.h new file mode 100644 index 0000000000..33828850e5 --- /dev/null +++ b/extensions/browser/extension_registry_observer.h @@ -0,0 +1,25 @@ +// Copyright 2014 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_REGISTRY_OBSERVER_H_ +#define EXTENSIONS_BROWSER_EXTENSION_REGISTRY_OBSERVER_H_ + +namespace extensions { + +class Extension; + +// Observer for ExtensionRegistry. Exists in a separate header file to reduce +// the include file burden for typical clients of ExtensionRegistry. +class ExtensionRegistryObserver { + public: + virtual ~ExtensionRegistryObserver() {} + + // Called after an extension is unloaded. The extension no longer exists in + // any of the ExtensionRegistry sets (enabled, disabled, etc.). + virtual void OnExtensionUnloaded(const Extension* extension) {} +}; + +} + +#endif // EXTENSIONS_BROWSER_EXTENSION_REGISTRY_OBSERVER_H_ diff --git a/extensions/browser/extension_registry_unittest.cc b/extensions/browser/extension_registry_unittest.cc new file mode 100644 index 0000000000..075019b244 --- /dev/null +++ b/extensions/browser/extension_registry_unittest.cc @@ -0,0 +1,169 @@ +// 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_registry.h" + +#include <string> + +#include "base/memory/ref_counted.h" +#include "extensions/common/extension.h" +#include "extensions/common/test_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace extensions { +namespace { + +typedef testing::Test ExtensionRegistryTest; + +TEST_F(ExtensionRegistryTest, FillAndClearRegistry) { + ExtensionRegistry registry; + scoped_refptr<Extension> extension1 = test_util::CreateExtensionWithID("id1"); + scoped_refptr<Extension> extension2 = test_util::CreateExtensionWithID("id2"); + scoped_refptr<Extension> extension3 = test_util::CreateExtensionWithID("id3"); + scoped_refptr<Extension> extension4 = test_util::CreateExtensionWithID("id4"); + + // All the sets start empty. + EXPECT_EQ(0u, registry.enabled_extensions().size()); + EXPECT_EQ(0u, registry.disabled_extensions().size()); + EXPECT_EQ(0u, registry.terminated_extensions().size()); + EXPECT_EQ(0u, registry.blacklisted_extensions().size()); + + // Extensions can be added to each set. + registry.AddEnabled(extension1); + registry.AddDisabled(extension2); + registry.AddTerminated(extension3); + registry.AddBlacklisted(extension4); + + EXPECT_EQ(1u, registry.enabled_extensions().size()); + EXPECT_EQ(1u, registry.disabled_extensions().size()); + EXPECT_EQ(1u, registry.terminated_extensions().size()); + EXPECT_EQ(1u, registry.blacklisted_extensions().size()); + + // Clearing the registry clears all sets. + registry.ClearAll(); + + EXPECT_EQ(0u, registry.enabled_extensions().size()); + EXPECT_EQ(0u, registry.disabled_extensions().size()); + EXPECT_EQ(0u, registry.terminated_extensions().size()); + EXPECT_EQ(0u, registry.blacklisted_extensions().size()); +} + +// A simple test of adding and removing things from sets. +TEST_F(ExtensionRegistryTest, AddAndRemoveExtensionFromRegistry) { + ExtensionRegistry registry; + + // Adding an extension works. + scoped_refptr<Extension> extension = test_util::CreateExtensionWithID("id"); + EXPECT_TRUE(registry.AddEnabled(extension)); + EXPECT_EQ(1u, registry.enabled_extensions().size()); + + // The extension was only added to one set. + EXPECT_EQ(0u, registry.disabled_extensions().size()); + EXPECT_EQ(0u, registry.terminated_extensions().size()); + EXPECT_EQ(0u, registry.blacklisted_extensions().size()); + + // Removing an extension works. + EXPECT_TRUE(registry.RemoveEnabled(extension->id())); + EXPECT_EQ(0u, registry.enabled_extensions().size()); + + // Trying to remove an extension that isn't in the set fails cleanly. + EXPECT_FALSE(registry.RemoveEnabled(extension->id())); +} + +TEST_F(ExtensionRegistryTest, AddExtensionToRegistryTwice) { + ExtensionRegistry registry; + scoped_refptr<Extension> extension = test_util::CreateExtensionWithID("id"); + + // An extension can exist in two sets at once. It would be nice to eliminate + // this functionality, but some users of ExtensionRegistry need it. + EXPECT_TRUE(registry.AddEnabled(extension)); + EXPECT_TRUE(registry.AddDisabled(extension)); + + EXPECT_EQ(1u, registry.enabled_extensions().size()); + EXPECT_EQ(1u, registry.disabled_extensions().size()); + EXPECT_EQ(0u, registry.terminated_extensions().size()); + EXPECT_EQ(0u, registry.blacklisted_extensions().size()); +} + +TEST_F(ExtensionRegistryTest, GetExtensionById) { + ExtensionRegistry registry; + + // Trying to get an extension fails cleanly when the sets are empty. + EXPECT_FALSE( + registry.GetExtensionById("id", ExtensionRegistry::EVERYTHING)); + + scoped_refptr<Extension> enabled = + test_util::CreateExtensionWithID("enabled"); + scoped_refptr<Extension> disabled = + test_util::CreateExtensionWithID("disabled"); + scoped_refptr<Extension> terminated = + test_util::CreateExtensionWithID("terminated"); + scoped_refptr<Extension> blacklisted = + test_util::CreateExtensionWithID("blacklisted"); + + // Add an extension to each set. + registry.AddEnabled(enabled); + registry.AddDisabled(disabled); + registry.AddTerminated(terminated); + registry.AddBlacklisted(blacklisted); + + // Enabled is part of everything and the enabled list. + EXPECT_TRUE( + registry.GetExtensionById("enabled", ExtensionRegistry::EVERYTHING)); + EXPECT_TRUE( + registry.GetExtensionById("enabled", ExtensionRegistry::ENABLED)); + EXPECT_FALSE( + registry.GetExtensionById("enabled", ExtensionRegistry::DISABLED)); + EXPECT_FALSE( + registry.GetExtensionById("enabled", ExtensionRegistry::TERMINATED)); + EXPECT_FALSE( + registry.GetExtensionById("enabled", ExtensionRegistry::BLACKLISTED)); + + // Disabled is part of everything and the disabled list. + EXPECT_TRUE( + registry.GetExtensionById("disabled", ExtensionRegistry::EVERYTHING)); + EXPECT_FALSE( + registry.GetExtensionById("disabled", ExtensionRegistry::ENABLED)); + EXPECT_TRUE( + registry.GetExtensionById("disabled", ExtensionRegistry::DISABLED)); + EXPECT_FALSE( + registry.GetExtensionById("disabled", ExtensionRegistry::TERMINATED)); + EXPECT_FALSE( + registry.GetExtensionById("disabled", ExtensionRegistry::BLACKLISTED)); + + // Terminated is part of everything and the terminated list. + EXPECT_TRUE( + registry.GetExtensionById("terminated", ExtensionRegistry::EVERYTHING)); + EXPECT_FALSE( + registry.GetExtensionById("terminated", ExtensionRegistry::ENABLED)); + EXPECT_FALSE( + registry.GetExtensionById("terminated", ExtensionRegistry::DISABLED)); + EXPECT_TRUE( + registry.GetExtensionById("terminated", ExtensionRegistry::TERMINATED)); + EXPECT_FALSE( + registry.GetExtensionById("terminated", ExtensionRegistry::BLACKLISTED)); + + // Blacklisted is part of everything and the blacklisted list. + EXPECT_TRUE( + registry.GetExtensionById("blacklisted", ExtensionRegistry::EVERYTHING)); + EXPECT_FALSE( + registry.GetExtensionById("blacklisted", ExtensionRegistry::ENABLED)); + EXPECT_FALSE( + registry.GetExtensionById("blacklisted", ExtensionRegistry::DISABLED)); + EXPECT_FALSE( + registry.GetExtensionById("blacklisted", ExtensionRegistry::TERMINATED)); + EXPECT_TRUE( + registry.GetExtensionById("blacklisted", ExtensionRegistry::BLACKLISTED)); + + // Enabled can be found with multiple flags set. + EXPECT_TRUE(registry.GetExtensionById( + "enabled", ExtensionRegistry::ENABLED | ExtensionRegistry::TERMINATED)); + + // Enabled isn't found if the wrong flags are set. + EXPECT_FALSE(registry.GetExtensionById( + "enabled", ExtensionRegistry::DISABLED | ExtensionRegistry::BLACKLISTED)); +} + +} // namespace +} // namespace extensions diff --git a/extensions/browser/extension_scoped_prefs.h b/extensions/browser/extension_scoped_prefs.h new file mode 100644 index 0000000000..15fc577f2d --- /dev/null +++ b/extensions/browser/extension_scoped_prefs.h @@ -0,0 +1,56 @@ +// Copyright 2014 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_SCOPED_PREFS_H_ +#define EXTENSIONS_BROWSER_EXTENSION_SCOPED_PREFS_H_ + +namespace extensions { + +class ExtensionScopedPrefs { + public: + ExtensionScopedPrefs() {} + ~ExtensionScopedPrefs() {} + + // Sets the pref |key| for extension |id| to |value|. + virtual void UpdateExtensionPref(const std::string& id, + const std::string& key, + base::Value* value) = 0; + + // Deletes the pref dictionary for extension |id|. + virtual void DeleteExtensionPrefs(const std::string& id) = 0; + + // Reads a boolean pref |pref_key| from extension with id |extension_id|. + virtual bool ReadPrefAsBoolean(const std::string& extension_id, + const std::string& pref_key, + bool* out_value) const = 0; + + // Reads an integer pref |pref_key| from extension with id |extension_id|. + virtual bool ReadPrefAsInteger(const std::string& extension_id, + const std::string& pref_key, + int* out_value) const = 0; + + // Reads a string pref |pref_key| from extension with id |extension_id|. + virtual bool ReadPrefAsString(const std::string& extension_id, + const std::string& pref_key, + std::string* out_value) const = 0; + + // Reads a list pref |pref_key| from extension with id |extension_id|. + virtual bool ReadPrefAsList(const std::string& extension_id, + const std::string& pref_key, + const base::ListValue** out_value) const = 0; + + // Reads a dictionary pref |pref_key| from extension with id |extension_id|. + virtual bool ReadPrefAsDictionary( + const std::string& extension_id, + const std::string& pref_key, + const base::DictionaryValue** out_value) const = 0; + + // Returns true if the prefs contain an entry for an extension with id + // |extension_id|. + virtual bool HasPrefForExtension(const std::string& extension_id) const = 0; +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_EXTENSION_SCOPED_PREFS_H_ diff --git a/extensions/browser/extension_system.cc b/extensions/browser/extension_system.cc new file mode 100644 index 0000000000..910e7d9021 --- /dev/null +++ b/extensions/browser/extension_system.cc @@ -0,0 +1,26 @@ +// Copyright 2014 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_system.h" + +#include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h" +#include "extensions/browser/extension_system_provider.h" +#include "extensions/browser/extensions_browser_client.h" + +namespace extensions { + +ExtensionSystem::ExtensionSystem() { +} + +ExtensionSystem::~ExtensionSystem() { +} + +// static +ExtensionSystem* ExtensionSystem::Get(content::BrowserContext* context) { + return ExtensionsBrowserClient::Get() + ->GetExtensionSystemFactory() + ->GetForBrowserContext(context); +} + +} // namespace extensions diff --git a/extensions/browser/extension_system.h b/extensions/browser/extension_system.h new file mode 100644 index 0000000000..b1e724c2bb --- /dev/null +++ b/extensions/browser/extension_system.h @@ -0,0 +1,131 @@ +// Copyright 2014 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_SYSTEM_H_ +#define EXTENSIONS_BROWSER_EXTENSION_SYSTEM_H_ + +#include <string> + +#include "base/memory/ref_counted.h" +#include "components/browser_context_keyed_service/browser_context_keyed_service.h" +#include "extensions/common/extension.h" + +class ExtensionService; + +#if defined(OS_CHROMEOS) +namespace chromeos { +class DeviceLocalAccountManagementPolicyProvider; +} +#endif // defined(OS_CHROMEOS) + +namespace content { +class BrowserContext; +} + +namespace extensions { + +class Blacklist; +class ErrorConsole; +class EventRouter; +class Extension; +class ExtensionWarningService; +class InfoMap; +class InstallVerifier; +class LazyBackgroundTaskQueue; +class ManagementPolicy; +class OneShotEvent; +class ProcessManager; +class QuotaService; +class RuntimeData; +class StateStore; +class UserScriptMaster; + +// ExtensionSystem manages the lifetime of many of the services used by the +// extensions and apps system, and it handles startup and shutdown as needed. +// Eventually, we'd like to make more of these services into +// BrowserContextKeyedServices in their own right. +class ExtensionSystem : public BrowserContextKeyedService { + public: + ExtensionSystem(); + virtual ~ExtensionSystem(); + + // Returns the instance for the given browser context, or NULL if none. + static ExtensionSystem* Get(content::BrowserContext* context); + + // Initializes extensions machinery. + // Component extensions are always enabled, external and user extensions are + // controlled by |extensions_enabled|. + virtual void InitForRegularProfile(bool extensions_enabled) = 0; + + // The ExtensionService is created at startup. + virtual ExtensionService* extension_service() = 0; + + // Per-extension data that can change during the life of the process but + // does not persist across restarts. Lives on UI thread. Created at startup. + virtual RuntimeData* runtime_data() = 0; + + // The class controlling whether users are permitted to perform certain + // actions on extensions (install, uninstall, disable, etc.). + // The ManagementPolicy is created at startup. + virtual ManagementPolicy* management_policy() = 0; + + // The UserScriptMaster is created at startup. + virtual UserScriptMaster* user_script_master() = 0; + + // The ProcessManager is created at startup. + virtual ProcessManager* process_manager() = 0; + + // The StateStore is created at startup. + virtual StateStore* state_store() = 0; + + // The rules store is created at startup. + virtual StateStore* rules_store() = 0; + + // Returns the IO-thread-accessible extension data. + virtual InfoMap* info_map() = 0; + + // The LazyBackgroundTaskQueue is created at startup. + virtual LazyBackgroundTaskQueue* lazy_background_task_queue() = 0; + + // The EventRouter is created at startup. + virtual EventRouter* event_router() = 0; + + // The ExtensionWarningService is created at startup. + virtual ExtensionWarningService* warning_service() = 0; + + // The blacklist is created at startup. + virtual Blacklist* blacklist() = 0; + + // The ErrorConsole is created at startup. + virtual ErrorConsole* error_console() = 0; + + // The InstallVerifier is created at startup. + virtual InstallVerifier* install_verifier() = 0; + + // Returns the QuotaService that limits calls to certain extension functions. + // Lives on the UI thread. Created at startup. + virtual QuotaService* quota_service() = 0; + + // Called by the ExtensionService that lives in this system. Gives the + // info map a chance to react to the load event before the EXTENSION_LOADED + // notification has fired. The purpose for handling this event first is to + // avoid race conditions by making sure URLRequestContexts learn about new + // extensions before anything else needs them to know. + virtual void RegisterExtensionWithRequestContexts( + const Extension* extension) {} + + // Called by the ExtensionService that lives in this system. Lets the + // info map clean up its RequestContexts once all the listeners to the + // EXTENSION_UNLOADED notification have finished running. + virtual void UnregisterExtensionWithRequestContexts( + const std::string& extension_id, + const UnloadedExtensionInfo::Reason reason) {} + + // Signaled when the extension system has completed its startup tasks. + virtual const OneShotEvent& ready() const = 0; +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_EXTENSION_SYSTEM_H_ diff --git a/extensions/browser/extension_system_provider.cc b/extensions/browser/extension_system_provider.cc new file mode 100644 index 0000000000..3cc6adb0b1 --- /dev/null +++ b/extensions/browser/extension_system_provider.cc @@ -0,0 +1,18 @@ +// Copyright 2014 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_system_provider.h" + +#include "components/browser_context_keyed_service/browser_context_dependency_manager.h" + +namespace extensions { + +ExtensionSystemProvider::ExtensionSystemProvider( + const char* name, + BrowserContextDependencyManager* manager) + : BrowserContextKeyedServiceFactory(name, manager) {} + +ExtensionSystemProvider::~ExtensionSystemProvider() {} + +} // namespace extensions diff --git a/extensions/browser/extension_system_provider.h b/extensions/browser/extension_system_provider.h new file mode 100644 index 0000000000..45ca56c185 --- /dev/null +++ b/extensions/browser/extension_system_provider.h @@ -0,0 +1,35 @@ +// Copyright 2014 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_SYSTEM_PROVIDER_H_ +#define EXTENSIONS_BROWSER_EXTENSION_SYSTEM_PROVIDER_H_ + +#include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h" + +class BrowserContextDependencyManager; + +namespace content { +class BrowserContext; +} + +namespace extensions { +class ExtensionSystem; + +// An ExtensionSystemProvider maps a BrowserContext to its ExtensionSystem. +// Different applications may use this to provide differing implementations +// of ExtensionSystem. +// TODO(yoz): Rename to ExtensionSystemFactory. +class ExtensionSystemProvider : public BrowserContextKeyedServiceFactory { + public: + ExtensionSystemProvider(const char* name, + BrowserContextDependencyManager* manager); + virtual ~ExtensionSystemProvider(); + + virtual ExtensionSystem* GetForBrowserContext( + content::BrowserContext* context) = 0; +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_EXTENSION_SYSTEM_PROVIDER_H_ diff --git a/extensions/browser/extensions_browser_client.h b/extensions/browser/extensions_browser_client.h index 61641a7931..08ffa586f1 100644 --- a/extensions/browser/extensions_browser_client.h +++ b/extensions/browser/extensions_browser_client.h @@ -5,6 +5,9 @@ #ifndef EXTENSIONS_BROWSER_EXTENSIONS_BROWSER_CLIENT_H_ #define EXTENSIONS_BROWSER_EXTENSIONS_BROWSER_CLIENT_H_ +#include <string> +#include <vector> + #include "base/memory/scoped_ptr.h" class CommandLine; @@ -12,11 +15,18 @@ class PrefService; namespace content { class BrowserContext; +class JavaScriptDialogManager; +class WebContents; } namespace extensions { +class ApiActivityMonitor; class AppSorting; +class Extension; +class ExtensionHost; +class ExtensionSystem; +class ExtensionSystemProvider; // Interface to allow the extensions module to make browser-process-specific // queries of the embedder. Should be Set() once in the browser process. @@ -54,11 +64,25 @@ class ExtensionsBrowserClient { virtual content::BrowserContext* GetOffTheRecordContext( content::BrowserContext* context) = 0; - // Return the original "recording" context. This method returns |context| if + // Returns the original "recording" context. This method returns |context| if // |context| is not incognito. virtual content::BrowserContext* GetOriginalContext( content::BrowserContext* context) = 0; + // Returns true if |context| corresponds to a guest session. + virtual bool IsGuestSession(content::BrowserContext* context) = 0; + + // Returns true if |extension_id| can run in an incognito window. + virtual bool IsExtensionIncognitoEnabled( + const std::string& extension_id, + content::BrowserContext* context) const = 0; + + // Returns true if |extension| can see events and data from another + // sub-profile (incognito to original profile, or vice versa). + virtual bool CanExtensionCrossIncognito( + const extensions::Extension* extension, + content::BrowserContext* context) const = 0; + // Returns the PrefService associated with |context|. virtual PrefService* GetPrefServiceForContext( content::BrowserContext* context) = 0; @@ -67,6 +91,16 @@ class ExtensionsBrowserClient { virtual bool DeferLoadingBackgroundHosts( content::BrowserContext* context) const = 0; + virtual bool IsBackgroundPageAllowed( + content::BrowserContext* context) const = 0; + + // Called after the hosting |web_contents| for an extension is created. The + // implementation may wish to add preference observers to |web_contents|. + virtual void OnExtensionHostCreated(content::WebContents* web_contents) = 0; + + // Called after |host| creates a RenderView for an extension. + virtual void OnRenderViewCreatedForBackgroundPage(ExtensionHost* host) = 0; + // Returns true if the client version has updated since the last run. Called // once each time the extensions system is loaded per browser_context. The // implementation may wish to use the BrowserContext to record the current @@ -79,6 +113,19 @@ class ExtensionsBrowserClient { // Return true if the system is run in forced app mode. virtual bool IsRunningInForcedAppMode() = 0; + // Returns the embedder's JavaScriptDialogManager or NULL if the embedder + // does not support JavaScript dialogs. + virtual content::JavaScriptDialogManager* GetJavaScriptDialogManager() = 0; + + // Returns the embedder's ApiActivityMonitor for |context|. Returns NULL if + // the embedder does not monitor extension API activity. + virtual ApiActivityMonitor* GetApiActivityMonitor( + content::BrowserContext* context) = 0; + + // Returns the factory that provides an ExtensionSystem to be returned from + // ExtensionSystem::Get. + virtual ExtensionSystemProvider* GetExtensionSystemFactory() = 0; + // Returns the single instance of |this|. static ExtensionsBrowserClient* Get(); diff --git a/extensions/browser/info_map.cc b/extensions/browser/info_map.cc index 2bff206cdd..7122eb0572 100644 --- a/extensions/browser/info_map.cc +++ b/extensions/browser/info_map.cc @@ -4,10 +4,10 @@ #include "extensions/browser/info_map.h" -#include "chrome/common/extensions/extension_set.h" #include "content/public/browser/browser_thread.h" #include "extensions/common/constants.h" #include "extensions/common/extension.h" +#include "extensions/common/extension_set.h" #include "extensions/common/manifest_handlers/incognito_info.h" using content::BrowserThread; @@ -44,6 +44,10 @@ InfoMap::InfoMap() : signin_process_id_(-1) {} const ProcessMap& InfoMap::process_map() const { return process_map_; } +const ProcessMap& InfoMap::worker_process_map() const { + return worker_process_map_; +} + void InfoMap::AddExtension(const Extension* extension, base::Time install_time, bool incognito_enabled, @@ -124,6 +128,19 @@ void InfoMap::UnregisterAllExtensionsInProcess(int process_id) { process_map_.RemoveAllFromProcess(process_id); } +void InfoMap::RegisterExtensionWorkerProcess(const std::string& extension_id, + int process_id, + int site_instance_id) { + if (!worker_process_map_.Insert(extension_id, process_id, site_instance_id)) { + NOTREACHED() << "Duplicate extension worker process registration for: " + << extension_id << "," << process_id << "."; + } +} + +void InfoMap::UnregisterExtensionWorkerProcess(int process_id) { + worker_process_map_.RemoveAllFromProcess(process_id); +} + void InfoMap::GetExtensionsWithAPIPermissionForSecurityOrigin( const GURL& origin, int process_id, diff --git a/extensions/browser/info_map.h b/extensions/browser/info_map.h index 8d5cf6dd82..ad475068ee 100644 --- a/extensions/browser/info_map.h +++ b/extensions/browser/info_map.h @@ -11,9 +11,9 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/time/time.h" -#include "chrome/common/extensions/extension_set.h" #include "extensions/browser/process_map.h" #include "extensions/browser/quota_service.h" +#include "extensions/common/extension_set.h" namespace extensions { class Extension; @@ -30,7 +30,10 @@ class InfoMap : public base::RefCountedThreadSafe<InfoMap> { return disabled_extensions_; } + // Information about which extensions are assigned to which render processes. const extensions::ProcessMap& process_map() const; + // Information about which extensions are assigned to which worker processes. + const extensions::ProcessMap& worker_process_map() const; // Callback for when new extensions are loaded. void AddExtension(const extensions::Extension* extension, @@ -64,6 +67,14 @@ class InfoMap : public base::RefCountedThreadSafe<InfoMap> { int site_instance_id); void UnregisterAllExtensionsInProcess(int process_id); + // Adds an entry to worker_process_map_. + void RegisterExtensionWorkerProcess(const std::string& extension_id, + int process_id, + int site_instance_id); + + // Removes an entry from worker_process_map_. + void UnregisterExtensionWorkerProcess(int process_id); + // Returns the subset of extensions which has the same |origin| in // |process_id| with the specified |permission|. void GetExtensionsWithAPIPermissionForSecurityOrigin( @@ -79,6 +90,7 @@ class InfoMap : public base::RefCountedThreadSafe<InfoMap> { extensions::APIPermission::ID permission) const; + // Returns the IO thread QuotaService. Creates the instance on first call. QuotaService* GetQuotaService(); // Keep track of the signin process, so we can restrict extension access to @@ -88,9 +100,8 @@ class InfoMap : public base::RefCountedThreadSafe<InfoMap> { // Notifications can be enabled/disabled in real time by the user. void SetNotificationsDisabled(const std::string& extension_id, - bool notifications_disabled); - bool AreNotificationsDisabled(const std::string& extension_id) - const; + bool notifications_disabled); + bool AreNotificationsDisabled(const std::string& extension_id) const; private: friend class base::RefCountedThreadSafe<InfoMap>; @@ -113,9 +124,12 @@ class InfoMap : public base::RefCountedThreadSafe<InfoMap> { // the IO thread. scoped_ptr<QuotaService> quota_service_; - // Assignment of extensions to processes. + // Assignment of extensions to renderer processes. extensions::ProcessMap process_map_; + // Assignment of extensions to worker processes. + extensions::ProcessMap worker_process_map_; + int signin_process_id_; }; diff --git a/extensions/browser/info_map_unittest.cc b/extensions/browser/info_map_unittest.cc index 8eaf48851e..d01fa61efa 100644 --- a/extensions/browser/info_map_unittest.cc +++ b/extensions/browser/info_map_unittest.cc @@ -38,7 +38,7 @@ static scoped_refptr<Extension> CreateExtension(const std::string& name) { base::FilePath path(FILE_PATH_LITERAL("/foo")); #endif - DictionaryValue manifest; + base::DictionaryValue manifest; manifest.SetString(keys::kVersion, "1.0.0.0"); manifest.SetString(keys::kName, name); @@ -61,7 +61,7 @@ static scoped_refptr<Extension> LoadManifest(const std::string& dir, path = path.AppendASCII("extensions").AppendASCII(dir).AppendASCII(test_file); JSONFileValueSerializer serializer(path); - scoped_ptr<Value> result(serializer.Deserialize(NULL, NULL)); + scoped_ptr<base::Value> result(serializer.Deserialize(NULL, NULL)); if (!result) return NULL; @@ -69,7 +69,7 @@ static scoped_refptr<Extension> LoadManifest(const std::string& dir, scoped_refptr<Extension> extension = Extension::Create(path, Manifest::INVALID_LOCATION, - *static_cast<DictionaryValue*>(result.get()), + *static_cast<base::DictionaryValue*>(result.get()), Extension::NO_FLAGS, &error); EXPECT_TRUE(extension.get()) << error; diff --git a/extensions/browser/lazy_background_task_queue.cc b/extensions/browser/lazy_background_task_queue.cc index f53cdd3e96..2b38ce9ad1 100644 --- a/extensions/browser/lazy_background_task_queue.cc +++ b/extensions/browser/lazy_background_task_queue.cc @@ -7,8 +7,6 @@ #include "base/callback.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/extensions/extension_host.h" -#include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/extensions/extension_system.h" #include "chrome/common/extensions/extension_messages.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/notification_service.h" @@ -16,6 +14,8 @@ #include "content/public/browser/render_view_host.h" #include "content/public/browser/site_instance.h" #include "content/public/browser/web_contents.h" +#include "extensions/browser/extension_registry.h" +#include "extensions/browser/extension_system.h" #include "extensions/browser/extensions_browser_client.h" #include "extensions/browser/process_manager.h" #include "extensions/browser/process_map.h" @@ -44,7 +44,7 @@ bool LazyBackgroundTaskQueue::ShouldEnqueueTask( const Extension* extension) { DCHECK(extension); if (BackgroundInfo::HasBackgroundPage(extension)) { - ProcessManager* pm = ExtensionSystem::GetForBrowserContext( + ProcessManager* pm = ExtensionSystem::Get( browser_context)->process_manager(); DCHECK(pm); ExtensionHost* background_host = @@ -73,15 +73,13 @@ void LazyBackgroundTaskQueue::AddPendingTask( tasks_list = new PendingTasksList(); pending_tasks_[key] = linked_ptr<PendingTasksList>(tasks_list); - ExtensionService* extension_service = ExtensionSystem::GetForBrowserContext( - browser_context)->extension_service(); - DCHECK(extension_service); const Extension* extension = - extension_service->extensions()->GetByID(extension_id); + ExtensionRegistry::Get(browser_context)->enabled_extensions().GetByID( + extension_id); if (extension && BackgroundInfo::HasLazyBackgroundPage(extension)) { // If this is the first enqueued task, and we're not waiting for the // background page to unload, ensure the background page is loaded. - ProcessManager* pm = ExtensionSystem::GetForBrowserContext( + ProcessManager* pm = ExtensionSystem::Get( browser_context)->process_manager(); pm->IncrementLazyKeepaliveCount(extension); // Creating the background host may fail, e.g. if |profile| is incognito @@ -129,7 +127,7 @@ void LazyBackgroundTaskQueue::ProcessPendingTasks( // Balance the keepalive in AddPendingTask. Note we don't do this on a // failure to load, because the keepalive count is reset in that case. if (host && BackgroundInfo::HasLazyBackgroundPage(extension)) { - ExtensionSystem::GetForBrowserContext(browser_context)->process_manager()-> + ExtensionSystem::Get(browser_context)->process_manager()-> DecrementLazyKeepaliveCount(extension); } } diff --git a/extensions/browser/management_policy.cc b/extensions/browser/management_policy.cc index 8bab962fb4..50183d0488 100644 --- a/extensions/browser/management_policy.cc +++ b/extensions/browser/management_policy.cc @@ -25,24 +25,25 @@ ManagementPolicy::~ManagementPolicy() { } bool ManagementPolicy::Provider::UserMayLoad(const Extension* extension, - string16* error) const { + base::string16* error) const { return true; } bool ManagementPolicy::Provider::UserMayModifySettings( - const Extension* extension, string16* error) const { + const Extension* extension, base::string16* error) const { return true; } bool ManagementPolicy::Provider::MustRemainEnabled(const Extension* extension, - string16* error) const { + base::string16* error) + const { return false; } bool ManagementPolicy::Provider::MustRemainDisabled( const Extension* extension, Extension::DisableReason* reason, - string16* error) const { + base::string16* error) const { return false; } @@ -55,26 +56,26 @@ void ManagementPolicy::UnregisterProvider(Provider* provider) { } bool ManagementPolicy::UserMayLoad(const Extension* extension, - string16* error) const { + base::string16* error) const { return ApplyToProviderList(&Provider::UserMayLoad, "Installation", true, extension, error); } bool ManagementPolicy::UserMayModifySettings(const Extension* extension, - string16* error) const { + base::string16* error) const { return ApplyToProviderList(&Provider::UserMayModifySettings, "Modification", true, extension, error); } bool ManagementPolicy::MustRemainEnabled(const Extension* extension, - string16* error) const { + base::string16* error) const { return ApplyToProviderList(&Provider::MustRemainEnabled, "Disabling", false, extension, error); } bool ManagementPolicy::MustRemainDisabled(const Extension* extension, Extension::DisableReason* reason, - string16* error) const { + base::string16* error) const { for (ProviderList::const_iterator it = providers_.begin(); it != providers_.end(); ++it) if ((*it)->MustRemainDisabled(extension, reason, error)) @@ -95,7 +96,7 @@ bool ManagementPolicy::ApplyToProviderList(ProviderFunction function, const char* debug_operation_name, bool normal_result, const Extension* extension, - string16* error) const { + base::string16* error) const { for (ProviderList::const_iterator it = providers_.begin(); it != providers_.end(); ++it) { const Provider* provider = *it; diff --git a/extensions/browser/management_policy.h b/extensions/browser/management_policy.h index ab3c9c3129..149a598fc3 100644 --- a/extensions/browser/management_policy.h +++ b/extensions/browser/management_policy.h @@ -52,13 +52,13 @@ class ManagementPolicy { // Providers should return false if a user may not install the |extension|, // or load or run it if it has already been installed. virtual bool UserMayLoad(const Extension* extension, - string16* error) const; + base::string16* error) const; // Providers should return false if a user may not enable, disable, or // uninstall the |extension|, or change its usage options (incognito // permission, file access, etc.). virtual bool UserMayModifySettings(const Extension* extension, - string16* error) const; + base::string16* error) const; // Providers should return true if the |extension| must always remain // enabled. This is distinct from UserMayModifySettings() in that the latter @@ -66,13 +66,13 @@ class ManagementPolicy { // Providers implementing this method should also implement the others // above, if they wish to completely lock in an extension. virtual bool MustRemainEnabled(const Extension* extension, - string16* error) const; + base::string16* error) const; // Similar to MustRemainEnabled, but for whether an extension must remain // disabled, and returns an error and/or reason if the caller needs it. virtual bool MustRemainDisabled(const Extension* extension, Extension::DisableReason* reason, - string16* error) const; + base::string16* error) const; private: DISALLOW_COPY_AND_ASSIGN(Provider); @@ -89,25 +89,25 @@ class ManagementPolicy { // Returns true if the user is permitted to install, load, and run the given // extension. If not, |error| may be set to an appropriate message. - bool UserMayLoad(const Extension* extension, string16* error) const; + bool UserMayLoad(const Extension* extension, base::string16* error) const; // Returns true if the user is permitted to enable, disable, or uninstall the // given extension, or change the extension's usage options (incognito mode, // file access, etc.). If not, |error| may be set to an appropriate message. bool UserMayModifySettings(const Extension* extension, - string16* error) const; + base::string16* error) const; // Returns true if the extension must remain enabled at all times (e.g. a // compoment extension). In that case, |error| may be set to an appropriate // message. bool MustRemainEnabled(const Extension* extension, - string16* error) const; + base::string16* error) const; // Returns true immediately if any registered provider's MustRemainDisabled // function returns true. bool MustRemainDisabled(const Extension* extension, Extension::DisableReason* reason, - string16* error) const; + base::string16* error) const; // For use in testing. void UnregisterAllProviders(); @@ -116,7 +116,8 @@ class ManagementPolicy { private: // This is a pointer to a function in the Provider interface, used in // ApplyToProviderList. - typedef bool (Provider::*ProviderFunction)(const Extension*, string16*) const; + typedef bool (Provider::*ProviderFunction)(const Extension*, + base::string16*) const; typedef std::set<Provider*> ProviderList; @@ -129,7 +130,7 @@ class ManagementPolicy { const char* debug_operation_name, bool normal_result, const Extension* extension, - string16* error) const; + base::string16* error) const; ProviderList providers_; diff --git a/extensions/browser/management_policy_unittest.cc b/extensions/browser/management_policy_unittest.cc index 595ba29133..52373c135b 100644 --- a/extensions/browser/management_policy_unittest.cc +++ b/extensions/browser/management_policy_unittest.cc @@ -63,7 +63,7 @@ TEST_F(ManagementPolicyTest, RegisterAndUnregister) { TEST_F(ManagementPolicyTest, UserMayLoad) { // No providers registered. - string16 error; + base::string16 error; // The extension and location are irrelevant to the // TestManagementPolicyProviders. EXPECT_TRUE(policy_.UserMayLoad(NULL, &error)); @@ -92,7 +92,7 @@ TEST_F(ManagementPolicyTest, UserMayLoad) { } TEST_F(ManagementPolicyTest, UserMayModifySettings) { // No providers registered. - string16 error; + base::string16 error; EXPECT_TRUE(policy_.UserMayModifySettings(NULL, &error)); EXPECT_TRUE(error.empty()); @@ -120,7 +120,7 @@ TEST_F(ManagementPolicyTest, UserMayModifySettings) { TEST_F(ManagementPolicyTest, MustRemainEnabled) { // No providers registered. - string16 error; + base::string16 error; EXPECT_FALSE(policy_.MustRemainEnabled(NULL, &error)); EXPECT_TRUE(error.empty()); @@ -148,7 +148,7 @@ TEST_F(ManagementPolicyTest, MustRemainEnabled) { TEST_F(ManagementPolicyTest, MustRemainDisabled) { // No providers registered. - string16 error; + base::string16 error; EXPECT_FALSE(policy_.MustRemainDisabled(NULL, NULL, &error)); EXPECT_TRUE(error.empty()); @@ -180,14 +180,14 @@ TEST_F(ManagementPolicyTest, MustRemainDisabled) { TEST_F(ManagementPolicyTest, ErrorHandling) { // The error parameter should be unchanged if no restriction was found. std::string original_error = "Ceci est en effet une erreur."; - string16 original_error16 = UTF8ToUTF16(original_error); - string16 error = original_error16; + base::string16 original_error16 = base::UTF8ToUTF16(original_error); + base::string16 error = original_error16; EXPECT_TRUE(policy_.UserMayLoad(NULL, &error)); - EXPECT_EQ(original_error, UTF16ToUTF8(error)); + EXPECT_EQ(original_error, base::UTF16ToUTF8(error)); EXPECT_TRUE(policy_.UserMayModifySettings(NULL, &error)); - EXPECT_EQ(original_error, UTF16ToUTF8(error)); + EXPECT_EQ(original_error, base::UTF16ToUTF8(error)); EXPECT_FALSE(policy_.MustRemainEnabled(NULL, &error)); - EXPECT_EQ(original_error, UTF16ToUTF8(error)); + EXPECT_EQ(original_error, base::UTF16ToUTF8(error)); // Ensure no crashes if no error message was requested. EXPECT_TRUE(policy_.UserMayLoad(NULL, NULL)); @@ -201,11 +201,11 @@ TEST_F(ManagementPolicyTest, ErrorHandling) { // Make sure returned error is correct. error = original_error16; EXPECT_FALSE(policy_.UserMayLoad(NULL, &error)); - EXPECT_EQ(UTF8ToUTF16(TestProvider::expected_error()), error); + EXPECT_EQ(base::UTF8ToUTF16(TestProvider::expected_error()), error); error = original_error16; EXPECT_FALSE(policy_.UserMayModifySettings(NULL, &error)); - EXPECT_EQ(UTF8ToUTF16(TestProvider::expected_error()), error); + EXPECT_EQ(base::UTF8ToUTF16(TestProvider::expected_error()), error); error = original_error16; EXPECT_TRUE(policy_.MustRemainEnabled(NULL, &error)); - EXPECT_EQ(UTF8ToUTF16(TestProvider::expected_error()), error); + EXPECT_EQ(base::UTF8ToUTF16(TestProvider::expected_error()), error); } diff --git a/extensions/browser/pending_extension_manager.cc b/extensions/browser/pending_extension_manager.cc index f1310dd1cd..d16b92b9df 100644 --- a/extensions/browser/pending_extension_manager.cc +++ b/extensions/browser/pending_extension_manager.cc @@ -65,15 +65,7 @@ bool PendingExtensionManager::Remove(const std::string& id) { } bool PendingExtensionManager::IsIdPending(const std::string& id) const { - PendingExtensionList::const_iterator iter; - for (iter = pending_extension_list_.begin(); - iter != pending_extension_list_.end(); - ++iter) { - if (id == iter->id()) - return true; - } - - return false; + return GetById(id) != NULL; } bool PendingExtensionManager::HasPendingExtensions() const { diff --git a/extensions/browser/pref_names.cc b/extensions/browser/pref_names.cc index 56de3397d9..76a7dc11e0 100644 --- a/extensions/browser/pref_names.cc +++ b/extensions/browser/pref_names.cc @@ -27,6 +27,29 @@ bool ScopeToPrefName(ExtensionPrefsScope scope, std::string* result) { return false; } +const char kAlertsInitialized[] = "extensions.alerts.initialized"; +const char kAllowedInstallSites[] = "extensions.allowed_install_sites"; +const char kAllowedTypes[] = "extensions.allowed_types"; +const char kBookmarkAppCreationLaunchType[] = + "extensions.bookmark_app_creation_launch_type"; +const char kBrowserActionContainerWidth[] = + "extensions.browseractions.container.width"; +const char kExtensions[] = "extensions.settings"; +const char kInstallAllowList[] = "extensions.install.allowlist"; +const char kInstallDenyList[] = "extensions.install.denylist"; +const char kInstallForceList[] = "extensions.install.forcelist"; +const char kKnownDisabled[] = "extensions.known_disabled"; +const char kLastChromeVersion[] = "extensions.last_chrome_version"; +const char kLastUpdateCheck[] = "extensions.autoupdate.last_check"; +const char kNativeMessagingBlacklist[] = "native_messaging.blacklist"; +const char kNativeMessagingWhitelist[] = "native_messaging.whitelist"; +const char kNativeMessagingUserLevelHosts[] = + "native_messaging.user_level_hosts"; +const char kNextUpdateCheck[] = "extensions.autoupdate.next_check"; +const char kStorageGarbageCollect[] = "extensions.storage.garbagecollect"; +const char kToolbar[] = "extensions.toolbar"; +const char kToolbarSize[] = "extensions.toolbarsize"; + const char kPrefPreferences[] = "preferences"; const char kPrefIncognitoPreferences[] = "incognito_preferences"; const char kPrefRegularOnlyPreferences[] = "regular_only_preferences"; diff --git a/extensions/browser/pref_names.h b/extensions/browser/pref_names.h index f70b525b15..96afa21644 100644 --- a/extensions/browser/pref_names.h +++ b/extensions/browser/pref_names.h @@ -9,34 +9,111 @@ #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 extensions { 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. +// appropriate property (i.e. one of kPref*) within a kExtensions dictionary. 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. +// Browser-level preferences --------------------------------------------------- + +// Whether we have run the extension-alert system (see ExtensionGlobalError) +// at least once for this profile. +extern const char kAlertsInitialized[]; + +// The sites that are allowed to install extensions. These sites should be +// allowed to install extensions without the scary dangerous downloads bar. +// Also, when off-store-extension installs are disabled, these sites are exempt. +extern const char kAllowedInstallSites[]; + +// A list of allowed extension types. Extensions can only be installed if their +// type is on this whitelist or alternatively on kInstallAllowList or +// kInstallForceList. +extern const char kAllowedTypes[]; + +// Integer which specifies the launch type that bookmark apps are created with +// by default. +extern const char kBookmarkAppCreationLaunchType[]; + +// Integer boolean representing the width (in pixels) of the container for +// browser actions. +extern const char kBrowserActionContainerWidth[]; + +// Dictionary pref that keeps track of per-extension settings. The keys are +// extension ids. +extern const char kExtensions[]; + +// A whitelist of extension ids the user can install: exceptions from the +// following blacklist. +extern const char kInstallAllowList[]; + +// A blacklist, containing extensions the user cannot install. This list can +// contain "*" meaning all extensions. This list should not be confused with the +// extension blacklist, which is Google controlled. +extern const char kInstallDenyList[]; + +// A list containing extensions that Chrome will silently install +// at startup time. It is a list of strings, each string contains +// an extension ID and an update URL, delimited by a semicolon. +// This preference is set by an admin policy, and meant to be only +// accessed through extensions::ExternalPolicyProvider. +extern const char kInstallForceList[]; + +// A list of known disabled extensions IDs. +extern const char kKnownDisabled[]; + +// String pref for what version chrome was last time the extension prefs were +// loaded. +extern const char kLastChromeVersion[]; + +// Time of the last extensions auto-update check. +extern const char kLastUpdateCheck[]; + +// Blacklist and whitelist for Native Messaging Hosts. +extern const char kNativeMessagingBlacklist[]; +extern const char kNativeMessagingWhitelist[]; + +// Flag allowing usage of Native Messaging hosts installed on user level. +extern const char kNativeMessagingUserLevelHosts[]; + +// Time of the next scheduled extensions auto-update checks. +extern const char kNextUpdateCheck[]; + +// Indicates on-disk data might have skeletal data that needs to be cleaned +// on the next start of the browser. +extern const char kStorageGarbageCollect[]; + +// A preference that tracks browser action toolbar configuration. This is a list +// object stored in the Preferences file. The extensions are stored by ID. +extern const char kToolbar[]; + +// Integer pref that tracks the number of browser actions visible in the browser +// actions toolbar. +extern const char kToolbarSize[]; + +// Properties in kExtensions dictionaries -------------------------------------- + +// Extension-controlled preferences. extern const char kPrefPreferences[]; -// A preference that contains any extension-controlled incognito preferences. +// Extension-controlled incognito preferences. extern const char kPrefIncognitoPreferences[]; -// A preference that contains any extension-controlled regular-only preferences. +// Extension-controlled regular-only preferences. extern const char kPrefRegularOnlyPreferences[]; -// A preference that contains extension-set content settings. +// Extension-set content settings. extern const char kPrefContentSettings[]; -// A preference that contains extension-set content settings. +// Extension-set incognito content settings. extern const char kPrefIncognitoContentSettings[]; } // namespace pref_names - } // namespace extensions #endif // EXTENSIONS_BROWSER_PREF_NAMES_H_ diff --git a/extensions/browser/process_manager.cc b/extensions/browser/process_manager.cc index 6a57d8e600..96a8b2867c 100644 --- a/extensions/browser/process_manager.cc +++ b/extensions/browser/process_manager.cc @@ -17,14 +17,13 @@ #include "chrome/browser/extensions/api/runtime/runtime_api.h" #include "chrome/browser/extensions/extension_host.h" #include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/extensions/extension_system.h" -#include "chrome/browser/extensions/extension_util.h" #include "chrome/common/extensions/extension_messages.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/devtools_manager.h" #include "content/public/browser/notification_service.h" +#include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/site_instance.h" @@ -33,6 +32,8 @@ #include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_contents_user_data.h" #include "content/public/common/renderer_preferences.h" +#include "extensions/browser/extension_registry.h" +#include "extensions/browser/extension_system.h" #include "extensions/browser/extensions_browser_client.h" #include "extensions/browser/view_type_utils.h" #include "extensions/common/extension.h" @@ -64,6 +65,22 @@ std::string GetExtensionID(RenderViewHost* render_view_host) { return render_view_host->GetSiteInstance()->GetSiteURL().host(); } +std::string GetExtensionIDFromFrame( + content::RenderFrameHost* render_frame_host) { + // This works for both apps and extensions because the site has been + // normalized to the extension URL for apps. + if (!render_frame_host->GetSiteInstance()) + return std::string(); + + return render_frame_host->GetSiteInstance()->GetSiteURL().host(); +} + +bool IsFrameInExtensionHost(ExtensionHost* extension_host, + content::RenderFrameHost* render_frame_host) { + return WebContents::FromRenderFrameHost(render_frame_host) == + extension_host->host_contents(); +} + void OnRenderViewHostUnregistered(BrowserContext* context, RenderViewHost* render_view_host) { content::NotificationService::current()->Notify( @@ -85,9 +102,6 @@ class IncognitoProcessManager : public ProcessManager { virtual SiteInstance* GetSiteInstanceForURL(const GURL& url) OVERRIDE; private: - // Returns true if the extension is allowed to run in incognito mode. - bool IsIncognitoEnabled(const Extension* extension); - ProcessManager* original_manager_; DISALLOW_COPY_AND_ASSIGN(IncognitoProcessManager); @@ -113,8 +127,7 @@ class RenderViewHostDestructionObserver explicit RenderViewHostDestructionObserver(WebContents* web_contents) : WebContentsObserver(web_contents) { BrowserContext* context = web_contents->GetBrowserContext(); - process_manager_ = - ExtensionSystem::GetForBrowserContext(context)->process_manager(); + process_manager_ = ExtensionSystem::Get(context)->process_manager(); } friend class content::WebContentsUserData<RenderViewHostDestructionObserver>; @@ -203,6 +216,8 @@ ProcessManager::ProcessManager(BrowserContext* context, content::Source<BrowserContext>(original_context)); } + // Note: event_page_idle_time_ must be sufficiently larger (e.g. 2x) than + // kKeepaliveThrottleIntervalInSeconds in ppapi/proxy/plugin_globals. event_page_idle_time_ = base::TimeDelta::FromSeconds(10); unsigned idle_time_msec = 0; if (base::StringToUint(CommandLine::ForCurrentProcess()->GetSwitchValueASCII( @@ -246,8 +261,11 @@ ExtensionHost* ProcessManager::CreateBackgroundHost(const Extension* extension, const GURL& url) { // Hosted apps are taken care of from BackgroundContentsService. Ignore them // here. - if (extension->is_hosted_app()) + if (extension->is_hosted_app() || + !ExtensionsBrowserClient::Get()-> + IsBackgroundPageAllowed(GetBrowserContext())) { return NULL; + } // Don't create multiple background hosts for an extension. if (ExtensionHost* host = GetBackgroundHostForExtension(extension->id())) @@ -296,12 +314,12 @@ const Extension* ProcessManager::GetExtensionForRenderViewHost( if (!render_view_host->GetSiteInstance()) return NULL; - ExtensionService* service = ExtensionSystem::GetForBrowserContext( - GetBrowserContext())->extension_service(); - if (!service) + ExtensionRegistry* registry = ExtensionRegistry::Get(GetBrowserContext()); + if (!registry) return NULL; - return service->extensions()->GetByID(GetExtensionID(render_view_host)); + return registry->enabled_extensions().GetByID( + GetExtensionID(render_view_host)); } void ProcessManager::UnregisterRenderViewHost( @@ -325,11 +343,11 @@ void ProcessManager::UnregisterRenderViewHost( } } -void ProcessManager::RegisterRenderViewHost(RenderViewHost* render_view_host) { +bool ProcessManager::RegisterRenderViewHost(RenderViewHost* render_view_host) { const Extension* extension = GetExtensionForRenderViewHost( render_view_host); if (!extension) - return; + return false; WebContents* web_contents = WebContents::FromRenderViewHost(render_view_host); all_extension_views_[render_view_host] = GetViewType(web_contents); @@ -338,6 +356,7 @@ void ProcessManager::RegisterRenderViewHost(RenderViewHost* render_view_host) { // extension views are visible. Keepalive count balanced in // UnregisterRenderViewHost. IncrementLazyKeepaliveCountForView(render_view_host); + return true; } SiteInstance* ProcessManager::GetSiteInstanceForURL(const GURL& url) { @@ -419,6 +438,12 @@ void ProcessManager::KeepaliveImpulse(const Extension* extension) { IncrementLazyKeepaliveCount(extension); } } + + if (!keepalive_impulse_callback_for_testing_.is_null()) { + ImpulseCallbackForTesting callback_may_clear_callbacks_reentrantly = + keepalive_impulse_callback_for_testing_; + callback_may_clear_callbacks_reentrantly.Run(extension->id()); + } } // DecrementLazyKeepaliveCount is called when no calls to KeepaliveImpulse @@ -431,8 +456,14 @@ void ProcessManager::OnKeepaliveImpulseCheck() { for (BackgroundPageDataMap::iterator i = background_page_data_.begin(); i != background_page_data_.end(); ++i) { - if (i->second.previous_keepalive_impulse && !i->second.keepalive_impulse) + if (i->second.previous_keepalive_impulse && !i->second.keepalive_impulse) { DecrementLazyKeepaliveCount(i->first); + if (!keepalive_impulse_decrement_callback_for_testing_.is_null()) { + ImpulseCallbackForTesting callback_may_clear_callbacks_reentrantly = + keepalive_impulse_decrement_callback_for_testing_; + callback_may_clear_callbacks_reentrantly.Run(i->first); + } + } i->second.previous_keepalive_impulse = i->second.keepalive_impulse; i->second.keepalive_impulse = false; @@ -505,17 +536,19 @@ void ProcessManager::CloseLazyBackgroundPageNow(const std::string& extension_id, } } -void ProcessManager::OnNetworkRequestStarted(RenderViewHost* render_view_host) { +void ProcessManager::OnNetworkRequestStarted( + content::RenderFrameHost* render_frame_host) { ExtensionHost* host = GetBackgroundHostForExtension( - GetExtensionID(render_view_host)); - if (host && host->render_view_host() == render_view_host) + GetExtensionIDFromFrame(render_frame_host)); + if (host && IsFrameInExtensionHost(host, render_frame_host)) IncrementLazyKeepaliveCount(host->extension()); } -void ProcessManager::OnNetworkRequestDone(RenderViewHost* render_view_host) { +void ProcessManager::OnNetworkRequestDone( + content::RenderFrameHost* render_frame_host) { ExtensionHost* host = GetBackgroundHostForExtension( - GetExtensionID(render_view_host)); - if (host && host->render_view_host() == render_view_host) + GetExtensionIDFromFrame(render_frame_host)); + if (host && IsFrameInExtensionHost(host, render_frame_host)) DecrementLazyKeepaliveCount(host->extension()); } @@ -536,7 +569,7 @@ void ProcessManager::CancelSuspend(const Extension* extension) { } void ProcessManager::OnBrowserWindowReady() { - ExtensionService* service = ExtensionSystem::GetForBrowserContext( + ExtensionService* service = ExtensionSystem::Get( GetBrowserContext())->extension_service(); // On Chrome OS, a login screen is implemented as a browser. // This browser has no extension service. In this case, @@ -551,6 +584,16 @@ content::BrowserContext* ProcessManager::GetBrowserContext() const { return site_instance_->GetBrowserContext(); } +void ProcessManager::SetKeepaliveImpulseCallbackForTesting( + const ImpulseCallbackForTesting& callback) { + keepalive_impulse_callback_for_testing_ = callback; +} + +void ProcessManager::SetKeepaliveImpulseDecrementCallbackForTesting( + const ImpulseCallbackForTesting& callback) { + keepalive_impulse_decrement_callback_for_testing_ = callback; +} + void ProcessManager::Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) { @@ -569,9 +612,9 @@ void ProcessManager::Observe(int type, case chrome::NOTIFICATION_EXTENSION_LOADED: { BrowserContext* context = content::Source<BrowserContext>(source).ptr(); - ExtensionService* service = - ExtensionSystem::GetForBrowserContext(context)->extension_service(); - if (service->is_ready()) { + ExtensionSystem* system = ExtensionSystem::Get(context); + if (system->ready().is_signaled()) { + // The extension system is ready, so create the background host. const Extension* extension = content::Details<const Extension>(details).ptr(); CreateBackgroundHostForExtensionLoad(this, extension); @@ -630,8 +673,9 @@ void ProcessManager::Observe(int type, // The above will unregister a RVH when it gets swapped out with a new // one. However we need to watch the WebContents to know when a RVH is // deleted because the WebContents has gone away. - RenderViewHostDestructionObserver::CreateForWebContents(contents); - RegisterRenderViewHost(switched_details->second); + if (RegisterRenderViewHost(switched_details->second)) { + RenderViewHostDestructionObserver::CreateForWebContents(contents); + } break; } @@ -691,14 +735,17 @@ void ProcessManager::OnDevToolsStateChanged( } void ProcessManager::CreateBackgroundHostsForProfileStartup() { - if (startup_background_hosts_created_) + if (startup_background_hosts_created_ || + !ExtensionsBrowserClient::Get()-> + IsBackgroundPageAllowed(GetBrowserContext())) { return; + } - ExtensionService* service = ExtensionSystem::GetForBrowserContext( - GetBrowserContext())->extension_service(); - DCHECK(service); - for (ExtensionSet::const_iterator extension = service->extensions()->begin(); - extension != service->extensions()->end(); ++extension) { + const ExtensionSet& enabled_extensions = + ExtensionRegistry::Get(GetBrowserContext())->enabled_extensions(); + for (ExtensionSet::const_iterator extension = enabled_extensions.begin(); + extension != enabled_extensions.end(); + ++extension) { CreateBackgroundHostForExtensionLoad(this, extension->get()); RuntimeEventRouter::DispatchOnStartupEvent(GetBrowserContext(), @@ -806,8 +853,8 @@ IncognitoProcessManager::IncognitoProcessManager( BrowserContext* incognito_context, BrowserContext* original_context) : ProcessManager(incognito_context, original_context), - original_manager_(ExtensionSystem::GetForBrowserContext( - original_context)->process_manager()) { + original_manager_( + ExtensionSystem::Get(original_context)->process_manager()) { DCHECK(incognito_context->IsOffTheRecord()); // The original profile will have its own ProcessManager to @@ -823,7 +870,8 @@ IncognitoProcessManager::IncognitoProcessManager( ExtensionHost* IncognitoProcessManager::CreateBackgroundHost( const Extension* extension, const GURL& url) { if (IncognitoInfo::IsSplitMode(extension)) { - if (IsIncognitoEnabled(extension)) + if (ExtensionsBrowserClient::Get()->IsExtensionIncognitoEnabled( + extension->id(), GetBrowserContext())) return ProcessManager::CreateBackgroundHost(extension, url); } else { // Do nothing. If an extension is spanning, then its original-profile @@ -833,11 +881,10 @@ ExtensionHost* IncognitoProcessManager::CreateBackgroundHost( } SiteInstance* IncognitoProcessManager::GetSiteInstanceForURL(const GURL& url) { - ExtensionService* service = ExtensionSystem::GetForBrowserContext( - GetBrowserContext())->extension_service(); - if (service) { + ExtensionRegistry* registry = ExtensionRegistry::Get(GetBrowserContext()); + if (registry) { const Extension* extension = - service->extensions()->GetExtensionOrAppByURL(url); + registry->enabled_extensions().GetExtensionOrAppByURL(url); if (extension && !IncognitoInfo::IsSplitMode(extension)) { return original_manager_->GetSiteInstanceForURL(url); } @@ -845,11 +892,4 @@ SiteInstance* IncognitoProcessManager::GetSiteInstanceForURL(const GURL& url) { return ProcessManager::GetSiteInstanceForURL(url); } -bool IncognitoProcessManager::IsIncognitoEnabled(const Extension* extension) { - // Keep in sync with duplicate in extension_info_map.cc. - ExtensionService* service = ExtensionSystem::GetForBrowserContext( - GetBrowserContext())->extension_service(); - return extension_util::IsIncognitoEnabled(extension->id(), service); -} - } // namespace extensions diff --git a/extensions/browser/process_manager.h b/extensions/browser/process_manager.h index fa5f2b6502..1059db3028 100644 --- a/extensions/browser/process_manager.h +++ b/extensions/browser/process_manager.h @@ -24,6 +24,7 @@ namespace content { class BrowserContext; class DevToolsAgentHost; class RenderViewHost; +class RenderFrameHost; class SiteInstance; }; @@ -104,10 +105,10 @@ class ProcessManager : public content::NotificationObserver { // Same as above, for the Suspend message. void OnSuspendAck(const std::string& extension_id); - // Tracks network requests for a given RenderViewHost, used to know + // Tracks network requests for a given RenderFrameHost, used to know // when network activity is idle for lazy background pages. - void OnNetworkRequestStarted(content::RenderViewHost* render_view_host); - void OnNetworkRequestDone(content::RenderViewHost* render_view_host); + void OnNetworkRequestStarted(content::RenderFrameHost* render_frame_host); + void OnNetworkRequestDone(content::RenderFrameHost* render_frame_host); // Prevents |extension|'s background page from being closed and sends the // onSuspendCanceled() event to it. @@ -120,6 +121,14 @@ class ProcessManager : public content::NotificationObserver { // related SiteInstances. content::BrowserContext* GetBrowserContext() const; + // Sets callbacks for testing keepalive impulse behavior. + typedef base::Callback<void(const std::string& extension_id)> + ImpulseCallbackForTesting; + void SetKeepaliveImpulseCallbackForTesting( + const ImpulseCallbackForTesting& callback); + void SetKeepaliveImpulseDecrementCallbackForTesting( + const ImpulseCallbackForTesting& callback); + protected: // If |context| is incognito pass the master context as |original_context|. // Otherwise pass the same context for both. @@ -181,7 +190,9 @@ class ProcessManager : public content::NotificationObserver { // Potentially registers a RenderViewHost, if it is associated with an // extension. Does nothing if this is not an extension renderer. - void RegisterRenderViewHost(content::RenderViewHost* render_view_host); + // Returns true, if render_view_host was registered (it is associated + // with an extension). + bool RegisterRenderViewHost(content::RenderViewHost* render_view_host); // Unregister RenderViewHosts and clear background page data for an extension // which has been unloaded. @@ -215,6 +226,9 @@ class ProcessManager : public content::NotificationObserver { base::Callback<void(content::DevToolsAgentHost*, bool)> devtools_callback_; + ImpulseCallbackForTesting keepalive_impulse_callback_for_testing_; + ImpulseCallbackForTesting keepalive_impulse_decrement_callback_for_testing_; + base::WeakPtrFactory<ProcessManager> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(ProcessManager); diff --git a/extensions/browser/process_map.cc b/extensions/browser/process_map.cc index 899940e045..3651f8af52 100644 --- a/extensions/browser/process_map.cc +++ b/extensions/browser/process_map.cc @@ -4,6 +4,8 @@ #include "extensions/browser/process_map.h" +#include "extensions/browser/process_map_factory.h" + namespace extensions { // Item @@ -60,6 +62,11 @@ ProcessMap::ProcessMap() { ProcessMap::~ProcessMap() { } +// static +ProcessMap* ProcessMap::Get(content::BrowserContext* browser_context) { + return ProcessMapFactory::GetForBrowserContext(browser_context); +} + bool ProcessMap::Insert(const std::string& extension_id, int process_id, int site_instance_id) { return items_.insert(Item(extension_id, process_id, site_instance_id)).second; @@ -112,4 +119,4 @@ std::set<std::string> ProcessMap::GetExtensionsInProcess(int process_id) const { return result; } -} // extensions +} // namespace extensions diff --git a/extensions/browser/process_map.h b/extensions/browser/process_map.h index 780b21a587..0310651d09 100644 --- a/extensions/browser/process_map.h +++ b/extensions/browser/process_map.h @@ -9,6 +9,11 @@ #include <string> #include "base/basictypes.h" +#include "components/browser_context_keyed_service/browser_context_keyed_service.h" + +namespace content { +class BrowserContext; +} namespace extensions { @@ -61,10 +66,14 @@ namespace extensions { // RenderProcessHost::FromID() and check the profile of the resulting object. // // TODO(aa): The above warnings suggest this class could use improvement :). -class ProcessMap { +class ProcessMap : public BrowserContextKeyedService { public: ProcessMap(); - ~ProcessMap(); + virtual ~ProcessMap(); + + // Returns the instance for |browser_context|. An instance is shared between + // an incognito and a regular context. + static ProcessMap* Get(content::BrowserContext* browser_context); size_t size() const { return items_.size(); } @@ -89,6 +98,6 @@ class ProcessMap { DISALLOW_COPY_AND_ASSIGN(ProcessMap); }; -} // extensions +} // namespace extensions #endif // EXTENSIONS_BROWSER_PROCESS_MAP_H_ diff --git a/extensions/browser/process_map_factory.cc b/extensions/browser/process_map_factory.cc new file mode 100644 index 0000000000..1351f22636 --- /dev/null +++ b/extensions/browser/process_map_factory.cc @@ -0,0 +1,46 @@ +// Copyright 2014 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/process_map_factory.h" + +#include "components/browser_context_keyed_service/browser_context_dependency_manager.h" +#include "extensions/browser/extensions_browser_client.h" +#include "extensions/browser/process_map.h" + +using content::BrowserContext; + +namespace extensions { + +// static +ProcessMap* ProcessMapFactory::GetForBrowserContext(BrowserContext* context) { + return static_cast<ProcessMap*>( + GetInstance()->GetServiceForBrowserContext(context, true)); +} + +// static +ProcessMapFactory* ProcessMapFactory::GetInstance() { + return Singleton<ProcessMapFactory>::get(); +} + +ProcessMapFactory::ProcessMapFactory() + : BrowserContextKeyedServiceFactory( + "ProcessMap", + BrowserContextDependencyManager::GetInstance()) { + // No dependencies on other services. +} + +ProcessMapFactory::~ProcessMapFactory() {} + +BrowserContextKeyedService* ProcessMapFactory::BuildServiceInstanceFor( + BrowserContext* context) const { + return new ProcessMap; +} + +BrowserContext* ProcessMapFactory::GetBrowserContextToUse( + BrowserContext* context) const { + // Redirected in incognito. + return ExtensionsBrowserClient::Get()->GetOriginalContext(context); +} + +} // namespace extensions diff --git a/extensions/browser/process_map_factory.h b/extensions/browser/process_map_factory.h new file mode 100644 index 0000000000..0a62dc75e2 --- /dev/null +++ b/extensions/browser/process_map_factory.h @@ -0,0 +1,42 @@ +// Copyright 2014 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_PROCESS_MAP_FACTORY_H_ +#define EXTENSIONS_BROWSER_PROCESS_MAP_FACTORY_H_ + +#include "base/compiler_specific.h" +#include "base/memory/singleton.h" +#include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h" + +namespace extensions { + +class ProcessMap; + +// Factory for ProcessMap objects. ProcessMap objects are shared between an +// incognito browser context and its master browser context. +class ProcessMapFactory : public BrowserContextKeyedServiceFactory { + public: + static ProcessMap* GetForBrowserContext(content::BrowserContext* context); + + static ProcessMapFactory* GetInstance(); + + private: + friend struct DefaultSingletonTraits<ProcessMapFactory>; + + ProcessMapFactory(); + virtual ~ProcessMapFactory(); + + // BrowserContextKeyedServiceFactory implementation: + virtual BrowserContextKeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const OVERRIDE; + virtual content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(ProcessMapFactory); +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_PROCESS_MAP_FACTORY_H_ diff --git a/extensions/browser/quota_service.h b/extensions/browser/quota_service.h index 8dacb959d1..7479c5535b 100644 --- a/extensions/browser/quota_service.h +++ b/extensions/browser/quota_service.h @@ -37,9 +37,10 @@ typedef std::list<QuotaLimitHeuristic*> QuotaLimitHeuristics; // The QuotaService takes care that calls to certain extension // functions do not exceed predefined quotas. // -// The QuotaService needs to live entirely on one thread, i.e. -// be created, called and destroyed on the same thread, due to its use -// of a RepeatingTimer. +// The QuotaService needs to live entirely on one thread, i.e. be created, +// called and destroyed on the same thread, due to its use of a RepeatingTimer. +// It is not a BrowserContextKeyedService because instances exist on both the UI +// and IO threads. class QuotaService : public base::NonThreadSafe { public: // Some concrete heuristics (declared below) that ExtensionFunctions can diff --git a/extensions/browser/renderer_startup_helper.cc b/extensions/browser/renderer_startup_helper.cc new file mode 100644 index 0000000000..ab278aca9e --- /dev/null +++ b/extensions/browser/renderer_startup_helper.cc @@ -0,0 +1,127 @@ +// Copyright 2014 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/renderer_startup_helper.h" + +#include "base/values.h" +#include "chrome/browser/extensions/extension_function_dispatcher.h" +#include "chrome/common/extensions/extension_messages.h" +#include "chrome/common/extensions/features/feature_channel.h" +#include "components/browser_context_keyed_service/browser_context_dependency_manager.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/notification_types.h" +#include "content/public/browser/render_process_host.h" +#include "extensions/browser/extension_registry.h" +#include "extensions/browser/extensions_browser_client.h" +#include "extensions/common/extension_set.h" +#include "extensions/common/extensions_client.h" +#include "ui/base/webui/web_ui_util.h" + +using content::BrowserContext; + +namespace extensions { + +RendererStartupHelper::RendererStartupHelper(BrowserContext* browser_context) + : browser_context_(browser_context) { + DCHECK(browser_context); + registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CREATED, + content::NotificationService::AllBrowserContextsAndSources()); +} + +RendererStartupHelper::~RendererStartupHelper() {} + +void RendererStartupHelper::Observe( + int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + switch (type) { + case content::NOTIFICATION_RENDERER_PROCESS_CREATED: { + content::RenderProcessHost* process = + content::Source<content::RenderProcessHost>(source).ptr(); + if (!ExtensionsBrowserClient::Get()->IsSameContext( + browser_context_, process->GetBrowserContext())) + break; + + // Extensions need to know the channel for API restrictions. + process->Send(new ExtensionMsg_SetChannel( + extensions::GetCurrentChannel())); + + // Platform apps need to know the system font. + scoped_ptr<base::DictionaryValue> fonts(new base::DictionaryValue); + webui::SetFontAndTextDirection(fonts.get()); + std::string font_family, font_size; + fonts->GetString("fontfamily", &font_family); + fonts->GetString("fontsize", &font_size); + process->Send(new ExtensionMsg_SetSystemFont( + font_family, font_size)); + + // Valid extension function names, used to setup bindings in renderer. + std::vector<std::string> function_names; + ExtensionFunctionDispatcher::GetAllFunctionNames(&function_names); + process->Send(new ExtensionMsg_SetFunctionNames(function_names)); + + // Scripting whitelist. This is modified by tests and must be communicated + // to renderers. + process->Send(new ExtensionMsg_SetScriptingWhitelist( + extensions::ExtensionsClient::Get()->GetScriptingWhitelist())); + + // Loaded extensions. + std::vector<ExtensionMsg_Loaded_Params> loaded_extensions; + const ExtensionSet& extensions = + ExtensionRegistry::Get(browser_context_)->enabled_extensions(); + for (ExtensionSet::const_iterator iter = extensions.begin(); + iter != extensions.end(); ++iter) { + // Renderers don't need to know about themes. + if (!(*iter)->is_theme()) + loaded_extensions.push_back(ExtensionMsg_Loaded_Params(iter->get())); + } + process->Send(new ExtensionMsg_Loaded(loaded_extensions)); + break; + } + default: + NOTREACHED(); + break; + } +} + +////////////////////////////////////////////////////////////////////////////// + +// static +RendererStartupHelper* RendererStartupHelperFactory::GetForBrowserContext( + BrowserContext* context) { + return static_cast<RendererStartupHelper*>( + GetInstance()->GetServiceForBrowserContext(context, true)); +} + +// static +RendererStartupHelperFactory* RendererStartupHelperFactory::GetInstance() { + return Singleton<RendererStartupHelperFactory>::get(); +} + +RendererStartupHelperFactory::RendererStartupHelperFactory() + : BrowserContextKeyedServiceFactory( + "RendererStartupHelper", + BrowserContextDependencyManager::GetInstance()) { + // No dependencies on other services. +} + +RendererStartupHelperFactory::~RendererStartupHelperFactory() {} + +BrowserContextKeyedService* +RendererStartupHelperFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + return new RendererStartupHelper(context); +} + +BrowserContext* RendererStartupHelperFactory::GetBrowserContextToUse( + BrowserContext* context) const { + // Redirected in incognito. + return ExtensionsBrowserClient::Get()->GetOriginalContext(context); +} + +bool RendererStartupHelperFactory::ServiceIsCreatedWithBrowserContext() const { + return true; +} + +} // namespace extensions diff --git a/extensions/browser/renderer_startup_helper.h b/extensions/browser/renderer_startup_helper.h new file mode 100644 index 0000000000..7fcc70f66a --- /dev/null +++ b/extensions/browser/renderer_startup_helper.h @@ -0,0 +1,74 @@ +// Copyright 2014 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_RENDER_PROCESS_HELPER_H_ +#define EXTENSIONS_BROWSER_RENDER_PROCESS_HELPER_H_ + +#include "base/compiler_specific.h" +#include "base/memory/singleton.h" +#include "components/browser_context_keyed_service/browser_context_keyed_service.h" +#include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" + +namespace content { +class BrowserContext; +class RenderProcessHost; +} + +namespace extensions { + +// Informs renderers about extensions-related data (channel, available +// functions, etc.) when they start. Sends this information to both extension +// and non-extension renderers, as the non-extension renderers may have content +// scripts. Lives on the UI thread. Shared between incognito and non-incognito +// browser contexts. +class RendererStartupHelper : public BrowserContextKeyedService, + public content::NotificationObserver { + public: + // This class sends messages to all renderers started for |browser_context|. + explicit RendererStartupHelper(content::BrowserContext* browser_context); + virtual ~RendererStartupHelper(); + + // content::NotificationObserver overrides: + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE; + + private: + content::BrowserContext* browser_context_; // Not owned. + + content::NotificationRegistrar registrar_; + + DISALLOW_COPY_AND_ASSIGN(RendererStartupHelper); +}; + +// Factory for RendererStartupHelpers. Declared here because this header is +// rarely included and it's probably cheaper to put it here than to make the +// compiler generate another object file. +class RendererStartupHelperFactory : public BrowserContextKeyedServiceFactory { + public: + static RendererStartupHelper* GetForBrowserContext( + content::BrowserContext* context); + static RendererStartupHelperFactory* GetInstance(); + + private: + friend struct DefaultSingletonTraits<RendererStartupHelperFactory>; + + RendererStartupHelperFactory(); + virtual ~RendererStartupHelperFactory(); + + // BrowserContextKeyedServiceFactory implementation: + virtual BrowserContextKeyedService* BuildServiceInstanceFor( + content::BrowserContext* profile) const OVERRIDE; + virtual content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const OVERRIDE; + virtual bool ServiceIsCreatedWithBrowserContext() const OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(RendererStartupHelperFactory); +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_RENDER_PROCESS_HELPER_H_ diff --git a/extensions/browser/runtime_data.cc b/extensions/browser/runtime_data.cc new file mode 100644 index 0000000000..a3b1c6c4e1 --- /dev/null +++ b/extensions/browser/runtime_data.cc @@ -0,0 +1,76 @@ +// Copyright 2014 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/runtime_data.h" + +#include "extensions/browser/extension_registry.h" +#include "extensions/common/extension.h" +#include "extensions/common/manifest_handlers/background_info.h" + +namespace extensions { + +RuntimeData::RuntimeData(ExtensionRegistry* registry) : registry_(registry) { + registry_->AddObserver(this); +} + +RuntimeData::~RuntimeData() { + registry_->RemoveObserver(this); +} + +bool RuntimeData::IsBackgroundPageReady(const Extension* extension) const { + if (!BackgroundInfo::HasPersistentBackgroundPage(extension)) + return true; + return HasFlag(extension, BACKGROUND_PAGE_READY); +} + +void RuntimeData::SetBackgroundPageReady(const Extension* extension, + bool value) { + SetFlag(extension, BACKGROUND_PAGE_READY, value); +} + +bool RuntimeData::IsBeingUpgraded(const Extension* extension) const { + return HasFlag(extension, BEING_UPGRADED); +} + +void RuntimeData::SetBeingUpgraded(const Extension* extension, bool value) { + SetFlag(extension, BEING_UPGRADED, value); +} + +bool RuntimeData::HasUsedWebRequest(const Extension* extension) const { + return HasFlag(extension, HAS_USED_WEBREQUEST); +} + +void RuntimeData::SetHasUsedWebRequest(const Extension* extension, bool value) { + SetFlag(extension, HAS_USED_WEBREQUEST, value); +} + +bool RuntimeData::HasExtensionForTesting(const Extension* extension) const { + return extension_flags_.find(extension->id()) != extension_flags_.end(); +} + +void RuntimeData::ClearAll() { + extension_flags_.clear(); +} + +void RuntimeData::OnExtensionUnloaded(const Extension* extension) { + extension_flags_.erase(extension->id()); +} + +bool RuntimeData::HasFlag(const Extension* extension, RuntimeFlag flag) const { + ExtensionFlagsMap::const_iterator it = extension_flags_.find(extension->id()); + if (it == extension_flags_.end()) + return false; + return !!(it->second & flag); +} + +void RuntimeData::SetFlag(const Extension* extension, + RuntimeFlag flag, + bool value) { + if (value) + extension_flags_[extension->id()] |= flag; + else + extension_flags_[extension->id()] &= ~flag; +} + +} // namespace extensions diff --git a/extensions/browser/runtime_data.h b/extensions/browser/runtime_data.h new file mode 100644 index 0000000000..1b4980016a --- /dev/null +++ b/extensions/browser/runtime_data.h @@ -0,0 +1,84 @@ +// Copyright 2014 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_RUNTIME_DATA_H_ +#define EXTENSIONS_BROWSER_RUNTIME_DATA_H_ + +#include <map> +#include <string> + +#include "base/compiler_specific.h" +#include "extensions/browser/extension_registry_observer.h" + +namespace extensions { + +class Extension; +class ExtensionRegistry; + +// Contains per-extension data that can change during the life of the process, +// but does not persist across restarts. Shared between incognito and regular +// browser contexts. Lives on the UI thread. Must be destroyed before +// ExtensionRegistry. +class RuntimeData : public ExtensionRegistryObserver { + public: + // Observes |registry| to clean itself up when extensions change state. + // |registry| must not be NULL. + explicit RuntimeData(ExtensionRegistry* registry); + virtual ~RuntimeData(); + + // Whether the persistent background page, if any, is ready. We don't load + // other components until then. If there is no background page, or if it is + // non-persistent (lazy), we consider it to be ready. + bool IsBackgroundPageReady(const Extension* extension) const; + void SetBackgroundPageReady(const Extension* extension, bool value); + + // Getter and setter for the flag that specifies whether the extension is + // being upgraded. + bool IsBeingUpgraded(const Extension* extension) const; + void SetBeingUpgraded(const Extension* extension, bool value); + + // Getter and setter for the flag that specifies if the extension has used + // the webrequest API. + // TODO(mpcomplete): remove. http://crbug.com/100411 + bool HasUsedWebRequest(const Extension* extension) const; + void SetHasUsedWebRequest(const Extension* extension, bool value); + + // Returns true if the extension is being tracked. Used only for testing. + bool HasExtensionForTesting(const Extension* extension) const; + + // Erase runtime data for all extensions. Used only for testing. Cannot be + // named ClearAllForTesting due to false-positive presubmit errors. + void ClearAll(); + + // ExtensionRegistryObserver overrides. Public for testing. + virtual void OnExtensionUnloaded(const Extension* extension) OVERRIDE; + + private: + // Bitmasks for runtime states. + enum RuntimeFlag { + // Set if the background page is ready. + BACKGROUND_PAGE_READY = 1 << 0, + // Set while the extension is being upgraded. + BEING_UPGRADED = 1 << 1, + // Set if the extension has used the webRequest API. + HAS_USED_WEBREQUEST = 1 << 2, + }; + + // Returns the setting for the flag or false if the extension isn't found. + bool HasFlag(const Extension* extension, RuntimeFlag flag) const; + + // Sets |flag| for |extension| to |value|. Adds |extension| to the list of + // extensions if it isn't present. + void SetFlag(const Extension* extension, RuntimeFlag flag, bool value); + + // Map from extension ID to the RuntimeFlags bits. + typedef std::map<std::string, int> ExtensionFlagsMap; + ExtensionFlagsMap extension_flags_; + + ExtensionRegistry* registry_; // Not owned. +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_RUNTIME_DATA_H_ diff --git a/extensions/browser/runtime_data_unittest.cc b/extensions/browser/runtime_data_unittest.cc new file mode 100644 index 0000000000..207f8aa9e0 --- /dev/null +++ b/extensions/browser/runtime_data_unittest.cc @@ -0,0 +1,108 @@ +// Copyright 2014 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/runtime_data.h" + +#include <string> + +#include "base/memory/ref_counted.h" +#include "extensions/browser/extension_registry.h" +#include "extensions/common/extension.h" +#include "extensions/common/extension_builder.h" +#include "extensions/common/value_builder.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace extensions { +namespace { + +// Creates a very simple extension. +scoped_refptr<Extension> CreateExtension() { + return ExtensionBuilder() + .SetManifest( + DictionaryBuilder().Set("name", "test").Set("version", "0.1")) + .SetID("id1") + .Build(); +} + +// Creates a very simple extension with a background page. +scoped_refptr<Extension> CreateExtensionWithBackgroundPage() { + return ExtensionBuilder() + .SetManifest( + DictionaryBuilder() + .Set("name", "test") + .Set("version", "0.1") + .Set("background", DictionaryBuilder().Set("page", "bg.html"))) + .SetID("id2") + .Build(); +} + +class RuntimeDataTest : public testing::Test { + public: + RuntimeDataTest() : runtime_data_(®istry_) {} + virtual ~RuntimeDataTest() {} + + protected: + ExtensionRegistry registry_; + RuntimeData runtime_data_; + + private: + DISALLOW_COPY_AND_ASSIGN(RuntimeDataTest); +}; + +TEST_F(RuntimeDataTest, IsBackgroundPageReady) { + // An extension without a background page is always considered ready. + scoped_refptr<Extension> no_background = CreateExtension(); + EXPECT_TRUE(runtime_data_.IsBackgroundPageReady(no_background)); + + // An extension with a background page is not ready until the flag is set. + scoped_refptr<Extension> with_background = + CreateExtensionWithBackgroundPage(); + EXPECT_FALSE(runtime_data_.IsBackgroundPageReady(with_background)); + + // The flag can be toggled. + runtime_data_.SetBackgroundPageReady(with_background, true); + EXPECT_TRUE(runtime_data_.IsBackgroundPageReady(with_background)); + runtime_data_.SetBackgroundPageReady(with_background, false); + EXPECT_FALSE(runtime_data_.IsBackgroundPageReady(with_background)); +} + +TEST_F(RuntimeDataTest, IsBeingUpgraded) { + scoped_refptr<Extension> extension = CreateExtension(); + + // An extension is not being upgraded until the flag is set. + EXPECT_FALSE(runtime_data_.IsBeingUpgraded(extension)); + + // The flag can be toggled. + runtime_data_.SetBeingUpgraded(extension, true); + EXPECT_TRUE(runtime_data_.IsBeingUpgraded(extension)); + runtime_data_.SetBeingUpgraded(extension, false); + EXPECT_FALSE(runtime_data_.IsBeingUpgraded(extension)); +} + +// TODO(mpcomplete): Remove. http://crbug.com/100411 +TEST_F(RuntimeDataTest, HasUsedWebRequest) { + scoped_refptr<Extension> extension = CreateExtension(); + + // An extension has not used web request until the flag is set. + EXPECT_FALSE(runtime_data_.HasUsedWebRequest(extension)); + + // The flag can be toggled. + runtime_data_.SetHasUsedWebRequest(extension, true); + EXPECT_TRUE(runtime_data_.HasUsedWebRequest(extension)); + runtime_data_.SetHasUsedWebRequest(extension, false); + EXPECT_FALSE(runtime_data_.HasUsedWebRequest(extension)); +} + +// Unloading an extension stops tracking it. +TEST_F(RuntimeDataTest, OnExtensionUnloaded) { + scoped_refptr<Extension> extension = CreateExtensionWithBackgroundPage(); + runtime_data_.SetBackgroundPageReady(extension, true); + ASSERT_TRUE(runtime_data_.HasExtensionForTesting(extension)); + + runtime_data_.OnExtensionUnloaded(extension); + EXPECT_FALSE(runtime_data_.HasExtensionForTesting(extension)); +} + +} // namespace +} // namespace extensions diff --git a/extensions/browser/test_management_policy.cc b/extensions/browser/test_management_policy.cc index a2c5404dd4..77ec19132f 100644 --- a/extensions/browser/test_management_policy.cc +++ b/extensions/browser/test_management_policy.cc @@ -14,13 +14,13 @@ TestManagementPolicyProvider::TestManagementPolicyProvider() must_remain_enabled_(false), must_remain_disabled_(false), disable_reason_(Extension::DISABLE_NONE) { - error_message_ = UTF8ToUTF16(expected_error()); + error_message_ = base::UTF8ToUTF16(expected_error()); } TestManagementPolicyProvider::TestManagementPolicyProvider( int prohibited_actions) { SetProhibitedActions(prohibited_actions); - error_message_ = UTF8ToUTF16(expected_error()); + error_message_ = base::UTF8ToUTF16(expected_error()); } void TestManagementPolicyProvider::SetProhibitedActions( @@ -41,21 +41,22 @@ std::string TestManagementPolicyProvider::GetDebugPolicyProviderName() const { } bool TestManagementPolicyProvider::UserMayLoad(const Extension* extension, - string16* error) const { + base::string16* error) const { if (error && !may_load_) *error = error_message_; return may_load_; } bool TestManagementPolicyProvider::UserMayModifySettings( - const Extension* extension, string16* error) const { + const Extension* extension, base::string16* error) const { if (error && !may_modify_status_) *error = error_message_; return may_modify_status_; } bool TestManagementPolicyProvider::MustRemainEnabled(const Extension* extension, - string16* error) const { + base::string16* error) + const { if (error && must_remain_enabled_) *error = error_message_; return must_remain_enabled_; @@ -64,7 +65,7 @@ bool TestManagementPolicyProvider::MustRemainEnabled(const Extension* extension, bool TestManagementPolicyProvider::MustRemainDisabled( const Extension* extension, Extension::DisableReason* reason, - string16* error) const { + base::string16* error) const { if (must_remain_disabled_) { if (error) *error = error_message_; diff --git a/extensions/browser/test_management_policy.h b/extensions/browser/test_management_policy.h index f59e015890..a96cde9df9 100644 --- a/extensions/browser/test_management_policy.h +++ b/extensions/browser/test_management_policy.h @@ -8,7 +8,6 @@ #include <string> #include "base/strings/string16.h" -#include "chrome/browser/extensions/extension_service.h" #include "extensions/browser/management_policy.h" namespace extensions { @@ -38,17 +37,17 @@ class TestManagementPolicyProvider : public ManagementPolicy::Provider { virtual std::string GetDebugPolicyProviderName() const OVERRIDE; virtual bool UserMayLoad(const Extension* extension, - string16* error) const OVERRIDE; + base::string16* error) const OVERRIDE; virtual bool UserMayModifySettings(const Extension* extension, - string16* error) const OVERRIDE; + base::string16* error) const OVERRIDE; virtual bool MustRemainEnabled(const Extension* extension, - string16* error) const OVERRIDE; + base::string16* error) const OVERRIDE; virtual bool MustRemainDisabled(const Extension* extension, Extension::DisableReason* reason, - string16* error) const OVERRIDE; + base::string16* error) const OVERRIDE; private: bool may_load_; @@ -57,7 +56,7 @@ class TestManagementPolicyProvider : public ManagementPolicy::Provider { bool must_remain_disabled_; Extension::DisableReason disable_reason_; - string16 error_message_; + base::string16 error_message_; }; } // namespace extensions diff --git a/extensions/common/common_manifest_handlers.cc b/extensions/common/common_manifest_handlers.cc new file mode 100644 index 0000000000..2dc14f2598 --- /dev/null +++ b/extensions/common/common_manifest_handlers.cc @@ -0,0 +1,34 @@ +// Copyright 2014 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/common_manifest_handlers.h" + +#include "extensions/common/manifest_handler.h" +#include "extensions/common/manifest_handlers/background_info.h" +#include "extensions/common/manifest_handlers/csp_info.h" +#include "extensions/common/manifest_handlers/incognito_info.h" +#include "extensions/common/manifest_handlers/kiosk_mode_info.h" +#include "extensions/common/manifest_handlers/offline_enabled_info.h" +#include "extensions/common/manifest_handlers/sandboxed_page_info.h" +#include "extensions/common/manifest_handlers/shared_module_info.h" +#include "extensions/common/manifest_handlers/web_accessible_resources_info.h" +#include "extensions/common/manifest_handlers/webview_info.h" + +namespace extensions { + +void RegisterCommonManifestHandlers() { + DCHECK(!ManifestHandler::IsRegistrationFinalized()); + (new BackgroundManifestHandler)->Register(); + (new CSPHandler(false))->Register(); + (new CSPHandler(true))->Register(); + (new IncognitoHandler)->Register(); + (new KioskModeHandler)->Register(); + (new OfflineEnabledHandler)->Register(); + (new SandboxedPageHandler)->Register(); + (new SharedModuleHandler)->Register(); + (new WebAccessibleResourcesHandler)->Register(); + (new WebviewHandler)->Register(); +} + +} // namespace extensions diff --git a/extensions/common/common_manifest_handlers.h b/extensions/common/common_manifest_handlers.h new file mode 100644 index 0000000000..4f1d7cc8f6 --- /dev/null +++ b/extensions/common/common_manifest_handlers.h @@ -0,0 +1,17 @@ +// Copyright 2014 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_COMMON_MANIFEST_HANDLERS_H_ +#define EXTENSIONS_COMMON_COMMON_MANIFEST_HANDLERS_H_ + +namespace extensions { + +// Registers manifest handlers used by all embedders of the extensions system. +// Should be called once in each process. Embedders may also wish to register +// their own set of manifest handlers, such as chrome_manifest_handlers.cc. +void RegisterCommonManifestHandlers(); + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_COMMON_MANIFEST_HANDLERS_H_ diff --git a/extensions/common/constants.cc b/extensions/common/constants.cc index a332d7003b..dced3101be 100644 --- a/extensions/common/constants.cc +++ b/extensions/common/constants.cc @@ -9,6 +9,7 @@ namespace extensions { const char kExtensionScheme[] = "chrome-extension"; +const char kExtensionResourceScheme[] = "chrome-extension-resource"; const base::FilePath::CharType kManifestFilename[] = FILE_PATH_LITERAL("manifest.json"); diff --git a/extensions/common/constants.h b/extensions/common/constants.h index 3113a17e9f..f55e5177b0 100644 --- a/extensions/common/constants.h +++ b/extensions/common/constants.h @@ -12,6 +12,9 @@ namespace extensions { // Scheme we serve extension content from. extern const char kExtensionScheme[]; +// Canonical schemes you can use as input to GURL.SchemeIs(). +extern const char kExtensionResourceScheme[]; + // The name of the manifest inside an extension. extern const base::FilePath::CharType kManifestFilename[]; diff --git a/extensions/common/error_utils.cc b/extensions/common/error_utils.cc index bd4d32e0d2..2343c04bf7 100644 --- a/extensions/common/error_utils.cc +++ b/extensions/common/error_utils.cc @@ -36,31 +36,22 @@ std::string ErrorUtils::FormatErrorMessage(const std::string& format, 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); +base::string16 ErrorUtils::FormatErrorMessageUTF16(const std::string& format, + const std::string& s1) { + return base::UTF8ToUTF16(FormatErrorMessage(format, s1)); } -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); +base::string16 ErrorUtils::FormatErrorMessageUTF16(const std::string& format, + const std::string& s1, + const std::string& s2) { + return base::UTF8ToUTF16(FormatErrorMessage(format, s1, s2)); } -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); +base::string16 ErrorUtils::FormatErrorMessageUTF16(const std::string& format, + const std::string& s1, + const std::string& s2, + const std::string& s3) { + return base::UTF8ToUTF16(FormatErrorMessage(format, s1, s2, s3)); } -} // namespace +} // namespace extensions diff --git a/extensions/common/error_utils.h b/extensions/common/error_utils.h index c4d012ae2c..fedbd2887b 100644 --- a/extensions/common/error_utils.h +++ b/extensions/common/error_utils.h @@ -26,17 +26,17 @@ class ErrorUtils { const std::string& s2, const std::string& s3); - static string16 FormatErrorMessageUTF16(const std::string& format, - const std::string& s1); + static base::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 base::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); + static base::string16 FormatErrorMessageUTF16(const std::string& format, + const std::string& s1, + const std::string& s2, + const std::string& s3); }; } // namespace extensions diff --git a/extensions/common/event_filter.cc b/extensions/common/event_filter.cc index 70915b976d..ad954c9510 100644 --- a/extensions/common/event_filter.cc +++ b/extensions/common/event_filter.cc @@ -4,9 +4,13 @@ #include "extensions/common/event_filter.h" -#include "extensions/common/matcher/url_matcher_factory.h" +#include "components/url_matcher/url_matcher_factory.h" #include "ipc/ipc_message.h" +using url_matcher::URLMatcher; +using url_matcher::URLMatcherConditionSet; +using url_matcher::URLMatcherFactory; + namespace extensions { EventFilter::EventMatcherEntry::EventMatcherEntry( diff --git a/extensions/common/event_filter.h b/extensions/common/event_filter.h index 6ef136cb9d..ddd0681fc8 100644 --- a/extensions/common/event_filter.h +++ b/extensions/common/event_filter.h @@ -9,9 +9,9 @@ #include <set> #include "base/memory/linked_ptr.h" +#include "components/url_matcher/url_matcher.h" #include "extensions/common/event_filtering_info.h" #include "extensions/common/event_matcher.h" -#include "extensions/common/matcher/url_matcher.h" namespace extensions { @@ -61,9 +61,10 @@ class EventFilter { // 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( + scoped_ptr<EventMatcher> event_matcher, + url_matcher::URLMatcher* url_matcher, + const url_matcher::URLMatcherConditionSet::Vector& condition_sets); ~EventMatcherEntry(); // Prevents the removal of condition sets when this class is destroyed. We @@ -79,8 +80,8 @@ class EventFilter { 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_; + std::vector<url_matcher::URLMatcherConditionSet::ID> condition_set_ids_; + url_matcher::URLMatcher* url_matcher_; DISALLOW_COPY_AND_ASSIGN(EventMatcherEntry); }; @@ -93,27 +94,28 @@ class EventFilter { // 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 CreateConditionSets( + MatcherID id, + EventMatcher* matcher, + url_matcher::URLMatcherConditionSet::Vector* condition_sets); bool AddDictionaryAsConditionSet( base::DictionaryValue* url_filter, - URLMatcherConditionSet::Vector* condition_sets); + url_matcher::URLMatcherConditionSet::Vector* condition_sets); - URLMatcher url_matcher_; + url_matcher::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_; + url_matcher::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> + std::map<url_matcher::URLMatcherConditionSet::ID, MatcherID> condition_set_id_to_event_matcher_id_; // Maps from event matcher ids to the name of the event they match on. diff --git a/extensions/common/event_filter_unittest.cc b/extensions/common/event_filter_unittest.cc index 3f7f12b050..f95c023b67 100644 --- a/extensions/common/event_filter_unittest.cc +++ b/extensions/common/event_filter_unittest.cc @@ -10,6 +10,10 @@ #include "ipc/ipc_message.h" #include "testing/gtest/include/gtest/gtest.h" +using base::DictionaryValue; +using base::ListValue; +using base::Value; + namespace extensions { class EventFilterUnittest : public testing::Test { diff --git a/extensions/common/event_filtering_info.cc b/extensions/common/event_filtering_info.cc index cc46f96916..877f2c5cab 100644 --- a/extensions/common/event_filtering_info.cc +++ b/extensions/common/event_filtering_info.cc @@ -46,7 +46,7 @@ scoped_ptr<base::Value> EventFilteringInfo::AsValue() const { } bool EventFilteringInfo::IsEmpty() const { - return !has_url_ && service_type_.empty(); + return !has_url_ && service_type_.empty() && !has_instance_id_; } } // namespace extensions diff --git a/extensions/common/extension.cc b/extensions/common/extension.cc index 0a67983d3a..df6ea04a61 100644 --- a/extensions/common/extension.cc +++ b/extensions/common/extension.cc @@ -34,7 +34,6 @@ #include "extensions/common/switches.h" #include "extensions/common/url_pattern_set.h" #include "grit/chromium_strings.h" -#include "grit/theme_resources.h" #include "net/base/net_util.h" #include "url/url_util.h" @@ -51,7 +50,7 @@ namespace errors = manifest_errors; namespace { const int kModernManifestVersion = 2; -const int kPEMOutputColumns = 65; +const int kPEMOutputColumns = 64; // KEY MARKERS const char kKeyBeginHeaderMarker[] = "-----BEGIN"; @@ -103,7 +102,7 @@ scoped_refptr<Extension> Extension::Create(const base::FilePath& path, } // TODO(sungguk): Continue removing std::string errors and replacing -// with string16. See http://crbug.com/71980. +// with base::string16. See http://crbug.com/71980. scoped_refptr<Extension> Extension::Create(const base::FilePath& path, Manifest::Location location, const base::DictionaryValue& value, @@ -111,13 +110,13 @@ scoped_refptr<Extension> Extension::Create(const base::FilePath& path, const std::string& explicit_id, std::string* utf8_error) { DCHECK(utf8_error); - string16 error; + base::string16 error; scoped_ptr<extensions::Manifest> manifest( new extensions::Manifest( location, scoped_ptr<base::DictionaryValue>(value.DeepCopy()))); if (!InitExtensionID(manifest.get(), path, explicit_id, flags, &error)) { - *utf8_error = UTF16ToUTF8(error); + *utf8_error = base::UTF16ToUTF8(error); return NULL; } @@ -130,7 +129,7 @@ scoped_refptr<Extension> Extension::Create(const base::FilePath& path, extension->install_warnings_.swap(install_warnings); if (!extension->InitFromValue(flags, &error)) { - *utf8_error = UTF16ToUTF8(error); + *utf8_error = base::UTF16ToUTF8(error); return NULL; } @@ -307,7 +306,8 @@ bool Extension::ShowConfigureContextMenus() const { // options for component extension button but now there is no component // extension with options. All other menu items like uninstall have // no sense for component extensions. - return location() != Manifest::COMPONENT; + return location() != Manifest::COMPONENT && + location() != Manifest::EXTERNAL_COMPONENT; } bool Extension::OverlapsWithOrigin(const GURL& origin) const { @@ -371,8 +371,7 @@ bool Extension::ShouldDisplayInExtensionSettings() const { bool Extension::ShouldNotBeVisible() const { // Don't show component extensions because they are only extensions as an // implementation detail of Chrome. - if ((location() == Manifest::COMPONENT || - location() == Manifest::EXTERNAL_COMPONENT) && + if (extensions::Manifest::IsComponentLocation(location()) && !CommandLine::ForCurrentProcess()->HasSwitch( switches::kShowComponentExtensionOptions)) { return true; @@ -456,6 +455,10 @@ bool Extension::force_incognito_enabled() const { } void Extension::AddWebExtentPattern(const URLPattern& pattern) { + // Bookmark apps are permissionless. + if (from_bookmark()) + return; + extent_.AddPattern(pattern); } @@ -468,7 +471,7 @@ bool Extension::InitExtensionID(extensions::Manifest* manifest, const base::FilePath& path, const std::string& explicit_id, int creation_flags, - string16* error) { + base::string16* error) { if (!explicit_id.empty()) { manifest->set_extension_id(explicit_id); return true; @@ -479,7 +482,7 @@ bool Extension::InitExtensionID(extensions::Manifest* manifest, std::string public_key_bytes; if (!manifest->GetString(keys::kPublicKey, &public_key) || !ParsePEMKeyBytes(public_key, &public_key_bytes)) { - *error = ASCIIToUTF16(errors::kInvalidKey); + *error = base::ASCIIToUTF16(errors::kInvalidKey); return false; } std::string extension_id = id_util::GenerateId(public_key_bytes); @@ -488,7 +491,7 @@ bool Extension::InitExtensionID(extensions::Manifest* manifest, } if (creation_flags & REQUIRE_KEY) { - *error = ASCIIToUTF16(errors::kInvalidKey); + *error = base::ASCIIToUTF16(errors::kInvalidKey); return false; } else { // If there is a path, we generate the ID from it. This is useful for @@ -521,7 +524,7 @@ Extension::Extension(const base::FilePath& path, Extension::~Extension() { } -bool Extension::InitFromValue(int flags, string16* error) { +bool Extension::InitFromValue(int flags, base::string16* error) { DCHECK(error); creation_flags_ = flags; @@ -565,53 +568,53 @@ bool Extension::InitFromValue(int flags, string16* error) { return true; } -bool Extension::LoadRequiredFeatures(string16* error) { +bool Extension::LoadRequiredFeatures(base::string16* error) { if (!LoadName(error) || !LoadVersion(error)) return false; return true; } -bool Extension::LoadName(string16* error) { - string16 localized_name; +bool Extension::LoadName(base::string16* error) { + base::string16 localized_name; if (!manifest_->GetString(keys::kName, &localized_name)) { - *error = ASCIIToUTF16(errors::kInvalidName); + *error = base::ASCIIToUTF16(errors::kInvalidName); return false; } - non_localized_name_ = UTF16ToUTF8(localized_name); + non_localized_name_ = base::UTF16ToUTF8(localized_name); base::i18n::AdjustStringForLocaleDirection(&localized_name); - name_ = UTF16ToUTF8(localized_name); + name_ = base::UTF16ToUTF8(localized_name); return true; } -bool Extension::LoadVersion(string16* error) { +bool Extension::LoadVersion(base::string16* error) { std::string version_str; if (!manifest_->GetString(keys::kVersion, &version_str)) { - *error = ASCIIToUTF16(errors::kInvalidVersion); + *error = base::ASCIIToUTF16(errors::kInvalidVersion); return false; } version_.reset(new Version(version_str)); if (!version_->IsValid() || version_->components().size() > 4) { - *error = ASCIIToUTF16(errors::kInvalidVersion); + *error = base::ASCIIToUTF16(errors::kInvalidVersion); return false; } return true; } -bool Extension::LoadAppFeatures(string16* error) { +bool Extension::LoadAppFeatures(base::string16* error) { if (!LoadExtent(keys::kWebURLs, &extent_, errors::kInvalidWebURLs, errors::kInvalidWebURL, error)) { return false; } if (manifest_->HasKey(keys::kDisplayInLauncher) && !manifest_->GetBoolean(keys::kDisplayInLauncher, &display_in_launcher_)) { - *error = ASCIIToUTF16(errors::kInvalidDisplayInLauncher); + *error = base::ASCIIToUTF16(errors::kInvalidDisplayInLauncher); return false; } if (manifest_->HasKey(keys::kDisplayInNewTabPage)) { if (!manifest_->GetBoolean(keys::kDisplayInNewTabPage, &display_in_new_tab_page_)) { - *error = ASCIIToUTF16(errors::kInvalidDisplayInNewTabPage); + *error = base::ASCIIToUTF16(errors::kInvalidDisplayInNewTabPage); return false; } } else { @@ -625,14 +628,14 @@ bool Extension::LoadExtent(const char* key, URLPatternSet* extent, const char* list_error, const char* value_error, - string16* error) { + base::string16* error) { const base::Value* temp_pattern_value = NULL; if (!manifest_->Get(key, &temp_pattern_value)) return true; const base::ListValue* pattern_list = NULL; if (!temp_pattern_value->GetAsList(&pattern_list)) { - *error = ASCIIToUTF16(list_error); + *error = base::ASCIIToUTF16(list_error); return false; } @@ -695,7 +698,7 @@ bool Extension::LoadExtent(const char* key, return true; } -bool Extension::LoadSharedFeatures(string16* error) { +bool Extension::LoadSharedFeatures(base::string16* error) { if (!LoadDescription(error) || !ManifestHandler::ParseExtension(this, error) || !LoadShortName(error)) @@ -704,23 +707,23 @@ bool Extension::LoadSharedFeatures(string16* error) { return true; } -bool Extension::LoadDescription(string16* error) { +bool Extension::LoadDescription(base::string16* error) { if (manifest_->HasKey(keys::kDescription) && !manifest_->GetString(keys::kDescription, &description_)) { - *error = ASCIIToUTF16(errors::kInvalidDescription); + *error = base::ASCIIToUTF16(errors::kInvalidDescription); return false; } return true; } -bool Extension::LoadManifestVersion(string16* error) { +bool Extension::LoadManifestVersion(base::string16* error) { // Get the original value out of the dictionary so that we can validate it // more strictly. if (manifest_->value()->HasKey(keys::kManifestVersion)) { int manifest_version = 1; if (!manifest_->GetInteger(keys::kManifestVersion, &manifest_version) || manifest_version < 1) { - *error = ASCIIToUTF16(errors::kInvalidManifestVersion); + *error = base::ASCIIToUTF16(errors::kInvalidManifestVersion); return false; } } @@ -741,17 +744,17 @@ bool Extension::LoadManifestVersion(string16* error) { return true; } -bool Extension::LoadShortName(string16* error) { +bool Extension::LoadShortName(base::string16* error) { if (manifest_->HasKey(keys::kShortName)) { - string16 localized_short_name; + base::string16 localized_short_name; if (!manifest_->GetString(keys::kShortName, &localized_short_name) || localized_short_name.empty()) { - *error = ASCIIToUTF16(errors::kInvalidShortName); + *error = base::ASCIIToUTF16(errors::kInvalidShortName); return false; } base::i18n::AdjustStringForLocaleDirection(&localized_short_name); - short_name_ = UTF16ToUTF8(localized_short_name); + short_name_ = base::UTF16ToUTF8(localized_short_name); } else { short_name_ = name_; } diff --git a/extensions/common/extension.h b/extensions/common/extension.h index 4da7a03625..4ea11b4db8 100644 --- a/extensions/common/extension.h +++ b/extensions/common/extension.h @@ -90,9 +90,9 @@ class Extension : public base::RefCountedThreadSafe<Extension> { DISABLE_UNKNOWN_FROM_SYNC = 1 << 5, DISABLE_PERMISSIONS_CONSENT = 1 << 6, // Unused - abandoned experiment. DISABLE_KNOWN_DISABLED = 1 << 7, - - // Disabled because we could not verify the install. - DISABLE_NOT_VERIFIED = 1 << 8, + DISABLE_NOT_VERIFIED = 1 << 8, // Disabled because we could not verify + // the install. + DISABLE_GREYLIST = 1 << 9 }; enum InstallType { @@ -133,8 +133,9 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // Chrome Web Store. FROM_WEBSTORE = 1 << 3, - // |FROM_BOOKMARK| indicates the extension was created using a mock App - // created from a bookmark. + // |FROM_BOOKMARK| indicates the extension is a bookmark app which has been + // generated from a web page. Bookmark apps have no permissions or extent + // and launch the web page they are created from when run. FROM_BOOKMARK = 1 << 4, // |FOLLOW_SYMLINKS_ANYWHERE| means that resources can be symlinks to @@ -341,7 +342,7 @@ class Extension : public base::RefCountedThreadSafe<Extension> { const base::FilePath& path, const std::string& explicit_id, int creation_flags, - string16* error); + base::string16* error); Extension(const base::FilePath& path, scoped_ptr<extensions::Manifest> manifest); @@ -353,28 +354,28 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // of the underlying DictionaryValue in its members. We should decide to // either wrap the DictionaryValue and go with that only, or we should parse // into strong types and discard the value. But doing both is bad. - bool InitFromValue(int flags, string16* error); + bool InitFromValue(int flags, base::string16* error); // The following are helpers for InitFromValue to load various features of the // extension from the manifest. - bool LoadRequiredFeatures(string16* error); - bool LoadName(string16* error); - bool LoadVersion(string16* error); + bool LoadRequiredFeatures(base::string16* error); + bool LoadName(base::string16* error); + bool LoadVersion(base::string16* error); - bool LoadAppFeatures(string16* error); + bool LoadAppFeatures(base::string16* error); bool LoadExtent(const char* key, URLPatternSet* extent, const char* list_error, const char* value_error, - string16* error); + base::string16* error); - bool LoadSharedFeatures(string16* error); - bool LoadDescription(string16* error); - bool LoadManifestVersion(string16* error); - bool LoadShortName(string16* error); + bool LoadSharedFeatures(base::string16* error); + bool LoadDescription(base::string16* error); + bool LoadManifestVersion(base::string16* error); + bool LoadShortName(base::string16* error); - bool CheckMinimumChromeVersion(string16* error) const; + bool CheckMinimumChromeVersion(base::string16* error) const; // The extension's human-readable name. Name is used for display purpose. It // might be wrapped with unicode bidi control characters so that it is diff --git a/extensions/common/extension_api.cc b/extensions/common/extension_api.cc index dd68ffd09e..8e27f2b036 100644 --- a/extensions/common/extension_api.cc +++ b/extensions/common/extension_api.cc @@ -241,8 +241,6 @@ void ExtensionAPI::InitDefaultConfiguration() { RegisterSchemaResource("runtime", IDR_EXTENSION_API_JSON_RUNTIME); RegisterSchemaResource("fileBrowserHandler", IDR_EXTENSION_API_JSON_FILEBROWSERHANDLER); - RegisterSchemaResource("fileBrowserPrivate", - IDR_EXTENSION_API_JSON_FILEBROWSERPRIVATE); RegisterSchemaResource("inputMethodPrivate", IDR_EXTENSION_API_JSON_INPUTMETHODPRIVATE); RegisterSchemaResource("pageAction", IDR_EXTENSION_API_JSON_PAGEACTION); @@ -251,8 +249,6 @@ void ExtensionAPI::InitDefaultConfiguration() { RegisterSchemaResource("processes", IDR_EXTENSION_API_JSON_PROCESSES); RegisterSchemaResource("proxy", IDR_EXTENSION_API_JSON_PROXY); RegisterSchemaResource("scriptBadge", IDR_EXTENSION_API_JSON_SCRIPTBADGE); - RegisterSchemaResource("streamsPrivate", - IDR_EXTENSION_API_JSON_STREAMSPRIVATE); RegisterSchemaResource("ttsEngine", IDR_EXTENSION_API_JSON_TTSENGINE); RegisterSchemaResource("tts", IDR_EXTENSION_API_JSON_TTS); RegisterSchemaResource("types", IDR_EXTENSION_API_JSON_TYPES); @@ -278,26 +274,25 @@ void ExtensionAPI::RegisterDependencyProvider(const std::string& name, dependency_providers_[name] = provider; } -bool ExtensionAPI::IsAnyFeatureAvailableToContext(const std::string& api_name, +bool ExtensionAPI::IsAnyFeatureAvailableToContext(const Feature& api, const Extension* extension, Feature::Context context, const GURL& url) { FeatureProviderMap::iterator provider = dependency_providers_.find("api"); CHECK(provider != dependency_providers_.end()); - const std::vector<std::string>& features = - provider->second->GetAllFeatureNames(); + if (IsAvailable(api, extension, context, url).is_available()) + return true; // Check to see if there are any parts of this API that are allowed in this // context. - for (std::vector<std::string>::const_iterator i = features.begin(); - i != features.end(); ++i) { - const std::string& feature_name = *i; - if (feature_name != api_name && feature_name.find(api_name + ".") == 0) { - if (IsAvailable(feature_name, extension, context, url).is_available()) - return true; - } + const std::vector<Feature*> features = provider->second->GetChildren(api); + for (std::vector<Feature*>::const_iterator feature = features.begin(); + feature != features.end(); + ++feature) { + if (IsAvailable(**feature, extension, context, url).is_available()) + return true; } - return IsAvailable(api_name, extension, context, url).is_available(); + return false; } Feature::Availability ExtensionAPI::IsAvailable(const std::string& full_name, @@ -306,14 +301,20 @@ Feature::Availability ExtensionAPI::IsAvailable(const std::string& full_name, const GURL& url) { Feature* feature = GetFeatureDependency(full_name); CHECK(feature) << full_name; + return IsAvailable(*feature, extension, context, url); +} +Feature::Availability ExtensionAPI::IsAvailable(const Feature& feature, + const Extension* extension, + Feature::Context context, + const GURL& url) { Feature::Availability availability = - feature->IsAvailableToContext(extension, context, url); + feature.IsAvailableToContext(extension, context, url); if (!availability.is_available()) return availability; - for (std::set<std::string>::iterator iter = feature->dependencies().begin(); - iter != feature->dependencies().end(); ++iter) { + for (std::set<std::string>::iterator iter = feature.dependencies().begin(); + iter != feature.dependencies().end(); ++iter) { Feature::Availability dependency_availability = IsAvailable(*iter, extension, context, url); if (!dependency_availability.is_available()) diff --git a/extensions/common/extension_api.h b/extensions/common/extension_api.h index 31ac97c1e8..1b2ad3edd2 100644 --- a/extensions/common/extension_api.h +++ b/extensions/common/extension_api.h @@ -65,10 +65,19 @@ class ExtensionAPI { void RegisterDependencyProvider(const std::string& name, FeatureProvider* provider); - // Returns true if the specified API is available. |api_full_name| can be - // either a namespace name (like "bookmarks") or a member name (like - // "bookmarks.create"). Returns true if the feature and all of its - // dependencies are available to the specified context. + // Returns true if the API feature |api| and all of its dependencies are + // available in |context|. + // + // Depending on the configuration of |api| (in _api_features.json), either + // |extension| or |url| (or both) may determine its availability, but this is + // up to the configuration of the individual feature. + Feature::Availability IsAvailable(const Feature& api, + const Extension* extension, + Feature::Context context, + const GURL& url); + // Same as the previous overload, but takes a feature name instead of an + // object. |api_full_name| can be either a namespace name (like "bookmarks") + // or a member name (like "bookmarks.create"). Feature::Availability IsAvailable(const std::string& api_full_name, const Extension* extension, Feature::Context context, @@ -76,7 +85,7 @@ class ExtensionAPI { // Determines whether an API, or any parts of that API, are available in // |context|. - bool IsAnyFeatureAvailableToContext(const std::string& api_name, + bool IsAnyFeatureAvailableToContext(const Feature& api, const Extension* extension, Feature::Context context, const GURL& url); diff --git a/extensions/common/extension_api_stub.cc b/extensions/common/extension_api_stub.cc index e323436f32..0a2468ae78 100644 --- a/extensions/common/extension_api_stub.cc +++ b/extensions/common/extension_api_stub.cc @@ -29,7 +29,15 @@ Feature::Availability ExtensionAPI::IsAvailable( return Feature::CreateAvailability(Feature::NOT_PRESENT, ""); } -bool ExtensionAPI::IsAnyFeatureAvailableToContext(const std::string& api_name, +Feature::Availability ExtensionAPI::IsAvailable( + const Feature& api, + const Extension* extension, + Feature::Context context, + const GURL& url) { + return Feature::CreateAvailability(Feature::NOT_PRESENT, ""); +} + +bool ExtensionAPI::IsAnyFeatureAvailableToContext(const Feature& api, const Extension* extension, Feature::Context context, const GURL& url) { diff --git a/extensions/common/extension_set.cc b/extensions/common/extension_set.cc new file mode 100644 index 0000000000..77d4491e96 --- /dev/null +++ b/extensions/common/extension_set.cc @@ -0,0 +1,150 @@ +// 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/extension_set.h" + +#include "base/callback.h" +#include "base/logging.h" +#include "base/stl_util.h" +#include "extensions/common/constants.h" +#include "extensions/common/extension.h" +#include "extensions/common/manifest_handlers/sandboxed_page_info.h" + +namespace extensions { + +ExtensionSet::const_iterator::const_iterator() {} + +ExtensionSet::const_iterator::const_iterator(const const_iterator& other) + : it_(other.it_) { +} + +ExtensionSet::const_iterator::const_iterator(ExtensionMap::const_iterator it) + : it_(it) { +} + +ExtensionSet::const_iterator::~const_iterator() {} + +ExtensionSet::ExtensionSet() { +} + +ExtensionSet::~ExtensionSet() { +} + +size_t ExtensionSet::size() const { + return extensions_.size(); +} + +bool ExtensionSet::is_empty() const { + return extensions_.empty(); +} + +bool ExtensionSet::Contains(const std::string& extension_id) const { + return extensions_.find(extension_id) != extensions_.end(); +} + +bool ExtensionSet::Insert(const scoped_refptr<const Extension>& extension) { + bool was_present = ContainsKey(extensions_, extension->id()); + extensions_[extension->id()] = extension; + if (!was_present && !modification_callback_.is_null()) + modification_callback_.Run(GetIDs()); + return !was_present; +} + +bool ExtensionSet::InsertAll(const ExtensionSet& extensions) { + size_t before = size(); + for (ExtensionSet::const_iterator iter = extensions.begin(); + iter != extensions.end(); ++iter) { + Insert(*iter); + } + return size() != before; +} + +bool ExtensionSet::Remove(const std::string& id) { + bool was_present = extensions_.erase(id) > 0; + if (was_present && !modification_callback_.is_null()) + modification_callback_.Run(GetIDs()); + return was_present; +} + +void ExtensionSet::Clear() { + extensions_.clear(); +} + +std::string ExtensionSet::GetExtensionOrAppIDByURL(const GURL& url) const { + if (url.SchemeIs(kExtensionScheme)) + return url.host(); + + const Extension* extension = GetHostedAppByURL(url); + if (!extension) + return std::string(); + + return extension->id(); +} + +const Extension* ExtensionSet::GetExtensionOrAppByURL(const GURL& url) const { + if (url.SchemeIs(kExtensionScheme)) + return GetByID(url.host()); + + return GetHostedAppByURL(url); +} + +const Extension* ExtensionSet::GetHostedAppByURL(const GURL& url) const { + for (ExtensionMap::const_iterator iter = extensions_.begin(); + iter != extensions_.end(); ++iter) { + if (iter->second->web_extent().MatchesURL(url)) + return iter->second.get(); + } + + return NULL; +} + +const Extension* ExtensionSet::GetHostedAppByOverlappingWebExtent( + const URLPatternSet& extent) const { + for (ExtensionMap::const_iterator iter = extensions_.begin(); + iter != extensions_.end(); ++iter) { + if (iter->second->web_extent().OverlapsWith(extent)) + return iter->second.get(); + } + + return NULL; +} + +bool ExtensionSet::InSameExtent(const GURL& old_url, + const GURL& new_url) const { + return GetExtensionOrAppByURL(old_url) == + GetExtensionOrAppByURL(new_url); +} + +const Extension* ExtensionSet::GetByID(const std::string& id) const { + ExtensionMap::const_iterator i = extensions_.find(id); + if (i != extensions_.end()) + return i->second.get(); + else + return NULL; +} + +ExtensionIdSet ExtensionSet::GetIDs() const { + ExtensionIdSet ids; + for (ExtensionMap::const_iterator it = extensions_.begin(); + it != extensions_.end(); ++it) { + ids.insert(it->first); + } + return ids; +} + +bool ExtensionSet::ExtensionBindingsAllowed(const GURL& url) const { + if (url.SchemeIs(kExtensionScheme)) + return true; + + for (ExtensionMap::const_iterator it = extensions_.begin(); + it != extensions_.end(); ++it) { + if (it->second->location() == Manifest::COMPONENT && + it->second->web_extent().MatchesURL(url)) + return true; + } + + return false; +} + +} // namespace extensions diff --git a/extensions/common/extension_set.h b/extensions/common/extension_set.h new file mode 100644 index 0000000000..4ba6530553 --- /dev/null +++ b/extensions/common/extension_set.h @@ -0,0 +1,148 @@ +// 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_EXTENSION_SET_H_ +#define EXTENSIONS_COMMON_EXTENSION_SET_H_ + +#include <iterator> +#include <map> +#include <string> + +#include "base/callback_forward.h" +#include "base/gtest_prod_util.h" +#include "base/memory/ref_counted.h" +#include "extensions/common/extension.h" +#include "url/gurl.h" + +namespace extensions { + +// The one true extension container. Extensions are identified by their id. +// Only one extension can be in the set with a given ID. +class ExtensionSet { + public: + typedef std::pair<base::FilePath, std::string> ExtensionPathAndDefaultLocale; + typedef std::map<std::string, scoped_refptr<const Extension> > ExtensionMap; + typedef base::Callback<void(const ExtensionIdSet&)> + ModificationCallback; + + // Iteration over the values of the map (given that it's an ExtensionSet, + // it should iterate like a set iterator). + class const_iterator : public std::iterator<std::input_iterator_tag, + scoped_refptr<const Extension> > { + public: + const_iterator(); + const_iterator(const const_iterator& other); + explicit const_iterator(ExtensionMap::const_iterator it); + ~const_iterator(); + const_iterator& operator++() { + ++it_; + return *this; + } + const_iterator operator++(int) { + const const_iterator old(*this); + ++it_; + return old; + } + const scoped_refptr<const Extension>& operator*() const { + return it_->second; + } + const scoped_refptr<const Extension>* operator->() const { + return &it_->second; + } + bool operator!=(const const_iterator& other) const { + return it_ != other.it_; + } + bool operator==(const const_iterator& other) const { + return it_ == other.it_; + } + + private: + ExtensionMap::const_iterator it_; + }; + + ExtensionSet(); + ~ExtensionSet(); + + size_t size() const; + bool is_empty() const; + + // Iteration support. + const_iterator begin() const { return const_iterator(extensions_.begin()); } + const_iterator end() const { return const_iterator(extensions_.end()); } + + // Returns true if the set contains the specified extension. + bool Contains(const std::string& id) const; + + // Adds the specified extension to the set. The set becomes an owner. Any + // previous extension with the same ID is removed. + // Returns true if there is no previous extension. + bool Insert(const scoped_refptr<const Extension>& extension); + + // Copies different items from |extensions| to the current set and returns + // whether anything changed. + bool InsertAll(const ExtensionSet& extensions); + + // Removes the specified extension. + // Returns true if the set contained the specified extnesion. + bool Remove(const std::string& id); + + // Removes all extensions. + void Clear(); + + // Returns the extension ID, or empty if none. This includes web URLs that + // are part of an extension's web extent. + std::string GetExtensionOrAppIDByURL(const GURL& url) const; + + // Returns the Extension, or NULL if none. This includes web URLs that are + // part of an extension's web extent. + // NOTE: This can return NULL if called before UpdateExtensions receives + // bulk extension data (e.g. if called from + // EventBindings::HandleContextCreated) + const Extension* GetExtensionOrAppByURL(const GURL& url) const; + + // Returns the hosted app whose web extent contains the URL. + const Extension* GetHostedAppByURL(const GURL& url) const; + + // Returns a hosted app that contains any URL that overlaps with the given + // extent, if one exists. + const Extension* GetHostedAppByOverlappingWebExtent( + const URLPatternSet& extent) const; + + // Returns true if |new_url| is in the extent of the same extension as + // |old_url|. Also returns true if neither URL is in an app. + bool InSameExtent(const GURL& old_url, const GURL& new_url) const; + + // Look up an Extension object by id. + const Extension* GetByID(const std::string& id) const; + + // Gets the IDs of all extensions in the set. + ExtensionIdSet GetIDs() const; + + // Returns true if |info| should get extension api bindings and be permitted + // to make api calls. Note that this is independent of what extension + // permissions the given extension has been granted. + bool ExtensionBindingsAllowed(const GURL& url) const; + + void set_modification_callback( + const ModificationCallback& modification_callback) { + modification_callback_ = modification_callback; + } + + private: + FRIEND_TEST_ALL_PREFIXES(ExtensionSetTest, ExtensionSet); + + ExtensionMap extensions_; + + // If non-null, called with the extension ids in this set after a modification + // occurred. This is not called on Clear() which is typically used when + // discarding the set (e.g., on shutdown) and we do not want to track that as + // a real modification. + ModificationCallback modification_callback_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionSet); +}; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_EXTENSION_SET_H_ diff --git a/extensions/common/extension_set_unittest.cc b/extensions/common/extension_set_unittest.cc new file mode 100644 index 0000000000..75a69de5f1 --- /dev/null +++ b/extensions/common/extension_set_unittest.cc @@ -0,0 +1,142 @@ +// 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/logging.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/values.h" +#include "extensions/common/extension.h" +#include "extensions/common/extension_set.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace extensions { + +namespace { + +scoped_refptr<Extension> CreateTestExtension(const std::string& name, + const std::string& launch_url, + const std::string& extent) { +#if defined(OS_WIN) + base::FilePath path(FILE_PATH_LITERAL("c:\\")); +#else + base::FilePath path(FILE_PATH_LITERAL("/")); +#endif + path = path.AppendASCII(name); + + base::DictionaryValue manifest; + manifest.SetString("name", name); + manifest.SetString("version", "1"); + + if (!launch_url.empty()) + manifest.SetString("app.launch.web_url", launch_url); + + if (!extent.empty()) { + base::ListValue* urls = new base::ListValue(); + manifest.Set("app.urls", urls); + urls->Append(new base::StringValue(extent)); + } + + std::string error; + scoped_refptr<Extension> extension( + Extension::Create(path, Manifest::INTERNAL, manifest, + Extension::NO_FLAGS, &error)); + EXPECT_TRUE(extension.get()) << error; + return extension; +} + +} // namespace + +TEST(ExtensionSetTest, ExtensionSet) { + scoped_refptr<Extension> ext1(CreateTestExtension( + "a", "https://chrome.google.com/launch", "https://chrome.google.com/")); + + scoped_refptr<Extension> ext2(CreateTestExtension( + "a", "http://code.google.com/p/chromium", + "http://code.google.com/p/chromium/")); + + scoped_refptr<Extension> ext3(CreateTestExtension( + "b", "http://dev.chromium.org/", "http://dev.chromium.org/")); + + scoped_refptr<Extension> ext4( + CreateTestExtension("c", std::string(), std::string())); + + ASSERT_TRUE(ext1.get() && ext2.get() && ext3.get() && ext4.get()); + + ExtensionSet extensions; + + // Add an extension. + EXPECT_TRUE(extensions.Insert(ext1)); + EXPECT_EQ(1u, extensions.size()); + EXPECT_EQ(ext1, extensions.GetByID(ext1->id())); + + // Since extension2 has same ID, it should overwrite extension1. + EXPECT_FALSE(extensions.Insert(ext2)); + EXPECT_EQ(1u, extensions.size()); + EXPECT_EQ(ext2, extensions.GetByID(ext1->id())); + + // Add the other extensions. + EXPECT_TRUE(extensions.Insert(ext3)); + EXPECT_TRUE(extensions.Insert(ext4)); + EXPECT_EQ(3u, extensions.size()); + + // Get extension by its chrome-extension:// URL + EXPECT_EQ(ext2, extensions.GetExtensionOrAppByURL( + ext2->GetResourceURL("test.html"))); + EXPECT_EQ(ext3, extensions.GetExtensionOrAppByURL( + ext3->GetResourceURL("test.html"))); + EXPECT_EQ(ext4, extensions.GetExtensionOrAppByURL( + ext4->GetResourceURL("test.html"))); + + // Get extension by web extent. + EXPECT_EQ(ext2, extensions.GetExtensionOrAppByURL( + GURL("http://code.google.com/p/chromium/monkey"))); + EXPECT_EQ(ext3, extensions.GetExtensionOrAppByURL( + GURL("http://dev.chromium.org/design-docs/"))); + EXPECT_FALSE(extensions.GetExtensionOrAppByURL( + GURL("http://blog.chromium.org/"))); + + // Test InSameExtent(). + EXPECT_TRUE(extensions.InSameExtent( + GURL("http://code.google.com/p/chromium/monkey/"), + GURL("http://code.google.com/p/chromium/"))); + EXPECT_FALSE(extensions.InSameExtent( + GURL("http://code.google.com/p/chromium/"), + GURL("https://code.google.com/p/chromium/"))); + EXPECT_FALSE(extensions.InSameExtent( + GURL("http://code.google.com/p/chromium/"), + GURL("http://dev.chromium.org/design-docs/"))); + + // Both of these should be NULL, which mean true for InSameExtent. + EXPECT_TRUE(extensions.InSameExtent( + GURL("http://www.google.com/"), + GURL("http://blog.chromium.org/"))); + + // Remove one of the extensions. + EXPECT_TRUE(extensions.Remove(ext2->id())); + EXPECT_EQ(2u, extensions.size()); + EXPECT_FALSE(extensions.GetByID(ext2->id())); + + // Make a union of a set with 3 more extensions (only 2 are new). + scoped_refptr<Extension> ext5( + CreateTestExtension("d", std::string(), std::string())); + scoped_refptr<Extension> ext6( + CreateTestExtension("e", std::string(), std::string())); + ASSERT_TRUE(ext5.get() && ext6.get()); + + scoped_ptr<ExtensionSet> to_add(new ExtensionSet()); + // |ext3| is already in |extensions|, should not affect size. + EXPECT_TRUE(to_add->Insert(ext3)); + EXPECT_TRUE(to_add->Insert(ext5)); + EXPECT_TRUE(to_add->Insert(ext6)); + + ASSERT_TRUE(extensions.Contains(ext3->id())); + ASSERT_TRUE(extensions.InsertAll(*to_add)); + EXPECT_EQ(4u, extensions.size()); + + ASSERT_FALSE(extensions.InsertAll(*to_add)); // Re-adding same set no-ops. + EXPECT_EQ(4u, extensions.size()); +} + +} // namespace extensions diff --git a/extensions/common/extensions_client.cc b/extensions/common/extensions_client.cc index ccca164a5f..89795b86e4 100644 --- a/extensions/common/extensions_client.cc +++ b/extensions/common/extensions_client.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/basictypes.h" +#include "base/logging.h" #include "extensions/common/extensions_client.h" namespace extensions { @@ -14,6 +15,7 @@ ExtensionsClient* g_client = NULL; } // namespace ExtensionsClient* ExtensionsClient::Get() { + DCHECK(g_client); return g_client; } diff --git a/extensions/common/extensions_client.h b/extensions/common/extensions_client.h index fb6aba87ac..d309fd7e80 100644 --- a/extensions/common/extensions_client.h +++ b/extensions/common/extensions_client.h @@ -28,6 +28,8 @@ class ExtensionsClient { public: typedef std::vector<std::string> ScriptingWhitelist; + virtual ~ExtensionsClient() {} + // Initializes global state. Not done in the constructor because unit tests // can create additional ExtensionsClients because the utility thread runs // in-process. diff --git a/extensions/common/feature_switch.cc b/extensions/common/feature_switch.cc index 002446196c..1108346b62 100644 --- a/extensions/common/feature_switch.cc +++ b/extensions/common/feature_switch.cc @@ -20,15 +20,12 @@ class CommonSwitches { : easy_off_store_install( switches::kEasyOffStoreExtensionInstall, FeatureSwitch::DEFAULT_DISABLED), + force_dev_mode_highlighting( + switches::kForceDevModeHighlighting, + FeatureSwitch::DEFAULT_DISABLED), global_commands( switches::kGlobalCommands, FeatureSwitch::DEFAULT_DISABLED), - script_badges( - switches::kScriptBadges, - FeatureSwitch::DEFAULT_DISABLED), - script_bubble( - switches::kScriptBubble, - FeatureSwitch::DEFAULT_DISABLED), prompt_for_external_extensions( switches::kPromptForExternalExtensions, #if defined(OS_WIN) @@ -38,14 +35,17 @@ class CommonSwitches { #endif error_console( switches::kErrorConsole, + FeatureSwitch::DEFAULT_DISABLED), + enable_override_bookmarks_ui( + switches::kEnableOverrideBookmarksUI, FeatureSwitch::DEFAULT_DISABLED) {} FeatureSwitch easy_off_store_install; + FeatureSwitch force_dev_mode_highlighting; FeatureSwitch global_commands; - FeatureSwitch script_badges; - FeatureSwitch script_bubble; FeatureSwitch prompt_for_external_extensions; FeatureSwitch error_console; + FeatureSwitch enable_override_bookmarks_ui; }; base::LazyInstance<CommonSwitches> g_common_switches = @@ -53,24 +53,24 @@ base::LazyInstance<CommonSwitches> g_common_switches = } // namespace +FeatureSwitch* FeatureSwitch::force_dev_mode_highlighting() { + return &g_common_switches.Get().force_dev_mode_highlighting; +} FeatureSwitch* FeatureSwitch::easy_off_store_install() { return &g_common_switches.Get().easy_off_store_install; } FeatureSwitch* FeatureSwitch::global_commands() { return &g_common_switches.Get().global_commands; } -FeatureSwitch* FeatureSwitch::script_badges() { - return &g_common_switches.Get().script_badges; -} -FeatureSwitch* FeatureSwitch::script_bubble() { - return &g_common_switches.Get().script_bubble; -} FeatureSwitch* FeatureSwitch::prompt_for_external_extensions() { return &g_common_switches.Get().prompt_for_external_extensions; } FeatureSwitch* FeatureSwitch::error_console() { return &g_common_switches.Get().error_console; } +FeatureSwitch* FeatureSwitch::enable_override_bookmarks_ui() { + return &g_common_switches.Get().enable_override_bookmarks_ui; +} FeatureSwitch::ScopedOverride::ScopedOverride(FeatureSwitch* feature, bool override_value) diff --git a/extensions/common/feature_switch.h b/extensions/common/feature_switch.h index ece1b01fd2..bbfd2c7a69 100644 --- a/extensions/common/feature_switch.h +++ b/extensions/common/feature_switch.h @@ -18,11 +18,11 @@ namespace extensions { class FeatureSwitch { public: static FeatureSwitch* easy_off_store_install(); + static FeatureSwitch* force_dev_mode_highlighting(); static FeatureSwitch* global_commands(); - static FeatureSwitch* script_badges(); - static FeatureSwitch* script_bubble(); static FeatureSwitch* prompt_for_external_extensions(); static FeatureSwitch* error_console(); + static FeatureSwitch* enable_override_bookmarks_ui(); enum DefaultValue { DEFAULT_ENABLED, diff --git a/extensions/common/features/feature.h b/extensions/common/features/feature.h index 0caebf6f1a..67201f07fd 100644 --- a/extensions/common/features/feature.h +++ b/extensions/common/features/feature.h @@ -39,6 +39,11 @@ class Feature { // A normal web page. This should have an associated URL matching pattern. WEB_PAGE_CONTEXT, + + // A web page context which has been blessed by the user. Typically this + // will be via the installation of a hosted app, so this may host an + // extension. This is not affected by the URL matching pattern. + BLESSED_WEB_PAGE_CONTEXT, }; // The location required of extensions the feature is supported in. @@ -101,7 +106,7 @@ class Feature { const std::string& name() const { return name_; } void set_name(const std::string& name) { name_ = name; } - const std::set<std::string>& dependencies() { return dependencies_; } + const std::set<std::string>& dependencies() const { return dependencies_; } bool no_parent() const { return no_parent_; } // Gets the platform the code is currently running on. diff --git a/extensions/common/features/feature_provider.h b/extensions/common/features/feature_provider.h index a295fe0dd2..97f94c65bf 100644 --- a/extensions/common/features/feature_provider.h +++ b/extensions/common/features/feature_provider.h @@ -19,13 +19,16 @@ class FeatureProvider { virtual ~FeatureProvider() {} // Returns the feature with the specified name. - virtual Feature* GetFeature(const std::string& name) = 0; + virtual Feature* GetFeature(const std::string& name) const = 0; // Returns the parent feature of |feature|, or NULL if there isn't one. - virtual Feature* GetParent(Feature* feature) = 0; + virtual Feature* GetParent(Feature* feature) const = 0; - // Returns all features described by this instance. - virtual const std::vector<std::string>& GetAllFeatureNames() = 0; + // Returns the features inside the |parent| namespace, recursively. + virtual std::vector<Feature*> GetChildren(const Feature& parent) const = 0; + + // Returns all features described by this instance, in asciibetical order. + virtual const std::vector<std::string>& GetAllFeatureNames() const = 0; // Gets a feature provider for a specific feature type, like "permission". static FeatureProvider* GetByName(const std::string& name); diff --git a/extensions/common/manifest.cc b/extensions/common/manifest.cc index bbd8c32c85..32981a3cf4 100644 --- a/extensions/common/manifest.cc +++ b/extensions/common/manifest.cc @@ -207,7 +207,7 @@ bool Manifest::GetString( } bool Manifest::GetString( - const std::string& path, string16* out_value) const { + const std::string& path, base::string16* out_value) const { return CanAccessPath(path) && value_->GetString(path, out_value); } diff --git a/extensions/common/manifest.h b/extensions/common/manifest.h index f7108f88f5..a50e724c57 100644 --- a/extensions/common/manifest.h +++ b/extensions/common/manifest.h @@ -96,8 +96,13 @@ class Manifest { // policy. static inline bool IsPolicyLocation(Location location) { return location == EXTERNAL_POLICY || - location == EXTERNAL_POLICY_DOWNLOAD || - location == EXTERNAL_COMPONENT; + location == EXTERNAL_POLICY_DOWNLOAD; + } + + // Whether the |location| is an extension intended to be an internal part of + // Chrome. + static inline bool IsComponentLocation(Location location) { + return location == COMPONENT || location == EXTERNAL_COMPONENT; } // Unpacked extensions start off with file access since they are a developer @@ -149,7 +154,7 @@ class Manifest { bool GetBoolean(const std::string& path, bool* out_value) const; bool GetInteger(const std::string& path, int* out_value) const; bool GetString(const std::string& path, std::string* out_value) const; - bool GetString(const std::string& path, string16* out_value) const; + bool GetString(const std::string& path, base::string16* out_value) const; bool GetDictionary(const std::string& path, const base::DictionaryValue** out_value) const; bool GetList(const std::string& path, diff --git a/extensions/common/manifest_constants.cc b/extensions/common/manifest_constants.cc index 1f28f94ffd..f7bf1f4bc5 100644 --- a/extensions/common/manifest_constants.cc +++ b/extensions/common/manifest_constants.cc @@ -16,6 +16,8 @@ const char kBackgroundPage[] = "background.page"; const char kBackgroundPageLegacy[] = "background_page"; const char kBackgroundPersistent[] = "background.persistent"; const char kBackgroundScripts[] = "background.scripts"; +const char kBluetooth[] = "bluetooth"; +const char kBookmarkUI[] = "chrome_settings_overrides.bookmarks_ui"; const char kBrowserAction[] = "browser_action"; const char kChromeURLOverrides[] = "chrome_url_overrides"; const char kCommands[] = "commands"; @@ -46,9 +48,11 @@ const char kFileHandlerExtensions[] = "extensions"; const char kFileHandlerTitle[] = "title"; const char kFileHandlerTypes[] = "types"; const char kGlobal[] = "global"; +const char kHideBookmarkButton[] = "hide_bookmark_button"; const char kHomepageURL[] = "homepage_url"; const char kIcons[] = "icons"; const char kId[] = "id"; +const char kImeOptionsPage[] = "options_page"; const char kImport[] = "import"; const char kIncognito[] = "incognito"; const char kIncludeGlobs[] = "include_globs"; @@ -106,6 +110,7 @@ const char kPlugins[] = "plugins"; const char kPluginsPath[] = "path"; const char kPluginsPublic[] = "public"; const char kPublicKey[] = "key"; +const char kRemoveButton[] = "remove_button"; const char kResources[] = "resources"; const char kRequirements[] = "requirements"; const char kRunAt[] = "run_at"; @@ -158,7 +163,8 @@ const char kWebAccessibleResources[] = "web_accessible_resources"; const char kWebURLs[] = "app.urls"; const char kWebview[] = "webview"; const char kWebviewAccessibleResources[] = "accessible_resources"; -const char kWebviewPrivilegedPartitions[] = "privileged_partitions"; +const char kWebviewName[] = "name"; +const char kWebviewPartitions[] = "partitions"; } // namespace manifest_keys @@ -217,6 +223,11 @@ const char kLaunchContainerWindow[] = "window"; // cross-platform. namespace manifest_errors { +const char kActiveTabPermissionNotGranted[] = + "The 'activeTab' permission is not in effect because this extension has " + "not been in invoked."; +const char kAllURLOrActiveTabNeeded[] = + "Either the '<all_urls>' or 'activeTab' permission is required."; const char kAppsNotEnabled[] = "Apps are not enabled."; const char kBackgroundPermissionNeeded[] = @@ -513,6 +524,8 @@ const char kInvalidPageActionsListSize[] = "Invalid value for 'page_actions'. There can be at most one page action."; const char kInvalidPageActionTypeValue[] = "Invalid value for 'page_actions[*].type', expected 'tab' or 'permanent'."; +const char kInvalidPermissionWithDetail[] = + "Invalid value for 'permissions[*]': *."; const char kInvalidPermission[] = "Invalid value for 'permissions[*]'."; const char kInvalidPermissions[] = @@ -610,10 +623,12 @@ const char kInvalidWebviewAccessibleResourcesList[] = "Invalid value for'webview.accessible_resources'."; const char kInvalidWebviewAccessibleResource[] = "Invalid value for 'webview.accessible_resources[*]'."; -const char kInvalidWebviewPrivilegedPartitionList[] = - "Invalid value for 'webview.privileged_partitions'."; -const char kInvalidWebviewPrivilegedPartition[] = - "Invalid value for 'webview.privileged_partitions[*]'."; +const char kInvalidWebviewPartition[] = + "Invalid value for 'webview.partitions[*]'."; +const char kInvalidWebviewPartitionName[] = + "Invalid value for 'webview.partitions[*].name'."; +const char kInvalidWebviewPartitionsList[] = + "Invalid value for 'webview.partitions'."; const char kInvalidWebURL[] = "Invalid value for 'app.urls[*]': *"; const char kInvalidWebURLs[] = @@ -628,6 +643,8 @@ const char kInsecureContentSecurityPolicy[] = " \"'unsafe-eval'\", \"http://127.0.0.1\", \"http://localhost\", or any" " \"https://\" or \"chrome-extension://\" origin. For more information," " see http://developer.chrome.com/extensions/contentSecurityPolicy.html"; +const char kKeyIsDeprecatedWithReplacement[] = + "Key \"*\" is deprecated. Key \"*\" should be used instead."; const char kLaunchPathAndExtentAreExclusive[] = "The 'app.launch.local_path' and 'app.urls' keys cannot both be set."; const char kLaunchPathAndURLAreExclusive[] = @@ -680,6 +697,8 @@ const char kScriptBadgeTitleIgnored[] = "default_title specified in script_badge manifest section will not be " "used."; const char kUnrecognizedManifestKey[] = "Unrecognized manifest key '*'."; +const char kUnrecognizedManifestProperty[] = + "Unrecognized property '*' of manifest key '*'."; const char kWebRequestConflictsWithLazyBackground[] = "The 'webRequest' API cannot be used with event pages."; #if defined(OS_CHROMEOS) diff --git a/extensions/common/manifest_constants.h b/extensions/common/manifest_constants.h index f2bdaf3088..a9c39fed2a 100644 --- a/extensions/common/manifest_constants.h +++ b/extensions/common/manifest_constants.h @@ -18,6 +18,8 @@ extern const char kBackgroundPage[]; extern const char kBackgroundPageLegacy[]; extern const char kBackgroundPersistent[]; extern const char kBackgroundScripts[]; +extern const char kBluetooth[]; +extern const char kBookmarkUI[]; extern const char kBrowserAction[]; extern const char kBrowseURLs[]; extern const char kChromeURLOverrides[]; @@ -48,9 +50,11 @@ extern const char kFileHandlerTypes[]; extern const char kFileFilters[]; extern const char kFileBrowserHandlers[]; extern const char kGlobal[]; +extern const char kHideBookmarkButton[]; extern const char kHomepageURL[]; extern const char kIcons[]; extern const char kId[]; +extern const char kImeOptionsPage[]; extern const char kImport[]; extern const char kIncognito[]; extern const char kIncludeGlobs[]; @@ -115,6 +119,7 @@ extern const char kPlugins[]; extern const char kPluginsPath[]; extern const char kPluginsPublic[]; extern const char kPublicKey[]; +extern const char kRemoveButton[]; extern const char kResources[]; extern const char kRequirements[]; extern const char kRunAt[]; @@ -165,8 +170,9 @@ extern const char kVersion[]; extern const char kWebAccessibleResources[]; extern const char kWebURLs[]; extern const char kWebview[]; +extern const char kWebviewName[]; extern const char kWebviewAccessibleResources[]; -extern const char kWebviewPrivilegedPartitions[]; +extern const char kWebviewPartitions[]; } // namespace manifest_keys @@ -218,11 +224,13 @@ extern const char kRunAtDocumentEnd[]; extern const char kRunAtDocumentIdle[]; extern const char kRunAtDocumentStart[]; -} // manifest_values +} // namespace manifest_values // Error messages returned from extension installation. namespace manifest_errors { +extern const char kActiveTabPermissionNotGranted[]; +extern const char kAllURLOrActiveTabNeeded[]; extern const char kAppsNotEnabled[]; extern const char kBackgroundPermissionNeeded[]; extern const char kBackgroundRequiredForPlatformApps[]; @@ -369,6 +377,7 @@ extern const char kInvalidPageActionPopupPath[]; extern const char kInvalidPageActionsList[]; extern const char kInvalidPageActionsListSize[]; extern const char kInvalidPageActionTypeValue[]; +extern const char kInvalidPermissionWithDetail[]; extern const char kInvalidPermission[]; extern const char kInvalidPermissions[]; extern const char kInvalidPermissionScheme[]; @@ -417,12 +426,14 @@ extern const char kInvalidWebAccessibleResource[]; extern const char kInvalidWebview[]; extern const char kInvalidWebviewAccessibleResourcesList[]; extern const char kInvalidWebviewAccessibleResource[]; -extern const char kInvalidWebviewPrivilegedPartitionList[]; -extern const char kInvalidWebviewPrivilegedPartition[]; +extern const char kInvalidWebviewPartition[]; +extern const char kInvalidWebviewPartitionName[]; +extern const char kInvalidWebviewPartitionsList[]; extern const char kInvalidWebURL[]; extern const char kInvalidWebURLs[]; extern const char kInvalidZipHash[]; extern const char kInsecureContentSecurityPolicy[]; +extern const char kKeyIsDeprecatedWithReplacement[]; extern const char kLaunchPathAndExtentAreExclusive[]; extern const char kLaunchPathAndURLAreExclusive[]; extern const char kLaunchURLRequired[]; @@ -445,6 +456,7 @@ extern const char kPermissionNotAllowedInManifest[]; extern const char kPermissionUnknownOrMalformed[]; extern const char kReservedMessageFound[]; extern const char kUnrecognizedManifestKey[]; +extern const char kUnrecognizedManifestProperty[]; extern const char kScriptBadgeRequiresFlag[]; extern const char kScriptBadgeIconIgnored[]; extern const char kScriptBadgeTitleIgnored[]; diff --git a/extensions/common/manifest_handler.cc b/extensions/common/manifest_handler.cc index 4546f3283a..ff0495c30b 100644 --- a/extensions/common/manifest_handler.cc +++ b/extensions/common/manifest_handler.cc @@ -79,7 +79,8 @@ bool ManifestHandler::IsRegistrationFinalized() { } // static -bool ManifestHandler::ParseExtension(Extension* extension, string16* error) { +bool ManifestHandler::ParseExtension(Extension* extension, + base::string16* error) { return GetRegistry()->ParseExtension(extension, error); } @@ -127,7 +128,7 @@ void ManifestHandlerRegistry::RegisterManifestHandler( } bool ManifestHandlerRegistry::ParseExtension(Extension* extension, - string16* error) { + base::string16* error) { std::map<int, ManifestHandler*> handlers_by_priority; for (ManifestHandlerMap::iterator iter = handlers_.begin(); iter != handlers_.end(); ++iter) { diff --git a/extensions/common/manifest_handler.h b/extensions/common/manifest_handler.h index 90fbe84998..4c7da31eee 100644 --- a/extensions/common/manifest_handler.h +++ b/extensions/common/manifest_handler.h @@ -29,7 +29,7 @@ class ManifestHandler { // Attempts to parse the extension's manifest. // Returns true on success or false on failure; if false, |error| will // be set to a failure message. - virtual bool Parse(Extension* extension, string16* error) = 0; + virtual bool Parse(Extension* extension, base::string16* error) = 0; // Validate that files associated with this manifest key exist. // Validation takes place after parsing. May also append a series of @@ -37,7 +37,7 @@ class ManifestHandler { // // Otherwise, returns false, and a description of the error is // returned in |error|. - // TODO(yoz): Change error to string16. See crbug.com/71980. + // TODO(yoz): Change error to base::string16. See crbug.com/71980. virtual bool Validate(const Extension* extension, std::string* error, std::vector<InstallWarning>* warnings) const; @@ -62,7 +62,7 @@ class ManifestHandler { // for multiple keys. The global registry takes ownership of this; // if it has an existing handler for |key|, it replaces it with this. // Manifest handlers must be registered at process startup in - // chrome_manifest_handlers.cc: + // common_manifest_handlers.cc or chrome_manifest_handlers.cc: // (new MyManifestHandler)->Register(); void Register(); @@ -87,7 +87,7 @@ class ManifestHandler { // Call Parse on all registered manifest handlers that should parse // this extension. - static bool ParseExtension(Extension* extension, string16* error); + static bool ParseExtension(Extension* extension, base::string16* error); // Call Validate on all registered manifest handlers for this extension. static bool ValidateExtension(const Extension* extension, @@ -132,7 +132,7 @@ class ManifestHandlerRegistry { void RegisterManifestHandler(const std::string& key, linked_ptr<ManifestHandler> handler); - bool ParseExtension(Extension* extension, string16* error); + bool ParseExtension(Extension* extension, base::string16* error); bool ValidateExtension(const Extension* extension, std::string* error, std::vector<InstallWarning>* warnings); diff --git a/extensions/common/manifest_handler_unittest.cc b/extensions/common/manifest_handler_unittest.cc index 9a3a29298b..1da6b408a4 100644 --- a/extensions/common/manifest_handler_unittest.cc +++ b/extensions/common/manifest_handler_unittest.cc @@ -83,7 +83,7 @@ class ManifestHandlerTest : public testing::Test { : name_(name), keys_(keys), prereqs_(prereqs), watcher_(watcher) { } - virtual bool Parse(Extension* extension, string16* error) OVERRIDE { + virtual bool Parse(Extension* extension, base::string16* error) OVERRIDE { watcher_->Record(name_); return true; } @@ -111,8 +111,8 @@ class ManifestHandlerTest : public testing::Test { ParsingWatcher* watcher) : TestManifestHandler(name, keys, prereqs, watcher) { } - virtual bool Parse(Extension* extension, string16* error) OVERRIDE { - *error = ASCIIToUTF16(name_); + virtual bool Parse(Extension* extension, base::string16* error) OVERRIDE { + *error = base::ASCIIToUTF16(name_); return false; } }; @@ -141,7 +141,7 @@ class ManifestHandlerTest : public testing::Test { keys_(keys) { } - virtual bool Parse(Extension* extension, string16* error) OVERRIDE { + virtual bool Parse(Extension* extension, base::string16* error) OVERRIDE { return true; } diff --git a/extensions/common/manifest_handlers/background_info.cc b/extensions/common/manifest_handlers/background_info.cc index 1ec404eaea..c7aee9053a 100644 --- a/extensions/common/manifest_handlers/background_info.cc +++ b/extensions/common/manifest_handlers/background_info.cc @@ -20,6 +20,7 @@ #include "grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" +using base::ASCIIToUTF16; using base::DictionaryValue; namespace extensions { @@ -93,7 +94,7 @@ bool BackgroundInfo::HasLazyBackgroundPage(const Extension* extension) { return GetBackgroundInfo(extension).has_lazy_background_page(); } -bool BackgroundInfo::Parse(const Extension* extension, string16* error) { +bool BackgroundInfo::Parse(const Extension* extension, base::string16* error) { const std::string& bg_scripts_key = extension->is_platform_app() ? keys::kPlatformAppBackgroundScripts : keys::kBackgroundScripts; if (!LoadBackgroundScripts(extension, bg_scripts_key, error) || @@ -107,7 +108,7 @@ bool BackgroundInfo::Parse(const Extension* extension, string16* error) { bool BackgroundInfo::LoadBackgroundScripts(const Extension* extension, const std::string& key, - string16* error) { + base::string16* error) { const base::Value* background_scripts_value = NULL; if (!extension->manifest()->Get(key, &background_scripts_value)) return true; @@ -135,7 +136,7 @@ bool BackgroundInfo::LoadBackgroundScripts(const Extension* extension, bool BackgroundInfo::LoadBackgroundPage(const Extension* extension, const std::string& key, - string16* error) { + base::string16* error) { const base::Value* background_page_value = NULL; if (!extension->manifest()->Get(key, &background_page_value)) return true; @@ -180,7 +181,7 @@ bool BackgroundInfo::LoadBackgroundPage(const Extension* extension, } bool BackgroundInfo::LoadBackgroundPage(const Extension* extension, - string16* error) { + base::string16* error) { if (extension->is_platform_app()) { return LoadBackgroundPage( extension, keys::kPlatformAppBackgroundPage, error); @@ -194,7 +195,7 @@ bool BackgroundInfo::LoadBackgroundPage(const Extension* extension, } bool BackgroundInfo::LoadBackgroundPersistent(const Extension* extension, - string16* error) { + base::string16* error) { if (extension->is_platform_app()) { is_persistent_ = false; return true; @@ -219,7 +220,7 @@ bool BackgroundInfo::LoadBackgroundPersistent(const Extension* extension, } bool BackgroundInfo::LoadAllowJSAccess(const Extension* extension, - string16* error) { + base::string16* error) { const base::Value* allow_js_access = NULL; if (!extension->manifest()->Get(keys::kBackgroundAllowJsAccess, &allow_js_access)) @@ -240,7 +241,8 @@ BackgroundManifestHandler::BackgroundManifestHandler() { BackgroundManifestHandler::~BackgroundManifestHandler() { } -bool BackgroundManifestHandler::Parse(Extension* extension, string16* error) { +bool BackgroundManifestHandler::Parse(Extension* extension, + base::string16* error) { scoped_ptr<BackgroundInfo> info(new BackgroundInfo); if (!info->Parse(extension, error)) return false; @@ -274,7 +276,7 @@ bool BackgroundManifestHandler::Validate( extension->GetResource(background_scripts[i]).GetFilePath())) { *error = l10n_util::GetStringFUTF8( IDS_EXTENSION_LOAD_BACKGROUND_SCRIPT_FAILED, - UTF8ToUTF16(background_scripts[i])); + base::UTF8ToUTF16(background_scripts[i])); return false; } } diff --git a/extensions/common/manifest_handlers/background_info.h b/extensions/common/manifest_handlers/background_info.h index 113c341c31..e9e360e696 100644 --- a/extensions/common/manifest_handlers/background_info.h +++ b/extensions/common/manifest_handlers/background_info.h @@ -41,18 +41,19 @@ class BackgroundInfo : public Extension::ManifestData { return has_background_page() && !is_persistent_; } - bool Parse(const Extension* extension, string16* error); + bool Parse(const Extension* extension, base::string16* error); private: bool LoadBackgroundScripts(const Extension* extension, const std::string& key, - string16* error); + base::string16* error); bool LoadBackgroundPage(const Extension* extension, const std::string& key, - string16* error); - bool LoadBackgroundPage(const Extension* extension, string16* error); - bool LoadBackgroundPersistent(const Extension* extension, string16* error); - bool LoadAllowJSAccess(const Extension* extension, string16* error); + base::string16* error); + bool LoadBackgroundPage(const Extension* extension, base::string16* error); + bool LoadBackgroundPersistent(const Extension* extension, + base::string16* error); + bool LoadAllowJSAccess(const Extension* extension, base::string16* error); // Optional URL to a master page of which a single instance should be always // loaded in the background. @@ -82,7 +83,7 @@ class BackgroundManifestHandler : public ManifestHandler { BackgroundManifestHandler(); virtual ~BackgroundManifestHandler(); - virtual bool Parse(Extension* extension, string16* error) OVERRIDE; + virtual bool Parse(Extension* extension, base::string16* error) OVERRIDE; virtual bool Validate(const Extension* extension, std::string* error, std::vector<InstallWarning>* warnings) const OVERRIDE; diff --git a/extensions/common/manifest_handlers/csp_info.cc b/extensions/common/manifest_handlers/csp_info.cc index 79f531a4f7..2fd74fb76b 100644 --- a/extensions/common/manifest_handlers/csp_info.cc +++ b/extensions/common/manifest_handlers/csp_info.cc @@ -77,7 +77,7 @@ CSPHandler::CSPHandler(bool is_platform_app) CSPHandler::~CSPHandler() { } -bool CSPHandler::Parse(Extension* extension, string16* error) { +bool CSPHandler::Parse(Extension* extension, base::string16* error) { const std::string key = Keys()[0]; if (!extension->manifest()->HasPath(key)) { if (extension->manifest_version() >= 2) { @@ -97,17 +97,17 @@ bool CSPHandler::Parse(Extension* extension, string16* error) { std::string content_security_policy; if (!extension->manifest()->GetString(key, &content_security_policy)) { - *error = ASCIIToUTF16(errors::kInvalidContentSecurityPolicy); + *error = base::ASCIIToUTF16(errors::kInvalidContentSecurityPolicy); return false; } if (!ContentSecurityPolicyIsLegal(content_security_policy)) { - *error = ASCIIToUTF16(errors::kInvalidContentSecurityPolicy); + *error = base::ASCIIToUTF16(errors::kInvalidContentSecurityPolicy); return false; } if (extension->manifest_version() >= 2 && !ContentSecurityPolicyIsSecure(content_security_policy, extension->GetType())) { - *error = ASCIIToUTF16(errors::kInsecureContentSecurityPolicy); + *error = base::ASCIIToUTF16(errors::kInsecureContentSecurityPolicy); return false; } diff --git a/extensions/common/manifest_handlers/csp_info.h b/extensions/common/manifest_handlers/csp_info.h index c0ac884520..7b5b25d13e 100644 --- a/extensions/common/manifest_handlers/csp_info.h +++ b/extensions/common/manifest_handlers/csp_info.h @@ -38,7 +38,7 @@ class CSPHandler : public ManifestHandler { explicit CSPHandler(bool is_platform_app); virtual ~CSPHandler(); - virtual bool Parse(Extension* extension, string16* error) OVERRIDE; + virtual bool Parse(Extension* extension, base::string16* error) OVERRIDE; virtual bool AlwaysParseForType(Manifest::Type type) const OVERRIDE; private: diff --git a/extensions/common/manifest_handlers/incognito_info.cc b/extensions/common/manifest_handlers/incognito_info.cc index b57a8a633d..02c3b28a52 100644 --- a/extensions/common/manifest_handlers/incognito_info.cc +++ b/extensions/common/manifest_handlers/incognito_info.cc @@ -34,7 +34,7 @@ IncognitoHandler::IncognitoHandler() { IncognitoHandler::~IncognitoHandler() { } -bool IncognitoHandler::Parse(Extension* extension, string16* error) { +bool IncognitoHandler::Parse(Extension* extension, base::string16* error) { if (!extension->manifest()->HasKey(keys::kIncognito)) { // Extensions and Chrome apps default to spanning mode. // Hosted and legacy packaged apps default to split mode. @@ -48,7 +48,7 @@ bool IncognitoHandler::Parse(Extension* extension, string16* error) { bool split_mode = false; std::string incognito_string; if (!extension->manifest()->GetString(keys::kIncognito, &incognito_string)) { - *error = ASCIIToUTF16(manifest_errors::kInvalidIncognitoBehavior); + *error = base::ASCIIToUTF16(manifest_errors::kInvalidIncognitoBehavior); return false; } @@ -57,7 +57,7 @@ bool IncognitoHandler::Parse(Extension* extension, string16* error) { else if (incognito_string != manifest_values::kIncognitoSpanning) { // If incognito_string == kIncognitoSpanning, it is valid and // split_mode remains false. - *error = ASCIIToUTF16(manifest_errors::kInvalidIncognitoBehavior); + *error = base::ASCIIToUTF16(manifest_errors::kInvalidIncognitoBehavior); return false; } diff --git a/extensions/common/manifest_handlers/incognito_info.h b/extensions/common/manifest_handlers/incognito_info.h index cd44934a93..3650883c46 100644 --- a/extensions/common/manifest_handlers/incognito_info.h +++ b/extensions/common/manifest_handlers/incognito_info.h @@ -29,7 +29,7 @@ class IncognitoHandler : public ManifestHandler { IncognitoHandler(); virtual ~IncognitoHandler(); - virtual bool Parse(Extension* extension, string16* error) OVERRIDE; + virtual bool Parse(Extension* extension, base::string16* error) OVERRIDE; virtual bool AlwaysParseForType(Manifest::Type type) const OVERRIDE; private: diff --git a/extensions/common/manifest_handlers/kiosk_mode_info.cc b/extensions/common/manifest_handlers/kiosk_mode_info.cc index 92f406b012..f5d36afbef 100644 --- a/extensions/common/manifest_handlers/kiosk_mode_info.cc +++ b/extensions/common/manifest_handlers/kiosk_mode_info.cc @@ -44,7 +44,7 @@ KioskModeHandler::KioskModeHandler() { KioskModeHandler::~KioskModeHandler() { } -bool KioskModeHandler::Parse(Extension* extension, string16* error) { +bool KioskModeHandler::Parse(Extension* extension, base::string16* error) { const Manifest* manifest = extension->manifest(); DCHECK(manifest->HasKey(keys::kKioskEnabled) || manifest->HasKey(keys::kKioskOnly)); @@ -52,19 +52,20 @@ bool KioskModeHandler::Parse(Extension* extension, string16* error) { bool kiosk_enabled = false; if (manifest->HasKey(keys::kKioskEnabled) && !manifest->GetBoolean(keys::kKioskEnabled, &kiosk_enabled)) { - *error = ASCIIToUTF16(manifest_errors::kInvalidKioskEnabled); + *error = base::ASCIIToUTF16(manifest_errors::kInvalidKioskEnabled); return false; } bool kiosk_only = false; if (manifest->HasKey(keys::kKioskOnly) && !manifest->GetBoolean(keys::kKioskOnly, &kiosk_only)) { - *error = ASCIIToUTF16(manifest_errors::kInvalidKioskOnly); + *error = base::ASCIIToUTF16(manifest_errors::kInvalidKioskOnly); return false; } if (kiosk_only && !kiosk_enabled) { - *error = ASCIIToUTF16(manifest_errors::kInvalidKioskOnlyButNotEnabled); + *error = base::ASCIIToUTF16( + manifest_errors::kInvalidKioskOnlyButNotEnabled); return false; } diff --git a/extensions/common/manifest_handlers/kiosk_mode_info.h b/extensions/common/manifest_handlers/kiosk_mode_info.h index e98fa31559..9cf57e9c3c 100644 --- a/extensions/common/manifest_handlers/kiosk_mode_info.h +++ b/extensions/common/manifest_handlers/kiosk_mode_info.h @@ -40,7 +40,7 @@ class KioskModeHandler : public ManifestHandler { KioskModeHandler(); virtual ~KioskModeHandler(); - virtual bool Parse(Extension* extension, string16* error) OVERRIDE; + virtual bool Parse(Extension* extension, base::string16* error) OVERRIDE; private: virtual const std::vector<std::string> Keys() const OVERRIDE; diff --git a/extensions/common/manifest_handlers/offline_enabled_info.cc b/extensions/common/manifest_handlers/offline_enabled_info.cc index 7cf1835d40..1d1f8b1540 100644 --- a/extensions/common/manifest_handlers/offline_enabled_info.cc +++ b/extensions/common/manifest_handlers/offline_enabled_info.cc @@ -35,7 +35,7 @@ OfflineEnabledHandler::OfflineEnabledHandler() { OfflineEnabledHandler::~OfflineEnabledHandler() { } -bool OfflineEnabledHandler::Parse(Extension* extension, string16* error) { +bool OfflineEnabledHandler::Parse(Extension* extension, base::string16* error) { if (!extension->manifest()->HasKey(keys::kOfflineEnabled)) { // Only platform apps default to being enabled offline, and we should only // attempt parsing without a key present if it is a platform app. @@ -49,7 +49,7 @@ bool OfflineEnabledHandler::Parse(Extension* extension, string16* error) { if (!extension->manifest()->GetBoolean(keys::kOfflineEnabled, &offline_enabled)) { - *error = ASCIIToUTF16(manifest_errors::kInvalidOfflineEnabled); + *error = base::ASCIIToUTF16(manifest_errors::kInvalidOfflineEnabled); return false; } diff --git a/extensions/common/manifest_handlers/offline_enabled_info.h b/extensions/common/manifest_handlers/offline_enabled_info.h index e9b4f35d10..55aa837c6b 100644 --- a/extensions/common/manifest_handlers/offline_enabled_info.h +++ b/extensions/common/manifest_handlers/offline_enabled_info.h @@ -31,7 +31,7 @@ class OfflineEnabledHandler : public ManifestHandler { OfflineEnabledHandler(); virtual ~OfflineEnabledHandler(); - virtual bool Parse(Extension* extension, string16* error) OVERRIDE; + virtual bool Parse(Extension* extension, base::string16* error) OVERRIDE; virtual bool AlwaysParseForType(Manifest::Type type) const OVERRIDE; private: diff --git a/extensions/common/manifest_handlers/requirements_info.cc b/extensions/common/manifest_handlers/requirements_info.cc index aaf30dd1fe..a775324c88 100644 --- a/extensions/common/manifest_handlers/requirements_info.cc +++ b/extensions/common/manifest_handlers/requirements_info.cc @@ -60,7 +60,7 @@ bool RequirementsHandler::AlwaysParseForType(Manifest::Type type) const { return true; } -bool RequirementsHandler::Parse(Extension* extension, string16* error) { +bool RequirementsHandler::Parse(Extension* extension, base::string16* error) { scoped_ptr<RequirementsInfo> requirements( new RequirementsInfo(extension->manifest())); @@ -72,7 +72,7 @@ bool RequirementsHandler::Parse(Extension* extension, string16* error) { const base::DictionaryValue* requirements_value = NULL; if (!extension->manifest()->GetDictionary(keys::kRequirements, &requirements_value)) { - *error = ASCIIToUTF16(errors::kInvalidRequirements); + *error = base::ASCIIToUTF16(errors::kInvalidRequirements); return false; } @@ -129,7 +129,7 @@ bool RequirementsHandler::Parse(Extension* extension, string16* error) { } } } else { - *error = ASCIIToUTF16(errors::kInvalidRequirements); + *error = base::ASCIIToUTF16(errors::kInvalidRequirements); return false; } } diff --git a/extensions/common/manifest_handlers/requirements_info.h b/extensions/common/manifest_handlers/requirements_info.h index adc128bdb2..37dbfb98b4 100644 --- a/extensions/common/manifest_handlers/requirements_info.h +++ b/extensions/common/manifest_handlers/requirements_info.h @@ -32,7 +32,7 @@ class RequirementsHandler : public ManifestHandler { RequirementsHandler(); virtual ~RequirementsHandler(); - virtual bool Parse(Extension* extension, string16* error) OVERRIDE; + virtual bool Parse(Extension* extension, base::string16* error) OVERRIDE; virtual bool AlwaysParseForType(Manifest::Type type) const OVERRIDE; diff --git a/extensions/common/manifest_handlers/sandboxed_page_info.cc b/extensions/common/manifest_handlers/sandboxed_page_info.cc index a225a709a2..2471e93467 100644 --- a/extensions/common/manifest_handlers/sandboxed_page_info.cc +++ b/extensions/common/manifest_handlers/sandboxed_page_info.cc @@ -61,12 +61,12 @@ SandboxedPageHandler::SandboxedPageHandler() { SandboxedPageHandler::~SandboxedPageHandler() { } -bool SandboxedPageHandler::Parse(Extension* extension, string16* error) { +bool SandboxedPageHandler::Parse(Extension* extension, base::string16* error) { scoped_ptr<SandboxedPageInfo> sandboxed_info(new SandboxedPageInfo); const base::ListValue* list_value = NULL; if (!extension->manifest()->GetList(keys::kSandboxedPages, &list_value)) { - *error = ASCIIToUTF16(errors::kInvalidSandboxedPagesList); + *error = base::ASCIIToUTF16(errors::kInvalidSandboxedPagesList); return false; } @@ -93,7 +93,7 @@ bool SandboxedPageHandler::Parse(Extension* extension, string16* error) { if (!extension->manifest()->GetString( keys::kSandboxedPagesCSP, &sandboxed_info->content_security_policy)) { - *error = ASCIIToUTF16(errors::kInvalidSandboxedPagesCSP); + *error = base::ASCIIToUTF16(errors::kInvalidSandboxedPagesCSP); return false; } @@ -101,7 +101,7 @@ bool SandboxedPageHandler::Parse(Extension* extension, string16* error) { sandboxed_info->content_security_policy) || !csp_validator::ContentSecurityPolicyIsSandboxed( sandboxed_info->content_security_policy, extension->GetType())) { - *error = ASCIIToUTF16(errors::kInvalidSandboxedPagesCSP); + *error = base::ASCIIToUTF16(errors::kInvalidSandboxedPagesCSP); return false; } } else { diff --git a/extensions/common/manifest_handlers/sandboxed_page_info.h b/extensions/common/manifest_handlers/sandboxed_page_info.h index 936973e9c7..9dbc9a6290 100644 --- a/extensions/common/manifest_handlers/sandboxed_page_info.h +++ b/extensions/common/manifest_handlers/sandboxed_page_info.h @@ -44,7 +44,7 @@ class SandboxedPageHandler : public ManifestHandler { SandboxedPageHandler(); virtual ~SandboxedPageHandler(); - virtual bool Parse(Extension* extension, string16* error) OVERRIDE; + virtual bool Parse(Extension* extension, base::string16* error) OVERRIDE; private: virtual const std::vector<std::string> Keys() const OVERRIDE; diff --git a/extensions/common/manifest_handlers/shared_module_info.cc b/extensions/common/manifest_handlers/shared_module_info.cc index 09df879b73..5f44160992 100644 --- a/extensions/common/manifest_handlers/shared_module_info.cc +++ b/extensions/common/manifest_handlers/shared_module_info.cc @@ -105,26 +105,27 @@ const std::vector<SharedModuleInfo::ImportInfo>& SharedModuleInfo::GetImports( return GetSharedModuleInfo(extension).imports_; } -bool SharedModuleInfo::Parse(const Extension* extension, string16* error) { +bool SharedModuleInfo::Parse(const Extension* extension, + base::string16* error) { bool has_import = extension->manifest()->HasKey(keys::kImport); bool has_export = extension->manifest()->HasKey(keys::kExport); if (!has_import && !has_export) return true; if (has_import && has_export) { - *error = ASCIIToUTF16(errors::kInvalidImportAndExport); + *error = base::ASCIIToUTF16(errors::kInvalidImportAndExport); return false; } if (has_export) { const base::DictionaryValue* export_value = NULL; if (!extension->manifest()->GetDictionary(keys::kExport, &export_value)) { - *error = ASCIIToUTF16(errors::kInvalidExport); + *error = base::ASCIIToUTF16(errors::kInvalidExport); return false; } const base::ListValue* resources_list = NULL; if (!export_value->GetList(keys::kResources, &resources_list)) { - *error = ASCIIToUTF16(errors::kInvalidExportResources); + *error = base::ASCIIToUTF16(errors::kInvalidExportResources); return false; } for (size_t i = 0; i < resources_list->GetSize(); ++i) { @@ -148,13 +149,13 @@ bool SharedModuleInfo::Parse(const Extension* extension, string16* error) { if (has_import) { const base::ListValue* import_list = NULL; if (!extension->manifest()->GetList(keys::kImport, &import_list)) { - *error = ASCIIToUTF16(errors::kInvalidImport); + *error = base::ASCIIToUTF16(errors::kInvalidImport); return false; } for (size_t i = 0; i < import_list->GetSize(); ++i) { const base::DictionaryValue* import_entry = NULL; if (!import_list->GetDictionary(i, &import_entry)) { - *error = ASCIIToUTF16(errors::kInvalidImport); + *error = base::ASCIIToUTF16(errors::kInvalidImport); return false; } std::string extension_id; @@ -193,7 +194,7 @@ SharedModuleHandler::SharedModuleHandler() { SharedModuleHandler::~SharedModuleHandler() { } -bool SharedModuleHandler::Parse(Extension* extension, string16* error) { +bool SharedModuleHandler::Parse(Extension* extension, base::string16* error) { scoped_ptr<SharedModuleInfo> info(new SharedModuleInfo); if (!info->Parse(extension, error)) return false; diff --git a/extensions/common/manifest_handlers/shared_module_info.h b/extensions/common/manifest_handlers/shared_module_info.h index db51594b07..4240ed7a81 100644 --- a/extensions/common/manifest_handlers/shared_module_info.h +++ b/extensions/common/manifest_handlers/shared_module_info.h @@ -19,7 +19,7 @@ class SharedModuleInfo : public Extension::ManifestData { SharedModuleInfo(); virtual ~SharedModuleInfo(); - bool Parse(const Extension* extension, string16* error); + bool Parse(const Extension* extension, base::string16* error); struct ImportInfo { std::string extension_id; @@ -57,7 +57,7 @@ class SharedModuleHandler : public ManifestHandler { SharedModuleHandler(); virtual ~SharedModuleHandler(); - virtual bool Parse(Extension* extension, string16* error) OVERRIDE; + virtual bool Parse(Extension* extension, base::string16* error) OVERRIDE; virtual bool Validate(const Extension* extension, std::string* error, std::vector<InstallWarning>* warnings) const OVERRIDE; diff --git a/extensions/common/manifest_handlers/web_accessible_resources_info.cc b/extensions/common/manifest_handlers/web_accessible_resources_info.cc new file mode 100644 index 0000000000..8da6bf5c7a --- /dev/null +++ b/extensions/common/manifest_handlers/web_accessible_resources_info.cc @@ -0,0 +1,99 @@ +// Copyright 2014 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_handlers/web_accessible_resources_info.h" + +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "extensions/common/error_utils.h" +#include "extensions/common/manifest.h" +#include "extensions/common/manifest_constants.h" + +namespace extensions { + +namespace keys = manifest_keys; +namespace errors = manifest_errors; + +namespace { + +const WebAccessibleResourcesInfo* GetResourcesInfo(const Extension* extension) { + return static_cast<WebAccessibleResourcesInfo*>( + extension->GetManifestData(keys::kWebAccessibleResources)); +} + +} // namespace + +WebAccessibleResourcesInfo::WebAccessibleResourcesInfo() { +} + +WebAccessibleResourcesInfo::~WebAccessibleResourcesInfo() { +} + +// static +bool WebAccessibleResourcesInfo::IsResourceWebAccessible( + const Extension* extension, + const std::string& relative_path) { + // For old manifest versions which do not specify web_accessible_resources + // we always allow resource loads. + if (extension->manifest_version() < 2 && + !WebAccessibleResourcesInfo::HasWebAccessibleResources(extension)) + return true; + + const WebAccessibleResourcesInfo* info = GetResourcesInfo(extension); + return info && + extension->ResourceMatches( + info->web_accessible_resources_, relative_path); +} + +// static +bool WebAccessibleResourcesInfo::HasWebAccessibleResources( + const Extension* extension) { + const WebAccessibleResourcesInfo* info = GetResourcesInfo(extension); + return info && info->web_accessible_resources_.size() > 0; +} + +WebAccessibleResourcesHandler::WebAccessibleResourcesHandler() { +} + +WebAccessibleResourcesHandler::~WebAccessibleResourcesHandler() { +} + +bool WebAccessibleResourcesHandler::Parse(Extension* extension, + base::string16* error) { + scoped_ptr<WebAccessibleResourcesInfo> info(new WebAccessibleResourcesInfo); + const base::ListValue* list_value = NULL; + if (!extension->manifest()->GetList(keys::kWebAccessibleResources, + &list_value)) { + *error = base::ASCIIToUTF16(errors::kInvalidWebAccessibleResourcesList); + return false; + } + for (size_t i = 0; i < list_value->GetSize(); ++i) { + std::string relative_path; + if (!list_value->GetString(i, &relative_path)) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidWebAccessibleResource, base::IntToString(i)); + return false; + } + URLPattern pattern(URLPattern::SCHEME_EXTENSION); + if (pattern.Parse(extension->url().spec()) != URLPattern::PARSE_SUCCESS) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidURLPatternError, extension->url().spec()); + return false; + } + while (relative_path[0] == '/') + relative_path = relative_path.substr(1, relative_path.length() - 1); + pattern.SetPath(pattern.path() + relative_path); + info->web_accessible_resources_.AddPattern(pattern); + } + extension->SetManifestData(keys::kWebAccessibleResources, info.release()); + return true; +} + +const std::vector<std::string> WebAccessibleResourcesHandler::Keys() const { + return SingleKey(keys::kWebAccessibleResources); +} + +} // namespace extensions diff --git a/extensions/common/manifest_handlers/web_accessible_resources_info.h b/extensions/common/manifest_handlers/web_accessible_resources_info.h new file mode 100644 index 0000000000..2d0ed81e6b --- /dev/null +++ b/extensions/common/manifest_handlers/web_accessible_resources_info.h @@ -0,0 +1,50 @@ +// Copyright 2014 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_HANDLERS_WEB_ACCESSIBLE_RESOURCES_INFO_H_ +#define EXTENSIONS_COMMON_MANIFEST_HANDLERS_WEB_ACCESSIBLE_RESOURCES_INFO_H_ + +#include <string> + +#include "extensions/common/extension.h" +#include "extensions/common/manifest_handler.h" + +namespace extensions { + +// A structure to hold the web accessible extension resources +// that may be specified in the manifest of an extension using +// "web_accessible_resources" key. +struct WebAccessibleResourcesInfo : public Extension::ManifestData { + // Define out of line constructor/destructor to please Clang. + WebAccessibleResourcesInfo(); + virtual ~WebAccessibleResourcesInfo(); + + // Returns true if the specified resource is web accessible. + static bool IsResourceWebAccessible(const Extension* extension, + const std::string& relative_path); + + // Returns true when 'web_accessible_resources' are defined for the extension. + static bool HasWebAccessibleResources(const Extension* extension); + + // Optional list of web accessible extension resources. + URLPatternSet web_accessible_resources_; +}; + +// Parses the "web_accessible_resources" manifest key. +class WebAccessibleResourcesHandler : public ManifestHandler { + public: + WebAccessibleResourcesHandler(); + virtual ~WebAccessibleResourcesHandler(); + + virtual bool Parse(Extension* extension, base::string16* error) OVERRIDE; + + private: + virtual const std::vector<std::string> Keys() const OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(WebAccessibleResourcesHandler); +}; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_MANIFEST_HANDLERS_WEB_ACCESSIBLE_RESOURCES_INFO_H_ diff --git a/extensions/common/manifest_handlers/webview_info.cc b/extensions/common/manifest_handlers/webview_info.cc new file mode 100644 index 0000000000..3b30bbe421 --- /dev/null +++ b/extensions/common/manifest_handlers/webview_info.cc @@ -0,0 +1,182 @@ +// Copyright 2014 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_handlers/webview_info.h" + +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "extensions/common/error_utils.h" +#include "extensions/common/manifest.h" +#include "extensions/common/manifest_constants.h" + +namespace extensions { + +namespace keys = extensions::manifest_keys; +namespace errors = extensions::manifest_errors; + +namespace { + +const WebviewInfo* GetResourcesInfo( + const Extension& extension) { + return static_cast<WebviewInfo*>( + extension.GetManifestData(keys::kWebviewAccessibleResources)); +} + +} // namespace + +// A PartitionItem represents a set of accessible resources given a partition +// ID pattern. +class PartitionItem { + public: + explicit PartitionItem(const std::string& partition_pattern) + : partition_pattern_(partition_pattern) { + } + + virtual ~PartitionItem() { + } + + bool Matches(const std::string& partition_id) const { + return MatchPattern(partition_id, partition_pattern_); + } + + // 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) { + return accessible_resources_.AddPattern(pattern); + } + + const URLPatternSet& accessible_resources() const { + return accessible_resources_; + } + private: + // A pattern string that matches partition IDs. + const std::string partition_pattern_; + // A URL pattern set of resources accessible to the given + // |partition_pattern_|. + URLPatternSet accessible_resources_; +}; + + +WebviewInfo::WebviewInfo() { +} + +WebviewInfo::~WebviewInfo() { +} + +// static +bool WebviewInfo::IsResourceWebviewAccessible( + const Extension* extension, + const std::string& partition_id, + const std::string& relative_path) { + if (!extension) + return false; + + const WebviewInfo* info = GetResourcesInfo(*extension); + if (!info) + return false; + + for (size_t i = 0; i < info->partition_items_.size(); ++i) { + const PartitionItem* const item = info->partition_items_[i]; + if (item->Matches(partition_id) && + extension->ResourceMatches(item->accessible_resources(), + relative_path)) { + return true; + } + } + + return false; +} + +void WebviewInfo::AddPartitionItem(scoped_ptr<PartitionItem> item) { + partition_items_.push_back(item.release()); +} + +WebviewHandler::WebviewHandler() { +} + +WebviewHandler::~WebviewHandler() { +} + +bool WebviewHandler::Parse(Extension* extension, base::string16* error) { + scoped_ptr<WebviewInfo> info(new WebviewInfo()); + + const base::DictionaryValue* dict_value = NULL; + if (!extension->manifest()->GetDictionary(keys::kWebview, + &dict_value)) { + *error = base::ASCIIToUTF16(errors::kInvalidWebview); + return false; + } + + const base::ListValue* partition_list = NULL; + if (!dict_value->GetList(keys::kWebviewPartitions, &partition_list)) { + *error = base::ASCIIToUTF16(errors::kInvalidWebviewPartitionsList); + return false; + } + + // The partition list must have at least one entry. + if (partition_list->GetSize() == 0) { + *error = base::ASCIIToUTF16(errors::kInvalidWebviewPartitionsList); + return false; + } + + for (size_t i = 0; i < partition_list->GetSize(); ++i) { + const base::DictionaryValue* partition = NULL; + if (!partition_list->GetDictionary(i, &partition)) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidWebviewPartition, base::IntToString(i)); + return false; + } + + std::string partition_pattern; + if (!partition->GetString(keys::kWebviewName, &partition_pattern)) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidWebviewPartitionName, base::IntToString(i)); + return false; + } + + const base::ListValue* url_list = NULL; + if (!partition->GetList(keys::kWebviewAccessibleResources, + &url_list)) { + *error = base::ASCIIToUTF16( + errors::kInvalidWebviewAccessibleResourcesList); + return false; + } + + // The URL list should have at least one entry. + if (url_list->GetSize() == 0) { + *error = base::ASCIIToUTF16( + errors::kInvalidWebviewAccessibleResourcesList); + return false; + } + + scoped_ptr<PartitionItem> partition_item( + new PartitionItem(partition_pattern)); + + for (size_t i = 0; i < url_list->GetSize(); ++i) { + std::string relative_path; + if (!url_list->GetString(i, &relative_path)) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidWebviewAccessibleResource, base::IntToString(i)); + return false; + } + URLPattern pattern(URLPattern::SCHEME_EXTENSION, + Extension::GetResourceURL(extension->url(), + relative_path).spec()); + partition_item->AddPattern(pattern); + } + info->AddPartitionItem(partition_item.Pass()); + } + + extension->SetManifestData(keys::kWebviewAccessibleResources, info.release()); + return true; +} + +const std::vector<std::string> WebviewHandler::Keys() const { + return SingleKey(keys::kWebview); +} + +} // namespace extensions diff --git a/extensions/common/manifest_handlers/webview_info.h b/extensions/common/manifest_handlers/webview_info.h new file mode 100644 index 0000000000..dcdb400c71 --- /dev/null +++ b/extensions/common/manifest_handlers/webview_info.h @@ -0,0 +1,54 @@ +// Copyright 2014 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_HANDLERS_WEBVIEW_INFO_H_ +#define EXTENSIONS_COMMON_MANIFEST_HANDLERS_WEBVIEW_INFO_H_ + +#include <string> + +#include "base/memory/scoped_vector.h" +#include "extensions/common/extension.h" +#include "extensions/common/manifest_handler.h" + +namespace extensions { + +class PartitionItem; + +// A class to hold the <webview> accessible extension resources +// that may be specified in the manifest of an extension using the +// "webview" key. +class WebviewInfo : public Extension::ManifestData { + public: + // Define out of line constructor/destructor to please Clang. + WebviewInfo(); + virtual ~WebviewInfo(); + + // Returns true if the specified resource is web accessible. + static bool IsResourceWebviewAccessible(const Extension* extension, + const std::string& partition_id, + const std::string& relative_path); + + void AddPartitionItem(scoped_ptr<PartitionItem> item); + + private: + ScopedVector<PartitionItem> partition_items_; +}; + +// Parses the "webview" manifest key. +class WebviewHandler : public ManifestHandler { + public: + WebviewHandler(); + virtual ~WebviewHandler(); + + virtual bool Parse(Extension* extension, base::string16* error) OVERRIDE; + + private: + virtual const std::vector<std::string> Keys() const OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(WebviewHandler); +}; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_MANIFEST_HANDLERS_WEBVIEW_INFO_H_ diff --git a/extensions/common/matcher/OWNERS b/extensions/common/matcher/OWNERS deleted file mode 100644 index d5f125f04e..0000000000 --- a/extensions/common/matcher/OWNERS +++ /dev/null @@ -1 +0,0 @@ -battre@chromium.org diff --git a/extensions/common/matcher/regex_set_matcher.cc b/extensions/common/matcher/regex_set_matcher.cc deleted file mode 100644 index 24d99efa3f..0000000000 --- a/extensions/common/matcher/regex_set_matcher.cc +++ /dev/null @@ -1,113 +0,0 @@ -// 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/extensions/common/matcher/regex_set_matcher.h b/extensions/common/matcher/regex_set_matcher.h deleted file mode 100644 index 906ab9ca3e..0000000000 --- a/extensions/common/matcher/regex_set_matcher.h +++ /dev/null @@ -1,82 +0,0 @@ -// 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/extensions/common/matcher/regex_set_matcher_unittest.cc b/extensions/common/matcher/regex_set_matcher_unittest.cc deleted file mode 100644 index 49d312a140..0000000000 --- a/extensions/common/matcher/regex_set_matcher_unittest.cc +++ /dev/null @@ -1,61 +0,0 @@ -// 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/extensions/common/matcher/string_pattern.cc b/extensions/common/matcher/string_pattern.cc deleted file mode 100644 index 4e5c350c45..0000000000 --- a/extensions/common/matcher/string_pattern.cc +++ /dev/null @@ -1,20 +0,0 @@ -// 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/extensions/common/matcher/string_pattern.h b/extensions/common/matcher/string_pattern.h deleted file mode 100644 index 1781b03283..0000000000 --- a/extensions/common/matcher/string_pattern.h +++ /dev/null @@ -1,42 +0,0 @@ -// 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/extensions/common/matcher/string_pattern_unittest.cc b/extensions/common/matcher/string_pattern_unittest.cc deleted file mode 100644 index 6e7e4bfbcf..0000000000 --- a/extensions/common/matcher/string_pattern_unittest.cc +++ /dev/null @@ -1,23 +0,0 @@ -// 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/extensions/common/matcher/substring_set_matcher.cc b/extensions/common/matcher/substring_set_matcher.cc deleted file mode 100644 index 91ac718b04..0000000000 --- a/extensions/common/matcher/substring_set_matcher.cc +++ /dev/null @@ -1,272 +0,0 @@ -// 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/extensions/common/matcher/substring_set_matcher.h b/extensions/common/matcher/substring_set_matcher.h deleted file mode 100644 index 610efc0fc2..0000000000 --- a/extensions/common/matcher/substring_set_matcher.h +++ /dev/null @@ -1,140 +0,0 @@ -// 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/extensions/common/matcher/substring_set_matcher_unittest.cc b/extensions/common/matcher/substring_set_matcher_unittest.cc deleted file mode 100644 index fde65bf2dc..0000000000 --- a/extensions/common/matcher/substring_set_matcher_unittest.cc +++ /dev/null @@ -1,167 +0,0 @@ -// 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/extensions/common/matcher/url_matcher.cc b/extensions/common/matcher/url_matcher.cc deleted file mode 100644 index 5a1d876cc6..0000000000 --- a/extensions/common/matcher/url_matcher.cc +++ /dev/null @@ -1,881 +0,0 @@ -// 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(®ex_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 = ®ex_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 = - base::STLSetDifference<std::vector<const StringPattern*> >( - new_patterns, registered_patterns); - - // Remove all patterns that are in registered_patterns but not in - // new_patterns. - std::vector<const StringPattern*> patterns_to_unregister = - base::STLSetDifference<std::vector<const StringPattern*> >( - registered_patterns, new_patterns); - - // 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/extensions/common/matcher/url_matcher.h b/extensions/common/matcher/url_matcher.h deleted file mode 100644 index d93a606701..0000000000 --- a/extensions/common/matcher/url_matcher.h +++ /dev/null @@ -1,355 +0,0 @@ -// 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/extensions/common/matcher/url_matcher_constants.cc b/extensions/common/matcher/url_matcher_constants.cc deleted file mode 100644 index 5b23dea09a..0000000000 --- a/extensions/common/matcher/url_matcher_constants.cc +++ /dev/null @@ -1,34 +0,0 @@ -// 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/extensions/common/matcher/url_matcher_constants.h b/extensions/common/matcher/url_matcher_constants.h deleted file mode 100644 index 0618a52157..0000000000 --- a/extensions/common/matcher/url_matcher_constants.h +++ /dev/null @@ -1,39 +0,0 @@ -// 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/extensions/common/matcher/url_matcher_factory.cc b/extensions/common/matcher/url_matcher_factory.cc deleted file mode 100644 index 6eec2e6ab1..0000000000 --- a/extensions/common/matcher/url_matcher_factory.cc +++ /dev/null @@ -1,277 +0,0 @@ -// 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/extensions/common/matcher/url_matcher_factory.h b/extensions/common/matcher/url_matcher_factory.h deleted file mode 100644 index 7c5be6832d..0000000000 --- a/extensions/common/matcher/url_matcher_factory.h +++ /dev/null @@ -1,62 +0,0 @@ -// 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/extensions/common/matcher/url_matcher_factory_unittest.cc b/extensions/common/matcher/url_matcher_factory_unittest.cc deleted file mode 100644 index 733e1b8a94..0000000000 --- a/extensions/common/matcher/url_matcher_factory_unittest.cc +++ /dev/null @@ -1,339 +0,0 @@ -// 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/extensions/common/matcher/url_matcher_helpers.cc b/extensions/common/matcher/url_matcher_helpers.cc deleted file mode 100644 index e4832cc18a..0000000000 --- a/extensions/common/matcher/url_matcher_helpers.cc +++ /dev/null @@ -1,31 +0,0 @@ -// 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/extensions/common/matcher/url_matcher_helpers.h b/extensions/common/matcher/url_matcher_helpers.h deleted file mode 100644 index 57bd2997ce..0000000000 --- a/extensions/common/matcher/url_matcher_helpers.h +++ /dev/null @@ -1,27 +0,0 @@ -// 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/extensions/common/matcher/url_matcher_unittest.cc b/extensions/common/matcher/url_matcher_unittest.cc deleted file mode 100644 index 29b7c890df..0000000000 --- a/extensions/common/matcher/url_matcher_unittest.cc +++ /dev/null @@ -1,682 +0,0 @@ -// 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/extensions/common/one_shot_event.cc b/extensions/common/one_shot_event.cc index 318acad5c6..28f91cb206 100644 --- a/extensions/common/one_shot_event.cc +++ b/extensions/common/one_shot_event.cc @@ -31,6 +31,9 @@ OneShotEvent::OneShotEvent() : signaled_(false) { // immediately move it to another thread. thread_checker_.DetachFromThread(); } +OneShotEvent::OneShotEvent(bool signaled) : signaled_(signaled) { + thread_checker_.DetachFromThread(); +} OneShotEvent::~OneShotEvent() {} void OneShotEvent::Post(const tracked_objects::Location& from_here, diff --git a/extensions/common/one_shot_event.h b/extensions/common/one_shot_event.h index 17ab5c88f7..9baa152d2c 100644 --- a/extensions/common/one_shot_event.h +++ b/extensions/common/one_shot_event.h @@ -35,6 +35,11 @@ namespace extensions { class OneShotEvent { public: OneShotEvent(); + // Use the following constructor to create an already signaled event. This is + // useful if you construct the event on a different thread from where it is + // used, in which case it is not possible to call Signal() just after + // construction. + explicit OneShotEvent(bool signaled); ~OneShotEvent(); // True if Signal has been called. This function is mostly for diff --git a/extensions/common/permissions/api_permission.cc b/extensions/common/permissions/api_permission.cc index f183ed29f8..95728fa1cf 100644 --- a/extensions/common/permissions/api_permission.cc +++ b/extensions/common/permissions/api_permission.cc @@ -37,21 +37,19 @@ class SimpleAPIPermission : public APIPermission { } virtual bool Contains(const APIPermission* rhs) const OVERRIDE { - CHECK(info() == rhs->info()); + CHECK_EQ(info(), rhs->info()); return true; } virtual bool Equal(const APIPermission* rhs) const OVERRIDE { - if (this == rhs) - return true; - CHECK(info() == rhs->info()); + if (this != rhs) + CHECK_EQ(info(), rhs->info()); return true; } - virtual bool FromValue(const base::Value* value) OVERRIDE { - if (value) - return false; - return true; + virtual bool FromValue(const base::Value* value, + std::string* /*error*/) OVERRIDE { + return (value == NULL); } virtual scoped_ptr<base::Value> ToValue() const OVERRIDE { @@ -63,17 +61,17 @@ class SimpleAPIPermission : public APIPermission { } virtual APIPermission* Diff(const APIPermission* rhs) const OVERRIDE { - CHECK(info() == rhs->info()); + CHECK_EQ(info(), rhs->info()); return NULL; } virtual APIPermission* Union(const APIPermission* rhs) const OVERRIDE { - CHECK(info() == rhs->info()); + CHECK_EQ(info(), rhs->info()); return new SimpleAPIPermission(info()); } virtual APIPermission* Intersect(const APIPermission* rhs) const OVERRIDE { - CHECK(info() == rhs->info()); + CHECK_EQ(info(), rhs->info()); return new SimpleAPIPermission(info()); } diff --git a/extensions/common/permissions/api_permission.h b/extensions/common/permissions/api_permission.h index b234af1454..aaf1d6efa4 100644 --- a/extensions/common/permissions/api_permission.h +++ b/extensions/common/permissions/api_permission.h @@ -40,14 +40,10 @@ class APIPermission { kAdView, kAlarms, kAlwaysOnTopWindows, - kAppCurrentWindowInternal, - kAppRuntime, - kAppWindow, kAudio, kAudioCapture, kAutoTestPrivate, kBackground, - kBluetooth, kBookmark, kBookmarkManagerPrivate, kBrailleDisplayPrivate, @@ -93,8 +89,10 @@ class APIPermission { kFullscreen, kGcm, kGeolocation, + kHid, kHistory, kHomepage, + kHotwordPrivate, kIdentity, kIdentityPrivate, kIdltest, @@ -126,13 +124,13 @@ class APIPermission { kProxy, kPushMessaging, kImageWriterPrivate, + kReadingListPrivate, kRtcPrivate, kSearchProvider, kSerial, kSessions, kSignedInDevices, kSocket, - kSocketsUdp, kStartupPages, kStorage, kStreamsPrivate, @@ -207,8 +205,9 @@ class APIPermission { // Returns true if |rhs| is equal to this. virtual bool Equal(const APIPermission* rhs) const = 0; - // Parses the APIPermission from |value|. Returns false if error happens. - virtual bool FromValue(const base::Value* value) = 0; + // Parses the APIPermission from |value|. Returns false if an error happens + // and optionally set |error| if |error| is not NULL. + virtual bool FromValue(const base::Value* value, std::string* error) = 0; // Stores this into a new created |value|. virtual scoped_ptr<base::Value> ToValue() const = 0; @@ -311,8 +310,7 @@ class APIPermissionInfo { } private: - // Instances should only be constructed from within a - // PermissionsInfo::Delegate. + // Instances should only be constructed from within a PermissionsProvider. friend class ChromeAPIPermissions; // Implementations of APIPermission will want to get the permission message, // but this class's implementation should be hidden from everyone else. diff --git a/extensions/common/permissions/api_permission_set.cc b/extensions/common/permissions/api_permission_set.cc index 0ed7769e08..fbbfdc028d 100644 --- a/extensions/common/permissions/api_permission_set.cc +++ b/extensions/common/permissions/api_permission_set.cc @@ -23,7 +23,7 @@ bool CreateAPIPermission( const base::Value* permission_value, APIPermissionSet::ParseSource source, APIPermissionSet* api_permissions, - string16* error, + base::string16* error, std::vector<std::string>* unhandled_permissions) { const APIPermissionInfo* permission_info = @@ -41,10 +41,19 @@ bool CreateAPIPermission( return false; } - if (!permission->FromValue(permission_value)) { + std::string error_details; + if (!permission->FromValue(permission_value, &error_details)) { if (error) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidPermission, permission_info->name()); + if (error_details.empty()) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidPermission, + permission_info->name()); + } else { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidPermissionWithDetail, + permission_info->name(), + error_details); + } return false; } LOG(WARNING) << "Parse permission failed."; @@ -66,7 +75,7 @@ bool ParseChildPermissions(const std::string& base_name, const base::Value* permission_value, APIPermissionSet::ParseSource source, APIPermissionSet* api_permissions, - string16* error, + base::string16* error, std::vector<std::string>* unhandled_permissions) { if (permission_value) { const base::ListValue* permissions; @@ -112,6 +121,7 @@ bool ParseChildPermissions(const std::string& base_name, void APIPermissionSet::insert(APIPermission::ID id) { const APIPermissionInfo* permission_info = PermissionsInfo::GetInstance()->GetByID(id); + DCHECK(permission_info); insert(permission_info->CreateAPIPermission()); } @@ -124,7 +134,7 @@ bool APIPermissionSet::ParseFromJSON( const base::ListValue* permissions, APIPermissionSet::ParseSource source, APIPermissionSet* api_permissions, - string16* error, + base::string16* error, std::vector<std::string>* unhandled_permissions) { for (size_t i = 0; i < permissions->GetSize(); ++i) { std::string permission_str; diff --git a/extensions/common/permissions/api_permission_set.h b/extensions/common/permissions/api_permission_set.h index c5dd248391..4db19f6818 100644 --- a/extensions/common/permissions/api_permission_set.h +++ b/extensions/common/permissions/api_permission_set.h @@ -54,7 +54,7 @@ class APIPermissionSet : public BaseSetOperators<APIPermissionSet> { const base::ListValue* permissions, ParseSource source, APIPermissionSet* api_permissions, - string16* error, + base::string16* error, std::vector<std::string>* unhandled_permissions); void AddImpliedPermissions(); diff --git a/extensions/common/permissions/api_permission_set_unittest.cc b/extensions/common/permissions/api_permission_set_unittest.cc index 85f8ccc87f..abeb9ab0cb 100644 --- a/extensions/common/permissions/api_permission_set_unittest.cc +++ b/extensions/common/permissions/api_permission_set_unittest.cc @@ -48,9 +48,7 @@ TEST(APIPermissionSetTest, CreateUnion) { value->Append(new base::StringValue("tcp-connect:*.example.com:80")); value->Append(new base::StringValue("udp-bind::8080")); value->Append(new base::StringValue("udp-send-to::8888")); - if (!permission->FromValue(value.get())) { - NOTREACHED(); - } + ASSERT_TRUE(permission->FromValue(value.get(), NULL)); } // Union with an empty set. @@ -83,9 +81,7 @@ TEST(APIPermissionSetTest, CreateUnion) { scoped_ptr<base::ListValue> value(new base::ListValue()); value->Append(new base::StringValue("tcp-connect:*.example.com:80")); value->Append(new base::StringValue("udp-send-to::8899")); - if (!permission->FromValue(value.get())) { - NOTREACHED(); - } + ASSERT_TRUE(permission->FromValue(value.get(), NULL)); } apis2.insert(permission); @@ -101,9 +97,7 @@ TEST(APIPermissionSetTest, CreateUnion) { value->Append(new base::StringValue("udp-bind::8080")); value->Append(new base::StringValue("udp-send-to::8888")); value->Append(new base::StringValue("udp-send-to::8899")); - if (!permission->FromValue(value.get())) { - NOTREACHED(); - } + ASSERT_TRUE(permission->FromValue(value.get(), NULL)); } // Insert a new socket permission which will replace the old one. expected_apis.insert(permission); @@ -140,9 +134,7 @@ TEST(APIPermissionSetTest, CreateIntersection) { value->Append(new base::StringValue("tcp-connect:*.example.com:80")); value->Append(new base::StringValue("udp-bind::8080")); value->Append(new base::StringValue("udp-send-to::8888")); - if (!permission->FromValue(value.get())) { - NOTREACHED(); - } + ASSERT_TRUE(permission->FromValue(value.get(), NULL)); } apis1.insert(permission); @@ -168,9 +160,7 @@ TEST(APIPermissionSetTest, CreateIntersection) { value->Append(new base::StringValue("udp-bind::8080")); value->Append(new base::StringValue("udp-send-to::8888")); value->Append(new base::StringValue("udp-send-to::8899")); - if (!permission->FromValue(value.get())) { - NOTREACHED(); - } + ASSERT_TRUE(permission->FromValue(value.get(), NULL)); } apis2.insert(permission); @@ -180,9 +170,7 @@ TEST(APIPermissionSetTest, CreateIntersection) { scoped_ptr<base::ListValue> value(new base::ListValue()); value->Append(new base::StringValue("udp-bind::8080")); value->Append(new base::StringValue("udp-send-to::8888")); - if (!permission->FromValue(value.get())) { - NOTREACHED(); - } + ASSERT_TRUE(permission->FromValue(value.get(), NULL)); } expected_apis.insert(permission); @@ -218,9 +206,7 @@ TEST(APIPermissionSetTest, CreateDifference) { value->Append(new base::StringValue("tcp-connect:*.example.com:80")); value->Append(new base::StringValue("udp-bind::8080")); value->Append(new base::StringValue("udp-send-to::8888")); - if (!permission->FromValue(value.get())) { - NOTREACHED(); - } + ASSERT_TRUE(permission->FromValue(value.get(), NULL)); } apis1.insert(permission); @@ -238,9 +224,7 @@ TEST(APIPermissionSetTest, CreateDifference) { scoped_ptr<base::ListValue> value(new base::ListValue()); value->Append(new base::StringValue("tcp-connect:*.example.com:80")); value->Append(new base::StringValue("udp-send-to::8899")); - if (!permission->FromValue(value.get())) { - NOTREACHED(); - } + ASSERT_TRUE(permission->FromValue(value.get(), NULL)); } apis2.insert(permission); @@ -250,9 +234,7 @@ TEST(APIPermissionSetTest, CreateDifference) { scoped_ptr<base::ListValue> value(new base::ListValue()); value->Append(new base::StringValue("udp-bind::8080")); value->Append(new base::StringValue("udp-send-to::8888")); - if (!permission->FromValue(value.get())) { - NOTREACHED(); - } + ASSERT_TRUE(permission->FromValue(value.get(), NULL)); } expected_apis.insert(permission); @@ -286,9 +268,7 @@ TEST(APIPermissionSetTest, IPC) { value->Append(new base::StringValue("tcp-connect:*.example.com:80")); value->Append(new base::StringValue("udp-bind::8080")); value->Append(new base::StringValue("udp-send-to::8888")); - if (!permission->FromValue(value.get())) { - NOTREACHED(); - } + ASSERT_TRUE(permission->FromValue(value.get(), NULL)); } apis.insert(permission); diff --git a/extensions/common/permissions/manifest_permission_set.cc b/extensions/common/permissions/manifest_permission_set.cc index 821159f6a2..fecb611dbe 100644 --- a/extensions/common/permissions/manifest_permission_set.cc +++ b/extensions/common/permissions/manifest_permission_set.cc @@ -24,7 +24,7 @@ bool CreateManifestPermission( const std::string& permission_name, const base::Value* permission_value, ManifestPermissionSet* manifest_permissions, - string16* error, + base::string16* error, std::vector<std::string>* unhandled_permissions) { scoped_ptr<ManifestPermission> permission( @@ -60,7 +60,7 @@ namespace extensions { bool ManifestPermissionSet::ParseFromJSON( const base::ListValue* permissions, ManifestPermissionSet* manifest_permissions, - string16* error, + base::string16* error, std::vector<std::string>* unhandled_permissions) { for (size_t i = 0; i < permissions->GetSize(); ++i) { std::string permission_name; diff --git a/extensions/common/permissions/manifest_permission_set.h b/extensions/common/permissions/manifest_permission_set.h index e9683bc7e9..4b1ffe0238 100644 --- a/extensions/common/permissions/manifest_permission_set.h +++ b/extensions/common/permissions/manifest_permission_set.h @@ -38,7 +38,7 @@ class ManifestPermissionSet : public BaseSetOperators<ManifestPermissionSet> { static bool ParseFromJSON( const base::ListValue* permissions, ManifestPermissionSet* manifest_permissions, - string16* error, + base::string16* error, std::vector<std::string>* unhandled_permissions); }; diff --git a/extensions/common/permissions/permission_message.cc b/extensions/common/permissions/permission_message.cc index 1b11a37a36..bc2008e058 100644 --- a/extensions/common/permissions/permission_message.cc +++ b/extensions/common/permissions/permission_message.cc @@ -10,13 +10,15 @@ namespace extensions { // PermissionMessage::PermissionMessage( - PermissionMessage::ID id, const string16& message) + PermissionMessage::ID id, const base::string16& message) : id_(id), message_(message) { } PermissionMessage::PermissionMessage( - PermissionMessage::ID id, const string16& message, const string16& details) + PermissionMessage::ID id, + const base::string16& message, + const base::string16& details) : id_(id), message_(message), details_(details) { diff --git a/extensions/common/permissions/permission_message.h b/extensions/common/permissions/permission_message.h index 82b516298d..9c3fe7ec1c 100644 --- a/extensions/common/permissions/permission_message.h +++ b/extensions/common/permissions/permission_message.h @@ -31,6 +31,7 @@ class PermissionMessage { kManagement, kDebugger, kDesktopCapture, + kHid, kHosts1, kHosts2, kHosts3, @@ -79,14 +80,17 @@ class PermissionMessage { kStartupPages, kMediaGalleriesAllGalleriesDelete, kScreenlockPrivate, + kOverrideBookmarksUI, kEnumBoundary, }; COMPILE_ASSERT(PermissionMessage::kNone > PermissionMessage::kUnknown, kNone_not_greater_than_kUnknown); // Creates the corresponding permission message. - PermissionMessage(ID id, const string16& message); - PermissionMessage(ID id, const string16& message, const string16& details); + PermissionMessage(ID id, const base::string16& message); + PermissionMessage(ID id, + const base::string16& message, + const base::string16& details); ~PermissionMessage(); // Gets the id of the permission message, which can be used in UMA @@ -95,12 +99,12 @@ class PermissionMessage { // Gets a localized message describing this permission. Please note that // the message will be empty for message types TYPE_NONE and TYPE_UNKNOWN. - const string16& message() const { return message_; } + const base::string16& message() const { return message_; } // Gets a localized message describing the details for this permission. Please // note that the message will be empty for message types TYPE_NONE and // TYPE_UNKNOWN. - const string16& details() const { return details_; } + const base::string16& details() const { return details_; } // Comparator to work with std::set. bool operator<(const PermissionMessage& that) const { @@ -113,8 +117,8 @@ class PermissionMessage { private: ID id_; - string16 message_; - string16 details_; + base::string16 message_; + base::string16 details_; }; typedef std::vector<PermissionMessage> PermissionMessages; diff --git a/extensions/common/permissions/permission_message_provider.h b/extensions/common/permissions/permission_message_provider.h index 7f682e3e5d..ab8b49e677 100644 --- a/extensions/common/permissions/permission_message_provider.h +++ b/extensions/common/permissions/permission_message_provider.h @@ -33,14 +33,14 @@ class PermissionMessageProvider { // Gets the localized permission messages that represent this set (represented // as strings). The set of permission messages shown varies by extension type. - virtual std::vector<string16> GetWarningMessages( + virtual std::vector<base::string16> GetWarningMessages( const PermissionSet* permissions, Manifest::Type extension_type) const = 0; // Gets the localized permission details for messages that represent this set // (represented as strings). The set of permission messages shown varies by // extension type. - virtual std::vector<string16> GetWarningMessagesDetails( + virtual std::vector<base::string16> GetWarningMessagesDetails( const PermissionSet* permissions, Manifest::Type extension_type) const = 0; diff --git a/extensions/common/permissions/permission_set.cc b/extensions/common/permissions/permission_set.cc index 8ec1cbedd0..fd32d4e83d 100644 --- a/extensions/common/permissions/permission_set.cc +++ b/extensions/common/permissions/permission_set.cc @@ -180,6 +180,7 @@ bool PermissionSet::HasAPIPermission( bool PermissionSet::HasAPIPermission(const std::string& permission_name) const { const APIPermissionInfo* permission = PermissionsInfo::GetInstance()->GetByName(permission_name); + // Ensure our PermissionsProvider is aware of this permission. CHECK(permission) << permission_name; return (permission && apis_.count(permission->id())); } diff --git a/extensions/common/permissions/permissions_data.cc b/extensions/common/permissions/permissions_data.cc index f43d1d7743..43a6004fd3 100644 --- a/extensions/common/permissions/permissions_data.cc +++ b/extensions/common/permissions/permissions_data.cc @@ -65,7 +65,7 @@ bool CanSpecifyHostPermission(const Extension* extension, const URLPattern& pattern, const APIPermissionSet& permissions) { if (!pattern.match_all_urls() && - pattern.MatchesScheme(chrome::kChromeUIScheme)) { + pattern.MatchesScheme(content::kChromeUIScheme)) { URLPatternSet chrome_scheme_hosts = ExtensionsClient::Get()-> GetPermittedChromeSchemeHosts(extension, permissions); if (chrome_scheme_hosts.ContainsPattern(pattern)) @@ -95,7 +95,7 @@ bool ParseHelper(Extension* extension, const char* key, APIPermissionSet* api_permissions, URLPatternSet* host_permissions, - string16* error) { + base::string16* error) { if (!extension->manifest()->HasKey(key)) return true; @@ -152,7 +152,7 @@ bool ParseHelper(Extension* extension, if (iter->id() == APIPermission::kExperimental) { if (!CanSpecifyExperimentalPermission(extension)) { - *error = ASCIIToUTF16(errors::kExperimentalFlagRequired); + *error = base::ASCIIToUTF16(errors::kExperimentalFlagRequired); return false; } } @@ -183,14 +183,14 @@ bool ParseHelper(Extension* extension, // to match all paths. pattern.SetPath("/*"); int valid_schemes = pattern.valid_schemes(); - if (pattern.MatchesScheme(chrome::kFileScheme) && + if (pattern.MatchesScheme(content::kFileScheme) && !PermissionsData::CanExecuteScriptEverywhere(extension)) { extension->set_wants_file_access(true); if (!(extension->creation_flags() & Extension::ALLOW_FILE_ACCESS)) valid_schemes &= ~URLPattern::SCHEME_FILE; } - if (pattern.scheme() != chrome::kChromeUIScheme && + if (pattern.scheme() != content::kChromeUIScheme && !PermissionsData::CanExecuteScriptEverywhere(extension)) { // Keep chrome:// in allowed schemes only if it's explicitly requested // or CanExecuteScriptEverywhere is true. If the @@ -429,11 +429,11 @@ PermissionMessages PermissionsData::GetPermissionMessages( } // static -std::vector<string16> PermissionsData::GetPermissionMessageStrings( +std::vector<base::string16> PermissionsData::GetPermissionMessageStrings( const Extension* extension) { base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_); if (ShouldSkipPermissionWarnings(extension)) { - return std::vector<string16>(); + return std::vector<base::string16>(); } else { return PermissionMessageProvider::Get()->GetWarningMessages( GetActivePermissions(extension), extension->GetType()); @@ -441,11 +441,11 @@ std::vector<string16> PermissionsData::GetPermissionMessageStrings( } // static -std::vector<string16> PermissionsData::GetPermissionMessageDetailsStrings( +std::vector<base::string16> PermissionsData::GetPermissionMessageDetailsStrings( const Extension* extension) { base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_); if (ShouldSkipPermissionWarnings(extension)) { - return std::vector<string16>(); + return std::vector<base::string16>(); } else { return PermissionMessageProvider::Get()->GetWarningMessagesDetails( GetActivePermissions(extension), extension->GetType()); @@ -476,7 +476,7 @@ bool PermissionsData::CanExecuteScriptOnPage(const Extension* extension, } if (!command_line->HasSwitch(switches::kExtensionsOnChromeURLs)) { - if (document_url.SchemeIs(chrome::kChromeUIScheme) && + if (document_url.SchemeIs(content::kChromeUIScheme) && !can_execute_everywhere) { if (error) *error = errors::kCannotAccessChromeUrl; @@ -537,31 +537,34 @@ bool PermissionsData::CanExecuteScriptEverywhere(const Extension* extension) { // static bool PermissionsData::CanCaptureVisiblePage(const Extension* extension, - const GURL& page_url, int tab_id, std::string* error) { + scoped_refptr<const PermissionSet> active_permissions = + GetActivePermissions(extension); + const URLPattern all_urls(URLPattern::SCHEME_ALL, + URLPattern::kAllUrlsPattern); + if (active_permissions->explicit_hosts().ContainsPattern(all_urls)) + return true; + if (tab_id >= 0) { scoped_refptr<const PermissionSet> tab_permissions = GetTabSpecificPermissions(extension, tab_id); - if (tab_permissions.get() && - tab_permissions->explicit_hosts().MatchesSecurityOrigin(page_url)) { + if (tab_permissions && + tab_permissions->HasAPIPermission(APIPermission::kTab)) { return true; } + if (error) + *error = errors::kActiveTabPermissionNotGranted; + return false; } - if (HasHostPermission(extension, page_url) || - page_url.GetOrigin() == extension->url()) { - return true; - } - - if (error) { - *error = ErrorUtils::FormatErrorMessage(errors::kCannotAccessPage, - page_url.spec()); - } + if (error) + *error = errors::kAllURLOrActiveTabNeeded; return false; } -bool PermissionsData::ParsePermissions(Extension* extension, string16* error) { +bool PermissionsData::ParsePermissions(Extension* extension, + base::string16* error) { initial_required_permissions_.reset(new InitialPermissions); if (!ParseHelper(extension, keys::kPermissions, @@ -571,18 +574,6 @@ bool PermissionsData::ParsePermissions(Extension* extension, string16* error) { return false; } - // TODO(jeremya/kalman) do this via the features system by exposing the - // app.window API to platform apps, with no dependency on any permissions. - // See http://crbug.com/120069. - if (extension->is_platform_app()) { - initial_required_permissions_->api_permissions.insert( - APIPermission::kAppCurrentWindowInternal); - initial_required_permissions_->api_permissions.insert( - APIPermission::kAppRuntime); - initial_required_permissions_->api_permissions.insert( - APIPermission::kAppWindow); - } - initial_optional_permissions_.reset(new InitialPermissions); if (!ParseHelper(extension, keys::kOptionalPermissions, diff --git a/extensions/common/permissions/permissions_data.h b/extensions/common/permissions/permissions_data.h index 72b9d247f6..ee1715df6c 100644 --- a/extensions/common/permissions/permissions_data.h +++ b/extensions/common/permissions/permissions_data.h @@ -141,13 +141,13 @@ class PermissionsData { // Returns the full list of permission messages that the given |extension| // should display at install time. The messages are returned as strings // for convenience. - static std::vector<string16> GetPermissionMessageStrings( + static std::vector<base::string16> GetPermissionMessageStrings( const Extension* extension); // Returns the full list of permission details for messages that the given // |extension| should display at install time. The messages are returned as // strings for convenience. - static std::vector<string16> GetPermissionMessageDetailsStrings( + static std::vector<base::string16> GetPermissionMessageDetailsStrings( const Extension* extension); // Returns true if the given |extension| can execute script on a page. If a @@ -174,12 +174,11 @@ class PermissionsData { // is restricted to the extension's host permissions as well as the // extension page itself. static bool CanCaptureVisiblePage(const Extension* extension, - const GURL& page_url, int tab_id, std::string* error); // Parse the permissions of a given extension in the initialization process. - bool ParsePermissions(Extension* extension, string16* error); + bool ParsePermissions(Extension* extension, base::string16* error); // Ensure manifest handlers provide their custom manifest permissions. void InitializeManifestPermissions(Extension* extension); diff --git a/extensions/common/permissions/permissions_data_unittest.cc b/extensions/common/permissions/permissions_data_unittest.cc index 3ea4856a51..fda57f9dae 100644 --- a/extensions/common/permissions/permissions_data_unittest.cc +++ b/extensions/common/permissions/permissions_data_unittest.cc @@ -24,6 +24,7 @@ #include "extensions/common/url_pattern_set.h" #include "testing/gtest/include/gtest/gtest.h" +using base::UTF16ToUTF8; using content::SocketPermissionRequest; using extension_test_util::LoadManifest; using extension_test_util::LoadManifestUnchecked; @@ -130,9 +131,11 @@ TEST(ExtensionPermissionsTest, SocketPermissions) { Manifest::INTERNAL, Extension::NO_FLAGS, &error); EXPECT_TRUE(extension.get() == NULL); - ASSERT_EQ(ErrorUtils::FormatErrorMessage( - manifest_errors::kInvalidPermission, "socket"), - error); + std::string expected_error_msg_header = ErrorUtils::FormatErrorMessage( + manifest_errors::kInvalidPermissionWithDetail, + "socket", + "NULL or empty permission list"); + EXPECT_EQ(expected_error_msg_header, error); extension = LoadManifest("socket_permissions", "socket2.json"); EXPECT_TRUE(CheckSocketPermission(extension, @@ -153,7 +156,7 @@ TEST(ExtensionPermissionsTest, SocketPermissions) { TEST(ExtensionPermissionsTest, GetPermissionMessages_ManyAPIPermissions) { scoped_refptr<Extension> extension; extension = LoadManifest("permissions", "many-apis.json"); - std::vector<string16> warnings = + std::vector<base::string16> warnings = PermissionsData::GetPermissionMessageStrings(extension.get()); ASSERT_EQ(6u, warnings.size()); EXPECT_EQ("Access your data on api.flickr.com", @@ -169,9 +172,9 @@ TEST(ExtensionPermissionsTest, GetPermissionMessages_ManyAPIPermissions) { TEST(ExtensionPermissionsTest, GetPermissionMessages_ManyHostsPermissions) { scoped_refptr<Extension> extension; extension = LoadManifest("permissions", "more-than-3-hosts.json"); - std::vector<string16> warnings = + std::vector<base::string16> warnings = PermissionsData::GetPermissionMessageStrings(extension.get()); - std::vector<string16> warnings_details = + std::vector<base::string16> warnings_details = PermissionsData::GetPermissionMessageDetailsStrings(extension.get()); ASSERT_EQ(1u, warnings.size()); ASSERT_EQ(1u, warnings_details.size()); @@ -186,7 +189,7 @@ TEST(ExtensionPermissionsTest, GetPermissionMessages_LocationApiPermission) { "location-api.json", Manifest::COMPONENT, Extension::NO_FLAGS); - std::vector<string16> warnings = + std::vector<base::string16> warnings = PermissionsData::GetPermissionMessageStrings(extension.get()); ASSERT_EQ(1u, warnings.size()); EXPECT_EQ("Detect your physical location", UTF16ToUTF8(warnings[0])); @@ -195,7 +198,7 @@ TEST(ExtensionPermissionsTest, GetPermissionMessages_LocationApiPermission) { TEST(ExtensionPermissionsTest, GetPermissionMessages_ManyHosts) { scoped_refptr<Extension> extension; extension = LoadManifest("permissions", "many-hosts.json"); - std::vector<string16> warnings = + std::vector<base::string16> warnings = PermissionsData::GetPermissionMessageStrings(extension.get()); ASSERT_EQ(1u, warnings.size()); EXPECT_EQ("Access your data on encrypted.google.com and www.google.com", @@ -205,7 +208,7 @@ TEST(ExtensionPermissionsTest, GetPermissionMessages_ManyHosts) { TEST(ExtensionPermissionsTest, GetPermissionMessages_Plugins) { scoped_refptr<Extension> extension; extension = LoadManifest("permissions", "plugins.json"); - std::vector<string16> warnings = + std::vector<base::string16> warnings = PermissionsData::GetPermissionMessageStrings(extension.get()); // We don't parse the plugins key on Chrome OS, so it should not ask for any // permissions. @@ -247,8 +250,13 @@ class ExtensionScriptAndCaptureVisibleTest : public testing::Test { bool AllowedScript(const Extension* extension, const GURL& url, const GURL& top_url) { + return AllowedScript(extension, url, top_url, -1); + } + + bool AllowedScript(const Extension* extension, const GURL& url, + const GURL& top_url, int tab_id) { return PermissionsData::CanExecuteScriptOnPage( - extension, url, top_url, -1, NULL, -1, NULL); + extension, url, top_url, tab_id, NULL, -1, NULL); } bool BlockedScript(const Extension* extension, const GURL& url, @@ -265,7 +273,7 @@ class ExtensionScriptAndCaptureVisibleTest : public testing::Test { return (PermissionsData::CanExecuteScriptOnPage( extension, url, url, tab_id, NULL, -1, NULL) && PermissionsData::CanCaptureVisiblePage( - extension, url, tab_id, NULL)); + extension, tab_id, NULL)); } bool CaptureOnly(const Extension* extension, const GURL& url) { @@ -275,7 +283,18 @@ class ExtensionScriptAndCaptureVisibleTest : public testing::Test { bool CaptureOnly(const Extension* extension, const GURL& url, int tab_id) { return !PermissionsData::CanExecuteScriptOnPage( extension, url, url, tab_id, NULL, -1, NULL) && - PermissionsData::CanCaptureVisiblePage(extension, url, tab_id, NULL); + PermissionsData::CanCaptureVisiblePage(extension, tab_id, NULL); + } + + bool ScriptOnly(const Extension* extension, const GURL& url, + const GURL& top_url) { + return ScriptOnly(extension, url, top_url, -1); + } + + bool ScriptOnly(const Extension* extension, const GURL& url, + const GURL& top_url, int tab_id) { + return AllowedScript(extension, url, top_url, tab_id) && + !PermissionsData::CanCaptureVisiblePage(extension, tab_id, NULL); } bool Blocked(const Extension* extension, const GURL& url) { @@ -286,10 +305,10 @@ class ExtensionScriptAndCaptureVisibleTest : public testing::Test { return !(PermissionsData::CanExecuteScriptOnPage( extension, url, url, tab_id, NULL, -1, NULL) || PermissionsData::CanCaptureVisiblePage( - extension, url, tab_id, NULL)); + extension, tab_id, NULL)); } - bool AllowedExclusivelyOnTab( + bool ScriptAllowedExclusivelyOnTab( const Extension* extension, const std::set<GURL>& allowed_urls, int tab_id) { @@ -297,7 +316,7 @@ class ExtensionScriptAndCaptureVisibleTest : public testing::Test { for (std::set<GURL>::iterator it = urls_.begin(); it != urls_.end(); ++it) { const GURL& url = *it; if (allowed_urls.count(url)) - result &= Allowed(extension, url, tab_id); + result &= AllowedScript(extension, url, url, tab_id); else result &= Blocked(extension, url, tab_id); } @@ -332,11 +351,11 @@ TEST_F(ExtensionScriptAndCaptureVisibleTest, Permissions) { EXPECT_TRUE(Allowed(extension.get(), http_url)); EXPECT_TRUE(Allowed(extension.get(), https_url)); - EXPECT_TRUE(Blocked(extension.get(), file_url)); - EXPECT_TRUE(Blocked(extension.get(), settings_url)); + EXPECT_TRUE(CaptureOnly(extension.get(), file_url)); + EXPECT_TRUE(CaptureOnly(extension.get(), settings_url)); EXPECT_TRUE(CaptureOnly(extension.get(), favicon_url)); - EXPECT_TRUE(Blocked(extension.get(), about_url)); - EXPECT_TRUE(Blocked(extension.get(), extension_url)); + EXPECT_TRUE(CaptureOnly(extension.get(), about_url)); + EXPECT_TRUE(CaptureOnly(extension.get(), extension_url)); // Test access to iframed content. GURL within_extension_url = extension->GetResourceURL("page.html"); @@ -355,8 +374,8 @@ TEST_F(ExtensionScriptAndCaptureVisibleTest, Permissions) { // Test * for scheme, which implies just the http/https schemes. extension = LoadManifestStrict("script_and_capture", "extension_wildcard.json"); - EXPECT_TRUE(Allowed(extension.get(), http_url)); - EXPECT_TRUE(Allowed(extension.get(), https_url)); + EXPECT_TRUE(ScriptOnly(extension.get(), http_url, http_url)); + EXPECT_TRUE(ScriptOnly(extension.get(), https_url, https_url)); EXPECT_TRUE(Blocked(extension.get(), settings_url)); EXPECT_TRUE(Blocked(extension.get(), about_url)); EXPECT_TRUE(Blocked(extension.get(), file_url)); @@ -386,7 +405,7 @@ TEST_F(ExtensionScriptAndCaptureVisibleTest, Permissions) { extension = LoadManifestStrict("script_and_capture", "extension_chrome_favicon_wildcard.json"); EXPECT_TRUE(Blocked(extension.get(), settings_url)); - EXPECT_TRUE(CaptureOnly(extension.get(), favicon_url)); + EXPECT_TRUE(Blocked(extension.get(), favicon_url)); EXPECT_TRUE(Blocked(extension.get(), about_url)); EXPECT_TRUE(PermissionsData::HasHostPermission(extension.get(), favicon_url)); @@ -410,7 +429,7 @@ TEST_F(ExtensionScriptAndCaptureVisibleTest, Permissions) { extension = LoadManifest("script_and_capture", "extension_component_google.json", Manifest::COMPONENT, Extension::NO_FLAGS); - EXPECT_TRUE(Allowed(extension.get(), http_url)); + EXPECT_TRUE(ScriptOnly(extension.get(), http_url, http_url)); EXPECT_TRUE(Blocked(extension.get(), https_url)); EXPECT_TRUE(Blocked(extension.get(), file_url)); EXPECT_TRUE(Blocked(extension.get(), settings_url)); @@ -432,11 +451,11 @@ TEST_F(ExtensionScriptAndCaptureVisibleTest, PermissionsWithChromeURLsEnabled) { "extension_regular_all.json"); EXPECT_TRUE(Allowed(extension.get(), http_url)); EXPECT_TRUE(Allowed(extension.get(), https_url)); - EXPECT_TRUE(Blocked(extension.get(), file_url)); - EXPECT_TRUE(Blocked(extension.get(), settings_url)); + EXPECT_TRUE(CaptureOnly(extension.get(), file_url)); + EXPECT_TRUE(CaptureOnly(extension.get(), settings_url)); EXPECT_TRUE(Allowed(extension.get(), favicon_url)); // chrome:// requested - EXPECT_TRUE(Blocked(extension.get(), about_url)); - EXPECT_TRUE(Blocked(extension.get(), extension_url)); + EXPECT_TRUE(CaptureOnly(extension.get(), about_url)); + EXPECT_TRUE(CaptureOnly(extension.get(), extension_url)); // Test access to iframed content. GURL within_extension_url = extension->GetResourceURL("page.html"); @@ -455,8 +474,8 @@ TEST_F(ExtensionScriptAndCaptureVisibleTest, PermissionsWithChromeURLsEnabled) { // Test * for scheme, which implies just the http/https schemes. extension = LoadManifestStrict("script_and_capture", "extension_wildcard.json"); - EXPECT_TRUE(Allowed(extension.get(), http_url)); - EXPECT_TRUE(Allowed(extension.get(), https_url)); + EXPECT_TRUE(ScriptOnly(extension.get(), http_url, http_url)); + EXPECT_TRUE(ScriptOnly(extension.get(), https_url, https_url)); EXPECT_TRUE(Blocked(extension.get(), settings_url)); EXPECT_TRUE(Blocked(extension.get(), about_url)); EXPECT_TRUE(Blocked(extension.get(), file_url)); @@ -475,16 +494,16 @@ TEST_F(ExtensionScriptAndCaptureVisibleTest, PermissionsWithChromeURLsEnabled) { EXPECT_FALSE(extension.get() == NULL); EXPECT_TRUE(Blocked(extension.get(), http_url)); EXPECT_TRUE(Blocked(extension.get(), https_url)); - EXPECT_TRUE(Allowed(extension.get(), settings_url)); + EXPECT_TRUE(ScriptOnly(extension.get(), settings_url, settings_url)); EXPECT_TRUE(Blocked(extension.get(), about_url)); EXPECT_TRUE(Blocked(extension.get(), file_url)); - EXPECT_TRUE(Allowed(extension.get(), favicon_url)); // chrome:// requested + EXPECT_TRUE(ScriptOnly(extension.get(), favicon_url, favicon_url)); // Having chrome://favicon/* should not give you chrome://* extension = LoadManifestStrict("script_and_capture", "extension_chrome_favicon_wildcard.json"); EXPECT_TRUE(Blocked(extension.get(), settings_url)); - EXPECT_TRUE(Allowed(extension.get(), favicon_url)); // chrome:// requested + EXPECT_TRUE(ScriptOnly(extension.get(), favicon_url, favicon_url)); EXPECT_TRUE(Blocked(extension.get(), about_url)); EXPECT_TRUE(PermissionsData::HasHostPermission(extension.get(), favicon_url)); @@ -508,7 +527,7 @@ TEST_F(ExtensionScriptAndCaptureVisibleTest, PermissionsWithChromeURLsEnabled) { extension = LoadManifest("script_and_capture", "extension_component_google.json", Manifest::COMPONENT, Extension::NO_FLAGS); - EXPECT_TRUE(Allowed(extension.get(), http_url)); + EXPECT_TRUE(ScriptOnly(extension.get(), http_url, http_url)); EXPECT_TRUE(Blocked(extension.get(), https_url)); EXPECT_TRUE(Blocked(extension.get(), file_url)); EXPECT_TRUE(Blocked(extension.get(), settings_url)); @@ -532,9 +551,9 @@ TEST_F(ExtensionScriptAndCaptureVisibleTest, TabSpecific) { std::set<GURL> no_urls; - EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 0)); - EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 1)); - EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 2)); + EXPECT_TRUE(ScriptAllowedExclusivelyOnTab(extension.get(), no_urls, 0)); + EXPECT_TRUE(ScriptAllowedExclusivelyOnTab(extension.get(), no_urls, 1)); + EXPECT_TRUE(ScriptAllowedExclusivelyOnTab(extension.get(), no_urls, 2)); URLPatternSet allowed_hosts; allowed_hosts.AddPattern(URLPattern(URLPattern::SCHEME_ALL, @@ -557,17 +576,17 @@ TEST_F(ExtensionScriptAndCaptureVisibleTest, TabSpecific) { ->explicit_hosts()); } - EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), allowed_urls, 0)); - EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 1)); - EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 2)); + EXPECT_TRUE(ScriptAllowedExclusivelyOnTab(extension.get(), allowed_urls, 0)); + EXPECT_TRUE(ScriptAllowedExclusivelyOnTab(extension.get(), no_urls, 1)); + EXPECT_TRUE(ScriptAllowedExclusivelyOnTab(extension.get(), no_urls, 2)); PermissionsData::ClearTabSpecificPermissions(extension.get(), 0); EXPECT_FALSE(PermissionsData::GetTabSpecificPermissions(extension.get(), 0) .get()); - EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 0)); - EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 1)); - EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 2)); + EXPECT_TRUE(ScriptAllowedExclusivelyOnTab(extension.get(), no_urls, 0)); + EXPECT_TRUE(ScriptAllowedExclusivelyOnTab(extension.get(), no_urls, 1)); + EXPECT_TRUE(ScriptAllowedExclusivelyOnTab(extension.get(), no_urls, 2)); std::set<GURL> more_allowed_urls = allowed_urls; more_allowed_urls.insert(https_url); @@ -596,25 +615,27 @@ TEST_F(ExtensionScriptAndCaptureVisibleTest, TabSpecific) { ->explicit_hosts()); } - EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), allowed_urls, 0)); - EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), more_allowed_urls, 1)); - EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 2)); + EXPECT_TRUE(ScriptAllowedExclusivelyOnTab(extension.get(), allowed_urls, 0)); + EXPECT_TRUE( + ScriptAllowedExclusivelyOnTab(extension.get(), more_allowed_urls, 1)); + EXPECT_TRUE(ScriptAllowedExclusivelyOnTab(extension.get(), no_urls, 2)); PermissionsData::ClearTabSpecificPermissions(extension.get(), 0); EXPECT_FALSE(PermissionsData::GetTabSpecificPermissions(extension.get(), 0) .get()); - EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 0)); - EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), more_allowed_urls, 1)); - EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 2)); + EXPECT_TRUE(ScriptAllowedExclusivelyOnTab(extension.get(), no_urls, 0)); + EXPECT_TRUE( + ScriptAllowedExclusivelyOnTab(extension.get(), more_allowed_urls, 1)); + EXPECT_TRUE(ScriptAllowedExclusivelyOnTab(extension.get(), no_urls, 2)); PermissionsData::ClearTabSpecificPermissions(extension.get(), 1); EXPECT_FALSE(PermissionsData::GetTabSpecificPermissions(extension.get(), 1) .get()); - EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 0)); - EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 1)); - EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 2)); + EXPECT_TRUE(ScriptAllowedExclusivelyOnTab(extension.get(), no_urls, 0)); + EXPECT_TRUE(ScriptAllowedExclusivelyOnTab(extension.get(), no_urls, 1)); + EXPECT_TRUE(ScriptAllowedExclusivelyOnTab(extension.get(), no_urls, 2)); } } // namespace extensions diff --git a/extensions/common/permissions/permissions_info.cc b/extensions/common/permissions/permissions_info.cc index bac3d9c18e..e4684aa522 100644 --- a/extensions/common/permissions/permissions_info.cc +++ b/extensions/common/permissions/permissions_info.cc @@ -64,7 +64,6 @@ bool PermissionsInfo::HasChildPermissions(const std::string& name) const { PermissionsInfo::PermissionsInfo() : hosted_app_permission_count_(0), permission_count_(0) { - DCHECK(ExtensionsClient::Get()); InitializeWithProvider(ExtensionsClient::Get()->GetPermissionsProvider()); } diff --git a/extensions/common/stack_frame_unittest.cc b/extensions/common/stack_frame_unittest.cc index 7dad047309..ddd92aec59 100644 --- a/extensions/common/stack_frame_unittest.cc +++ b/extensions/common/stack_frame_unittest.cc @@ -21,18 +21,18 @@ void AssertStackFrameValid(const std::string& text, size_t column, const std::string& source, const std::string& function) { - base::string16 utf16_text = UTF8ToUTF16(text); + base::string16 utf16_text = base::UTF8ToUTF16(text); scoped_ptr<StackFrame> frame = StackFrame::CreateFromText(utf16_text); ASSERT_TRUE(frame.get()) << "Failed to create frame from '" << text << "'"; EXPECT_EQ(line, frame->line_number()); EXPECT_EQ(column, frame->column_number()); - EXPECT_EQ(UTF8ToUTF16(source), frame->source()); - EXPECT_EQ(UTF8ToUTF16(function), frame->function()); + EXPECT_EQ(base::UTF8ToUTF16(source), frame->source()); + EXPECT_EQ(base::UTF8ToUTF16(function), frame->function()); } void AssertStackFrameInvalid(const std::string& text) { - base::string16 utf16_text = UTF8ToUTF16(text); + base::string16 utf16_text = base::UTF8ToUTF16(text); scoped_ptr<StackFrame> frame = StackFrame::CreateFromText(utf16_text); ASSERT_FALSE(frame.get()) << "Errantly created frame from '" << text << "'"; } diff --git a/extensions/common/switches.cc b/extensions/common/switches.cc index 5d50344671..9a1a91956b 100644 --- a/extensions/common/switches.cc +++ b/extensions/common/switches.cc @@ -9,7 +9,7 @@ namespace extensions { namespace switches { // Allows non-https URL for background_page for hosted apps. -const char kAllowHTTPBackgroundPage[] = "allow-http-background-page"; +const char kAllowHTTPBackgroundPage[] = "allow-http-background-page"; // Allows the browser to load extensions that lack a modern manifest when that // would otherwise be forbidden. @@ -30,23 +30,32 @@ const char kEasyOffStoreExtensionInstall[] = "easy-off-store-extension-install"; const char kEnableExperimentalExtensionApis[] = "enable-experimental-extension-apis"; +// Enables extensions to hide bookmarks UI elements. +const char kEnableOverrideBookmarksUI[] = "enable-override-bookmarks-ui"; + // Allows the ErrorConsole to collect runtime and manifest errors, and display // them in the chrome:extensions page. const char kErrorConsole[] = "error-console"; // The time in milliseconds that an extension event page can be idle before it // is shut down. -const char kEventPageIdleTime[] = "event-page-idle-time"; +const char kEventPageIdleTime[] = "event-page-idle-time"; // The time in milliseconds that an extension event page has between being // notified of its impending unload and that unload happening. -const char kEventPageSuspendingTime[] = "event-page-unloading-time"; +const char kEventPageSuspendingTime[] = "event-page-unloading-time"; + +// Marks a renderer as extension process. +const char kExtensionProcess[] = "extension-process"; // 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"; +// Whether to force developer mode extensions highlighting. +const char kForceDevModeHighlighting[] = "force-dev-mode-highlighting"; + // Enables setting global commands through the Extensions Commands API. const char kGlobalCommands[] = "global-commands"; @@ -54,13 +63,6 @@ const char kGlobalCommands[] = "global-commands"; // Default is yes. const char kPromptForExternalExtensions[] = "prompt-for-external-extensions"; -// Enables or disables extension scripts badges in the location bar. -const char kScriptBadges[] = "script-badges"; - -// Enable or diable the "script bubble" icon in the URL bar that tells you how -// many extensions are running scripts on a page. -const char kScriptBubble[] = "script-bubble"; - // Makes component extensions appear in chrome://settings/extensions. const char kShowComponentExtensionOptions[] = "show-component-extension-options"; diff --git a/extensions/common/switches.h b/extensions/common/switches.h index 75f384366f..6f08870b54 100644 --- a/extensions/common/switches.h +++ b/extensions/common/switches.h @@ -16,14 +16,15 @@ extern const char kAllowLegacyExtensionManifests[]; extern const char kAllowScriptingGallery[]; extern const char kEasyOffStoreExtensionInstall[]; extern const char kEnableExperimentalExtensionApis[]; +extern const char kEnableOverrideBookmarksUI[]; extern const char kErrorConsole[]; extern const char kEventPageIdleTime[]; extern const char kEventPageSuspendingTime[]; +extern const char kExtensionProcess[]; extern const char kExtensionsOnChromeURLs[]; +extern const char kForceDevModeHighlighting[]; extern const char kGlobalCommands[]; extern const char kPromptForExternalExtensions[]; -extern const char kScriptBadges[]; -extern const char kScriptBubble[]; extern const char kShowComponentExtensionOptions[]; } // namespace switches diff --git a/extensions/common/test_util.cc b/extensions/common/test_util.cc new file mode 100644 index 0000000000..4d3164ffe1 --- /dev/null +++ b/extensions/common/test_util.cc @@ -0,0 +1,31 @@ +// Copyright 2014 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/test_util.h" + +#include "extensions/common/extension.h" +#include "extensions/common/extension_builder.h" +#include "extensions/common/value_builder.h" + +namespace extensions { +namespace test_util { + +ExtensionBuilder& BuildExtension(ExtensionBuilder& builder) { + return builder + .SetManifest(DictionaryBuilder() + .Set("name", "Test extension") + .Set("version", "1.0") + .Set("manifest_version", 2)); +} + +scoped_refptr<Extension> CreateExtensionWithID(const std::string& id) { + return ExtensionBuilder() + .SetManifest( + DictionaryBuilder().Set("name", "test").Set("version", "0.1")) + .SetID(id) + .Build(); +} + +} // namespace test_util +} // namespace extensions diff --git a/extensions/common/test_util.h b/extensions/common/test_util.h new file mode 100644 index 0000000000..e38b28fda4 --- /dev/null +++ b/extensions/common/test_util.h @@ -0,0 +1,27 @@ +// Copyright 2014 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_TEST_UTIL_H_ +#define EXTENSIONS_COMMON_TEST_UTIL_H_ + +#include <string> + +#include "base/memory/ref_counted.h" + +namespace extensions { +class Extension; +class ExtensionBuilder; + +namespace test_util { + +// Adds an extension manifest to a builder. +ExtensionBuilder& BuildExtension(ExtensionBuilder& builder); + +// Return a very simple extension with a given |id|. +scoped_refptr<Extension> CreateExtensionWithID(const std::string& id); + +} // namespace test_util +} // namespace extensions + +#endif // EXTENSIONS_COMMON_TEST_UTIL_H_ diff --git a/extensions/common/url_pattern.cc b/extensions/common/url_pattern.cc index 5326f431ba..477d3ddf08 100644 --- a/extensions/common/url_pattern.cc +++ b/extensions/common/url_pattern.cc @@ -22,11 +22,11 @@ namespace { const char* kValidSchemes[] = { content::kHttpScheme, content::kHttpsScheme, - chrome::kFileScheme, + content::kFileScheme, content::kFtpScheme, - chrome::kChromeUIScheme, + content::kChromeUIScheme, extensions::kExtensionScheme, - chrome::kFileSystemScheme, + content::kFileSystemScheme, }; const int kValidSchemeMasks[] = { @@ -128,8 +128,9 @@ URLPattern::URLPattern(int valid_schemes, const std::string& pattern) match_all_urls_(false), match_subdomains_(false), port_("*") { - if (PARSE_SUCCESS != Parse(pattern)) - NOTREACHED() << "URLPattern is invalid: " << pattern; + ParseResult result = Parse(pattern); + if (PARSE_SUCCESS != result) + NOTREACHED() << "URLPattern invalid: " << pattern << " result " << result; } URLPattern::~URLPattern() { @@ -191,7 +192,7 @@ URLPattern::ParseResult URLPattern::Parse(const std::string& pattern) { if (!standard_scheme) { path_start_pos = host_start_pos; - } else if (scheme_ == chrome::kFileScheme) { + } else if (scheme_ == content::kFileScheme) { size_t host_end_pos = pattern.find(kPathSeparator, host_start_pos); if (host_end_pos == std::string::npos) { // Allow hostname omission. @@ -425,7 +426,7 @@ const std::string& URLPattern::GetAsString() const { std::string spec = scheme_ + (standard_scheme ? content::kStandardSchemeSeparator : ":"); - if (scheme_ != chrome::kFileScheme && standard_scheme) { + if (scheme_ != content::kFileScheme && standard_scheme) { if (match_subdomains_) { spec += "*"; if (!host_.empty()) @@ -492,7 +493,7 @@ bool URLPattern::MatchesAllSchemes( bool URLPattern::MatchesSecurityOriginHelper(const GURL& test) const { // Ignore hostname if scheme is file://. - if (scheme_ != chrome::kFileScheme && !MatchesHost(test)) + if (scheme_ != content::kFileScheme && !MatchesHost(test)) return false; if (!MatchesPortPattern(base::IntToString(test.EffectiveIntPort()))) diff --git a/extensions/common/url_pattern_set.cc b/extensions/common/url_pattern_set.cc index db5b4eb7c4..118f9ee31e 100644 --- a/extensions/common/url_pattern_set.cc +++ b/extensions/common/url_pattern_set.cc @@ -183,10 +183,10 @@ bool URLPatternSet::OverlapsWith(const URLPatternSet& other) const { } scoped_ptr<base::ListValue> URLPatternSet::ToValue() const { - scoped_ptr<ListValue> value(new ListValue); + scoped_ptr<base::ListValue> value(new base::ListValue); for (URLPatternSet::const_iterator i = patterns_.begin(); i != patterns_.end(); ++i) - value->AppendIfNotPresent(Value::CreateStringValue(i->GetAsString())); + value->AppendIfNotPresent(new base::StringValue(i->GetAsString())); return value.Pass(); } @@ -206,7 +206,7 @@ bool URLPatternSet::Populate(const std::vector<std::string>& patterns, } return false; } - if (!allow_file_access && pattern.MatchesScheme(chrome::kFileScheme)) { + if (!allow_file_access && pattern.MatchesScheme(content::kFileScheme)) { pattern.SetValidSchemes( pattern.valid_schemes() & ~URLPattern::SCHEME_FILE); } diff --git a/extensions/common/value_builder.cc b/extensions/common/value_builder.cc index c32679e326..0b25745432 100644 --- a/extensions/common/value_builder.cc +++ b/extensions/common/value_builder.cc @@ -34,7 +34,7 @@ DictionaryBuilder& DictionaryBuilder::Set(const std::string& path, } DictionaryBuilder& DictionaryBuilder::Set(const std::string& path, - const string16& in_value) { + const base::string16& in_value) { dict_->SetWithoutPathExpansion(path, new base::StringValue(in_value)); return *this; } @@ -79,7 +79,7 @@ ListBuilder& ListBuilder::Append(const std::string& in_value) { return *this; } -ListBuilder& ListBuilder::Append(const string16& in_value) { +ListBuilder& ListBuilder::Append(const base::string16& in_value) { list_->Append(new base::StringValue(in_value)); return *this; } diff --git a/extensions/common/value_builder.h b/extensions/common/value_builder.h index 77b0d73dfc..bcb40aa51a 100644 --- a/extensions/common/value_builder.h +++ b/extensions/common/value_builder.h @@ -59,7 +59,8 @@ class DictionaryBuilder { DictionaryBuilder& Set(const std::string& path, int in_value); DictionaryBuilder& Set(const std::string& path, double in_value); DictionaryBuilder& Set(const std::string& path, const std::string& in_value); - DictionaryBuilder& Set(const std::string& path, const string16& in_value); + DictionaryBuilder& Set(const std::string& path, + const base::string16& in_value); DictionaryBuilder& Set(const std::string& path, DictionaryBuilder& in_value); DictionaryBuilder& Set(const std::string& path, ListBuilder& in_value); @@ -87,7 +88,7 @@ class ListBuilder { ListBuilder& Append(int in_value); ListBuilder& Append(double in_value); ListBuilder& Append(const std::string& in_value); - ListBuilder& Append(const string16& in_value); + ListBuilder& Append(const base::string16& in_value); ListBuilder& Append(DictionaryBuilder& in_value); ListBuilder& Append(ListBuilder& in_value); diff --git a/extensions/common/view_type.h b/extensions/common/view_type.h index 6030e6ca92..3815da0884 100644 --- a/extensions/common/view_type.h +++ b/extensions/common/view_type.h @@ -14,6 +14,8 @@ namespace extensions { // data. enum ViewType { VIEW_TYPE_INVALID, + // TODO(jamescook): Rename this to VIEW_TYPE_APP_WINDOW after figuring out + // if the strings can be changed. http://crbug.com/344067 VIEW_TYPE_APP_SHELL, VIEW_TYPE_BACKGROUND_CONTENTS, VIEW_TYPE_EXTENSION_BACKGROUND_PAGE, @@ -26,6 +28,7 @@ enum ViewType { VIEW_TYPE_PANEL, VIEW_TYPE_TAB_CONTENTS, VIEW_TYPE_VIRTUAL_KEYBOARD, + VIEW_TYPE_LAST = VIEW_TYPE_VIRTUAL_KEYBOARD }; // Constant strings corresponding to the Type enumeration values. Used diff --git a/extensions/extensions.gyp b/extensions/extensions.gyp index 96a0075357..1045e535d1 100644 --- a/extensions/extensions.gyp +++ b/extensions/extensions.gyp @@ -11,19 +11,24 @@ 'target_name': 'extensions_common', 'type': 'static_library', 'dependencies': [ + '../third_party/re2/re2.gyp:re2', # TODO(benwells): figure out what to do with the api target and # api resources compiled into the chrome resource bundle. # http://crbug.com/162530 '../chrome/chrome_resources.gyp:chrome_resources', + # TODO(jamescook|derat): Pull strings into extensions module. + '../chrome/chrome_resources.gyp:chrome_strings', '../chrome/common/extensions/api/api.gyp:api', + '../components/components.gyp:url_matcher', '../content/content.gyp:content_common', - '../third_party/re2/re2.gyp:re2', ], 'include_dirs': [ '..', '<(INTERMEDIATE_DIR)', ], 'sources': [ + 'common/common_manifest_handlers.cc', + 'common/common_manifest_handlers.h', 'common/crx_file.cc', 'common/crx_file.h', 'common/csp_validator.cc', @@ -47,6 +52,8 @@ 'common/extension_paths.h', 'common/extension_resource.cc', 'common/extension_resource.h', + 'common/extension_set.cc', + 'common/extension_set.h', 'common/extension_urls.cc', 'common/extension_urls.h', 'common/extensions_client.cc', @@ -85,20 +92,10 @@ 'common/manifest_handlers/sandboxed_page_info.h', 'common/manifest_handlers/shared_module_info.cc', 'common/manifest_handlers/shared_module_info.h', - 'common/matcher/regex_set_matcher.cc', - 'common/matcher/regex_set_matcher.h', - 'common/matcher/string_pattern.cc', - 'common/matcher/string_pattern.h', - 'common/matcher/substring_set_matcher.cc', - 'common/matcher/substring_set_matcher.h', - 'common/matcher/url_matcher.cc', - 'common/matcher/url_matcher.h', - 'common/matcher/url_matcher_constants.cc', - 'common/matcher/url_matcher_constants.h', - 'common/matcher/url_matcher_factory.cc', - 'common/matcher/url_matcher_factory.h', - 'common/matcher/url_matcher_helpers.cc', - 'common/matcher/url_matcher_helpers.h', + 'common/manifest_handlers/web_accessible_resources_info.cc', + 'common/manifest_handlers/web_accessible_resources_info.h', + 'common/manifest_handlers/webview_info.cc', + 'common/manifest_handlers/webview_info.h', 'common/one_shot_event.cc', 'common/one_shot_event.h', 'common/permissions/api_permission.cc', @@ -170,16 +167,42 @@ 'sources': [ 'browser/admin_policy.cc', 'browser/admin_policy.h', + 'browser/api_activity_monitor.h', 'browser/app_sorting.h', + 'browser/blacklist_state.h', + 'browser/error_map.cc', + 'browser/error_map.h', 'browser/event_listener_map.cc', 'browser/event_listener_map.h', 'browser/event_router.cc', 'browser/event_router.h', - 'browser/extension_prefs_scope.h', 'browser/extension_error.cc', 'browser/extension_error.h', 'browser/extension_function.cc', 'browser/extension_function.h', + 'browser/extension_message_filter.cc', + 'browser/extension_message_filter.h', + 'browser/extension_pref_store.cc', + 'browser/extension_pref_store.h', + 'browser/extension_pref_value_map.cc', + 'browser/extension_pref_value_map_factory.cc', + 'browser/extension_pref_value_map_factory.h', + 'browser/extension_pref_value_map.h', + 'browser/extension_prefs.cc', + 'browser/extension_prefs.h', + 'browser/extension_prefs_factory.cc', + 'browser/extension_prefs_factory.h', + 'browser/extension_prefs_scope.h', + 'browser/extension_registry.cc', + 'browser/extension_registry.h', + 'browser/extension_registry_factory.cc', + 'browser/extension_registry_factory.h', + 'browser/extension_registry_observer.h', + 'browser/extension_scoped_prefs.h', + 'browser/extension_system.cc', + 'browser/extension_system.h', + 'browser/extension_system_provider.cc', + 'browser/extension_system_provider.h', 'browser/extensions_browser_client.cc', 'browser/extensions_browser_client.h', 'browser/external_provider_interface.h', @@ -203,8 +226,14 @@ 'browser/process_manager.h', 'browser/process_map.cc', 'browser/process_map.h', + 'browser/process_map_factory.cc', + 'browser/process_map_factory.h', 'browser/quota_service.cc', 'browser/quota_service.h', + 'browser/renderer_startup_helper.cc', + 'browser/renderer_startup_helper.h', + 'browser/runtime_data.cc', + 'browser/runtime_data.h', 'browser/update_observer.h', 'browser/view_type_utils.cc', 'browser/view_type_utils.h', @@ -212,5 +241,29 @@ # Disable c4267 warnings until we fix size_t to int truncations. 'msvs_disabled_warnings': [ 4267, ], }, + { + 'target_name': 'extensions_test_support', + 'type': 'static_library', + 'dependencies': [ + 'extensions_browser', + 'extensions_common', + '../base/base.gyp:base', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'browser/test_management_policy.cc', + 'browser/test_management_policy.h', + 'common/extension_builder.cc', + 'common/extension_builder.h', + 'common/test_util.cc', + 'common/test_util.h', + 'common/value_builder.cc', + 'common/value_builder.h', + ], + # Disable c4267 warnings until we fix size_t to int truncations. + 'msvs_disabled_warnings': [ 4267, ], + }, ] } |