| Index: chrome/browser/extensions/activity_log.cc
|
| ===================================================================
|
| --- chrome/browser/extensions/activity_log.cc (revision 177317)
|
| +++ chrome/browser/extensions/activity_log.cc (working copy)
|
| @@ -4,36 +4,131 @@
|
|
|
| #include "chrome/browser/extensions/activity_log.h"
|
|
|
| +#include <set>
|
| #include "base/command_line.h"
|
| +#include "base/json/json_string_value_serializer.h"
|
| #include "base/logging.h"
|
| #include "base/string_util.h"
|
| +#include "base/threading/thread_checker.h"
|
| +#include "chrome/browser/extensions/api_actions.h"
|
| +#include "chrome/browser/extensions/blocked_actions.h"
|
| #include "chrome/browser/extensions/extension_service.h"
|
| #include "chrome/browser/extensions/extension_system.h"
|
| -#include "chrome/browser/profiles/profile.h"
|
| +#include "chrome/common/chrome_constants.h"
|
| #include "chrome/common/chrome_switches.h"
|
| #include "chrome/common/extensions/extension.h"
|
| #include "content/public/browser/web_contents.h"
|
| #include "googleurl/src/gurl.h"
|
| +#include "sql/error_delegate_util.h"
|
| +#include "third_party/re2/re2/re2.h"
|
|
|
| +namespace {
|
| +
|
| +// Concatenate an API call with its arguments.
|
| +std::string MakeCallSignature(const std::string& name, const ListValue* args) {
|
| + std::string call_signature = name + "(";
|
| + ListValue::const_iterator it = args->begin();
|
| + for (; it != args->end(); ++it) {
|
| + std::string arg;
|
| + JSONStringValueSerializer serializer(&arg);
|
| + if (serializer.SerializeAndOmitBinaryValues(**it)) {
|
| + if (it != args->begin())
|
| + call_signature += ", ";
|
| + call_signature += arg;
|
| + }
|
| + }
|
| + call_signature += ")";
|
| + return call_signature;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| namespace extensions {
|
|
|
| -ActivityLog::ActivityLog() {
|
| +// This handles errors from the database.
|
| +class KillActivityDatabaseErrorDelegate : public sql::ErrorDelegate {
|
| + public:
|
| + explicit KillActivityDatabaseErrorDelegate(ActivityLog* backend)
|
| + : backend_(backend),
|
| + scheduled_death_(false) {}
|
| +
|
| + virtual int OnError(int error,
|
| + sql::Connection* connection,
|
| + sql::Statement* stmt) OVERRIDE {
|
| + if (!scheduled_death_ && sql::IsErrorCatastrophic(error)) {
|
| + ScheduleDeath();
|
| + }
|
| + return error;
|
| + }
|
| +
|
| + // Schedules death if an error wasn't already reported.
|
| + void ScheduleDeath() {
|
| + if (!scheduled_death_) {
|
| + scheduled_death_ = true;
|
| + backend_->KillActivityLogDatabase();
|
| + }
|
| + }
|
| +
|
| + bool scheduled_death() const {
|
| + return scheduled_death_;
|
| + }
|
| +
|
| + private:
|
| + ActivityLog* backend_;
|
| + bool scheduled_death_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(KillActivityDatabaseErrorDelegate);
|
| +};
|
| +
|
| +// ActivityLogFactory
|
| +
|
| +ActivityLogFactory* ActivityLogFactory::GetInstance() {
|
| + return Singleton<ActivityLogFactory>::get();
|
| +}
|
| +
|
| +ProfileKeyedService* ActivityLogFactory::BuildServiceInstanceFor(
|
| + Profile* profile) const {
|
| + return new ActivityLog(profile);
|
| +}
|
| +
|
| +bool ActivityLogFactory::ServiceRedirectedInIncognito() const {
|
| + return true;
|
| +}
|
| +
|
| +// ActivityLog
|
| +
|
| +// Use GetInstance instead of directly creating an ActivityLog.
|
| +ActivityLog::ActivityLog(Profile* profile) {
|
| log_activity_to_stdout_ = CommandLine::ForCurrentProcess()->
|
| HasSwitch(switches::kEnableExtensionActivityLogging);
|
| +
|
| + // If the database cannot be initialized for some reason, we keep
|
| + // chugging along but nothing will get recorded. If the UI is
|
| + // available, things will still get sent to the UI even if nothing
|
| + // is being written to the database.
|
| + db_ = new ActivityDatabase();
|
| +#if defined(ENABLE_EXTENSIONS)
|
| + FilePath base_dir = profile->GetPath();
|
| + FilePath database_name = base_dir.Append(
|
| + chrome::kExtensionActivityLogFilename);
|
| + KillActivityDatabaseErrorDelegate* error_delegate =
|
| + new KillActivityDatabaseErrorDelegate(this);
|
| + db_->SetErrorDelegate(error_delegate);
|
| + ScheduleAndForget(&ActivityDatabase::Init,
|
| + database_name);
|
| +#endif
|
| }
|
|
|
| ActivityLog::~ActivityLog() {
|
| }
|
|
|
| // static
|
| -ActivityLog* ActivityLog::GetInstance() {
|
| - return Singleton<ActivityLog>::get();
|
| +ActivityLog* ActivityLog::GetInstance(Profile* profile) {
|
| + return ActivityLogFactory::GetForProfile(profile);
|
| }
|
|
|
| void ActivityLog::AddObserver(const Extension* extension,
|
| ActivityLog::Observer* observer) {
|
| - base::AutoLock scoped_lock(lock_);
|
| -
|
| if (observers_.count(extension) == 0) {
|
| observers_[extension] = new ObserverListThreadSafe<Observer>;
|
| }
|
| @@ -43,43 +138,101 @@
|
|
|
| void ActivityLog::RemoveObserver(const Extension* extension,
|
| ActivityLog::Observer* observer) {
|
| - base::AutoLock scoped_lock(lock_);
|
| -
|
| if (observers_.count(extension) == 1) {
|
| observers_[extension]->RemoveObserver(observer);
|
| }
|
| }
|
|
|
| -// Extension*
|
| bool ActivityLog::HasObservers(const Extension* extension) const {
|
| - base::AutoLock scoped_lock(lock_);
|
| -
|
| // We also return true if extension activity logging is enabled since in that
|
| // case this class is observing all extensions.
|
| return observers_.count(extension) > 0 || log_activity_to_stdout_;
|
| }
|
|
|
| -void ActivityLog::Log(const Extension* extension,
|
| - Activity activity,
|
| - const std::string& message) const {
|
| - std::vector<std::string> messages(1, message);
|
| - Log(extension, activity, messages);
|
| +void ActivityLog::LogAPIAction(const Extension* extension,
|
| + const std::string& name,
|
| + const ListValue* args,
|
| + const std::string& extra) {
|
| + std::string verb, manager;
|
| + bool matches = RE2::FullMatch(name, "(.*?)\\.(.*)", &manager, &verb);
|
| + if (matches) {
|
| + std::string call_signature = MakeCallSignature(name, args);
|
| + scoped_refptr<APIAction> action = new APIAction(
|
| + extension->id(),
|
| + base::Time::Now(),
|
| + APIAction::StringAsActionType(verb),
|
| + APIAction::StringAsTargetType(manager),
|
| + call_signature,
|
| + extra);
|
| + ScheduleAndForget(&ActivityDatabase::RecordAction, action);
|
| +
|
| + // Display the action.
|
| + ObserverMap::const_iterator iter = observers_.find(extension);
|
| + if (iter != observers_.end()) {
|
| + iter->second->Notify(&Observer::OnExtensionActivity,
|
| + extension,
|
| + ActivityLog::ACTIVITY_EXTENSION_API_CALL,
|
| + call_signature);
|
| + }
|
| + if (log_activity_to_stdout_) {
|
| + LOG(INFO) << action->PrettyPrintForDebug();
|
| + }
|
| + } else {
|
| + LOG(ERROR) << "Unknown API call! " << name;
|
| + }
|
| }
|
|
|
| -void ActivityLog::Log(const Extension* extension,
|
| - Activity activity,
|
| - const std::vector<std::string>& messages) const {
|
| - base::AutoLock scoped_lock(lock_);
|
| +void ActivityLog::LogBlockedAction(const Extension* extension,
|
| + const std::string& blocked_name,
|
| + const ListValue* args,
|
| + const char* reason,
|
| + const std::string& extra) {
|
| + std::string blocked_call = MakeCallSignature(blocked_name, args);
|
| + scoped_refptr<BlockedAction> action = new BlockedAction(extension->id(),
|
| + base::Time::Now(),
|
| + blocked_call,
|
| + std::string(reason),
|
| + extra);
|
| + ScheduleAndForget(&ActivityDatabase::RecordAction, action);
|
| + // Display the action.
|
| + ObserverMap::const_iterator iter = observers_.find(extension);
|
| + if (iter != observers_.end()) {
|
| + iter->second->Notify(&Observer::OnExtensionActivity,
|
| + extension,
|
| + ActivityLog::ACTIVITY_EXTENSION_API_BLOCK,
|
| + blocked_call);
|
| + }
|
| + if (log_activity_to_stdout_) {
|
| + LOG(INFO) << action->PrettyPrintForDebug();
|
| + }
|
| +}
|
|
|
| +void ActivityLog::LogUrlAction(const Extension* extension,
|
| + const UrlAction::UrlActionType verb,
|
| + const GURL& url,
|
| + const string16& url_title,
|
| + const std::string& technical_message,
|
| + const std::string& extra) {
|
| + scoped_refptr<UrlAction> action = new UrlAction(
|
| + extension->id(),
|
| + base::Time::Now(),
|
| + verb,
|
| + url,
|
| + url_title,
|
| + technical_message,
|
| + extra);
|
| + ScheduleAndForget(&ActivityDatabase::RecordAction, action);
|
| +
|
| + // Display the action.
|
| ObserverMap::const_iterator iter = observers_.find(extension);
|
| if (iter != observers_.end()) {
|
| - iter->second->Notify(&Observer::OnExtensionActivity, extension, activity,
|
| - messages);
|
| + iter->second->Notify(&Observer::OnExtensionActivity,
|
| + extension,
|
| + ActivityLog::ACTIVITY_CONTENT_SCRIPT,
|
| + action->PrettyPrintForDebug());
|
| }
|
| -
|
| if (log_activity_to_stdout_) {
|
| - LOG(INFO) << extension->id() << ":" << ActivityToString(activity) << ":" <<
|
| - JoinString(messages, ' ');
|
| + LOG(INFO) << action->PrettyPrintForDebug();
|
| }
|
| }
|
|
|
| @@ -100,16 +253,32 @@
|
| if (!extension || !HasObservers(extension))
|
| continue;
|
|
|
| - for (std::set<std::string>::const_iterator it2 = it->second.begin();
|
| - it2 != it->second.end(); ++it2) {
|
| - std::vector<std::string> messages;
|
| - messages.push_back(on_url.spec());
|
| - messages.push_back(*it2);
|
| - Log(extension, ActivityLog::ACTIVITY_CONTENT_SCRIPT, messages);
|
| + // If OnScriptsExecuted is fired because of tabs.executeScript, the list
|
| + // of content scripts will be empty. We don't want to log it because
|
| + // the call to tabs.executeScript will have already been logged anyway.
|
| + if (!it->second.empty()) {
|
| + std::string ext_scripts_str = "";
|
| + for (std::set<std::string>::const_iterator it2 = it->second.begin();
|
| + it2 != it->second.end(); ++it2) {
|
| + ext_scripts_str += *it2;
|
| + ext_scripts_str += " ";
|
| + }
|
| + LogUrlAction(extension,
|
| + UrlAction::INSERTED,
|
| + on_url,
|
| + web_contents->GetTitle(),
|
| + ext_scripts_str,
|
| + "");
|
| }
|
| }
|
| }
|
|
|
| +void ActivityLog::KillActivityLogDatabase() {
|
| + if (db_.get()) {
|
| + ScheduleAndForget(&ActivityDatabase::KillDatabase);
|
| + }
|
| +}
|
| +
|
| // static
|
| const char* ActivityLog::ActivityToString(Activity activity) {
|
| switch (activity) {
|
| @@ -126,3 +295,4 @@
|
| }
|
|
|
| } // namespace extensions
|
| +
|
|
|