OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/extensions/activity_log.h" | 5 #include "chrome/browser/extensions/activity_log.h" |
6 | 6 |
| 7 #include <set> |
7 #include "base/command_line.h" | 8 #include "base/command_line.h" |
| 9 #include "base/json/json_string_value_serializer.h" |
8 #include "base/logging.h" | 10 #include "base/logging.h" |
9 #include "base/string_util.h" | 11 #include "base/string_util.h" |
| 12 #include "base/threading/thread_checker.h" |
| 13 #include "chrome/browser/extensions/api_actions.h" |
| 14 #include "chrome/browser/extensions/blocked_actions.h" |
10 #include "chrome/browser/extensions/extension_service.h" | 15 #include "chrome/browser/extensions/extension_service.h" |
11 #include "chrome/browser/extensions/extension_system.h" | 16 #include "chrome/browser/extensions/extension_system.h" |
12 #include "chrome/browser/profiles/profile.h" | 17 #include "chrome/common/chrome_constants.h" |
13 #include "chrome/common/chrome_switches.h" | 18 #include "chrome/common/chrome_switches.h" |
14 #include "chrome/common/extensions/extension.h" | 19 #include "chrome/common/extensions/extension.h" |
15 #include "content/public/browser/web_contents.h" | 20 #include "content/public/browser/web_contents.h" |
16 #include "googleurl/src/gurl.h" | 21 #include "googleurl/src/gurl.h" |
| 22 #include "sql/error_delegate_util.h" |
| 23 #include "third_party/re2/re2/re2.h" |
| 24 |
| 25 namespace { |
| 26 |
| 27 // Concatenate an API call with its arguments. |
| 28 std::string MakeCallSignature(const std::string& name, const ListValue* args) { |
| 29 std::string call_signature = name + "("; |
| 30 ListValue::const_iterator it = args->begin(); |
| 31 for (; it != args->end(); ++it) { |
| 32 std::string arg; |
| 33 JSONStringValueSerializer serializer(&arg); |
| 34 if (serializer.SerializeAndOmitBinaryValues(**it)) { |
| 35 if (it != args->begin()) |
| 36 call_signature += ", "; |
| 37 call_signature += arg; |
| 38 } |
| 39 } |
| 40 call_signature += ")"; |
| 41 return call_signature; |
| 42 } |
| 43 |
| 44 } // namespace |
17 | 45 |
18 namespace extensions { | 46 namespace extensions { |
19 | 47 |
20 ActivityLog::ActivityLog() { | 48 // This handles errors from the database. |
| 49 class KillActivityDatabaseErrorDelegate : public sql::ErrorDelegate { |
| 50 public: |
| 51 explicit KillActivityDatabaseErrorDelegate(ActivityLog* backend) |
| 52 : backend_(backend), |
| 53 scheduled_death_(false) {} |
| 54 |
| 55 virtual int OnError(int error, |
| 56 sql::Connection* connection, |
| 57 sql::Statement* stmt) OVERRIDE { |
| 58 if (!scheduled_death_ && sql::IsErrorCatastrophic(error)) { |
| 59 ScheduleDeath(); |
| 60 } |
| 61 return error; |
| 62 } |
| 63 |
| 64 // Schedules death if an error wasn't already reported. |
| 65 void ScheduleDeath() { |
| 66 if (!scheduled_death_) { |
| 67 scheduled_death_ = true; |
| 68 backend_->KillActivityLogDatabase(); |
| 69 } |
| 70 } |
| 71 |
| 72 bool scheduled_death() const { |
| 73 return scheduled_death_; |
| 74 } |
| 75 |
| 76 private: |
| 77 ActivityLog* backend_; |
| 78 bool scheduled_death_; |
| 79 |
| 80 DISALLOW_COPY_AND_ASSIGN(KillActivityDatabaseErrorDelegate); |
| 81 }; |
| 82 |
| 83 // ActivityLogFactory |
| 84 |
| 85 ActivityLogFactory* ActivityLogFactory::GetInstance() { |
| 86 return Singleton<ActivityLogFactory>::get(); |
| 87 } |
| 88 |
| 89 ProfileKeyedService* ActivityLogFactory::BuildServiceInstanceFor( |
| 90 Profile* profile) const { |
| 91 return new ActivityLog(profile); |
| 92 } |
| 93 |
| 94 bool ActivityLogFactory::ServiceRedirectedInIncognito() const { |
| 95 return true; |
| 96 } |
| 97 |
| 98 // ActivityLog |
| 99 |
| 100 // Use GetInstance instead of directly creating an ActivityLog. |
| 101 ActivityLog::ActivityLog(Profile* profile) { |
21 log_activity_to_stdout_ = CommandLine::ForCurrentProcess()-> | 102 log_activity_to_stdout_ = CommandLine::ForCurrentProcess()-> |
22 HasSwitch(switches::kEnableExtensionActivityLogging); | 103 HasSwitch(switches::kEnableExtensionActivityLogging); |
| 104 |
| 105 // If the database cannot be initialized for some reason, we keep |
| 106 // chugging along but nothing will get recorded. If the UI is |
| 107 // available, things will still get sent to the UI even if nothing |
| 108 // is being written to the database. |
| 109 db_ = new ActivityDatabase(); |
| 110 #if defined(ENABLE_EXTENSIONS) |
| 111 FilePath base_dir = profile->GetPath(); |
| 112 FilePath database_name = base_dir.Append( |
| 113 chrome::kExtensionActivityLogFilename); |
| 114 KillActivityDatabaseErrorDelegate* error_delegate = |
| 115 new KillActivityDatabaseErrorDelegate(this); |
| 116 db_->SetErrorDelegate(error_delegate); |
| 117 ScheduleAndForget(&ActivityDatabase::Init, |
| 118 database_name); |
| 119 #endif |
23 } | 120 } |
24 | 121 |
25 ActivityLog::~ActivityLog() { | 122 ActivityLog::~ActivityLog() { |
26 } | 123 } |
27 | 124 |
28 // static | 125 // static |
29 ActivityLog* ActivityLog::GetInstance() { | 126 ActivityLog* ActivityLog::GetInstance(Profile* profile) { |
30 return Singleton<ActivityLog>::get(); | 127 return ActivityLogFactory::GetForProfile(profile); |
31 } | 128 } |
32 | 129 |
33 void ActivityLog::AddObserver(const Extension* extension, | 130 void ActivityLog::AddObserver(const Extension* extension, |
34 ActivityLog::Observer* observer) { | 131 ActivityLog::Observer* observer) { |
35 base::AutoLock scoped_lock(lock_); | |
36 | |
37 if (observers_.count(extension) == 0) { | 132 if (observers_.count(extension) == 0) { |
38 observers_[extension] = new ObserverListThreadSafe<Observer>; | 133 observers_[extension] = new ObserverListThreadSafe<Observer>; |
39 } | 134 } |
40 | 135 |
41 observers_[extension]->AddObserver(observer); | 136 observers_[extension]->AddObserver(observer); |
42 } | 137 } |
43 | 138 |
44 void ActivityLog::RemoveObserver(const Extension* extension, | 139 void ActivityLog::RemoveObserver(const Extension* extension, |
45 ActivityLog::Observer* observer) { | 140 ActivityLog::Observer* observer) { |
46 base::AutoLock scoped_lock(lock_); | |
47 | |
48 if (observers_.count(extension) == 1) { | 141 if (observers_.count(extension) == 1) { |
49 observers_[extension]->RemoveObserver(observer); | 142 observers_[extension]->RemoveObserver(observer); |
50 } | 143 } |
51 } | 144 } |
52 | 145 |
53 // Extension* | |
54 bool ActivityLog::HasObservers(const Extension* extension) const { | 146 bool ActivityLog::HasObservers(const Extension* extension) const { |
55 base::AutoLock scoped_lock(lock_); | |
56 | |
57 // We also return true if extension activity logging is enabled since in that | 147 // We also return true if extension activity logging is enabled since in that |
58 // case this class is observing all extensions. | 148 // case this class is observing all extensions. |
59 return observers_.count(extension) > 0 || log_activity_to_stdout_; | 149 return observers_.count(extension) > 0 || log_activity_to_stdout_; |
60 } | 150 } |
61 | 151 |
62 void ActivityLog::Log(const Extension* extension, | 152 void ActivityLog::LogAPIAction(const Extension* extension, |
63 Activity activity, | 153 const std::string& name, |
64 const std::string& message) const { | 154 const ListValue* args, |
65 std::vector<std::string> messages(1, message); | 155 const std::string& extra) { |
66 Log(extension, activity, messages); | 156 std::string verb, manager; |
67 } | 157 bool matches = RE2::FullMatch(name, "(.*?)\\.(.*)", &manager, &verb); |
| 158 if (matches) { |
| 159 std::string call_signature = MakeCallSignature(name, args); |
| 160 scoped_refptr<APIAction> action = new APIAction( |
| 161 extension->id(), |
| 162 base::Time::Now(), |
| 163 APIAction::StringAsActionType(verb), |
| 164 APIAction::StringAsTargetType(manager), |
| 165 call_signature, |
| 166 extra); |
| 167 ScheduleAndForget(&ActivityDatabase::RecordAction, action); |
68 | 168 |
69 void ActivityLog::Log(const Extension* extension, | 169 // Display the action. |
70 Activity activity, | 170 ObserverMap::const_iterator iter = observers_.find(extension); |
71 const std::vector<std::string>& messages) const { | 171 if (iter != observers_.end()) { |
72 base::AutoLock scoped_lock(lock_); | 172 iter->second->Notify(&Observer::OnExtensionActivity, |
73 | 173 extension, |
74 ObserverMap::const_iterator iter = observers_.find(extension); | 174 ActivityLog::ACTIVITY_EXTENSION_API_CALL, |
75 if (iter != observers_.end()) { | 175 call_signature); |
76 iter->second->Notify(&Observer::OnExtensionActivity, extension, activity, | 176 } |
77 messages); | 177 if (log_activity_to_stdout_) { |
78 } | 178 LOG(INFO) << action->PrettyPrintForDebug(); |
79 | 179 } |
80 if (log_activity_to_stdout_) { | 180 } else { |
81 LOG(INFO) << extension->id() << ":" << ActivityToString(activity) << ":" << | 181 LOG(ERROR) << "Unknown API call! " << name; |
82 JoinString(messages, ' '); | |
83 } | 182 } |
84 } | 183 } |
85 | 184 |
| 185 void ActivityLog::LogBlockedAction(const Extension* extension, |
| 186 const std::string& blocked_name, |
| 187 const ListValue* args, |
| 188 const char* reason, |
| 189 const std::string& extra) { |
| 190 std::string blocked_call = MakeCallSignature(blocked_name, args); |
| 191 scoped_refptr<BlockedAction> action = new BlockedAction(extension->id(), |
| 192 base::Time::Now(), |
| 193 blocked_call, |
| 194 std::string(reason), |
| 195 extra); |
| 196 ScheduleAndForget(&ActivityDatabase::RecordAction, action); |
| 197 // Display the action. |
| 198 ObserverMap::const_iterator iter = observers_.find(extension); |
| 199 if (iter != observers_.end()) { |
| 200 iter->second->Notify(&Observer::OnExtensionActivity, |
| 201 extension, |
| 202 ActivityLog::ACTIVITY_EXTENSION_API_BLOCK, |
| 203 blocked_call); |
| 204 } |
| 205 if (log_activity_to_stdout_) { |
| 206 LOG(INFO) << action->PrettyPrintForDebug(); |
| 207 } |
| 208 } |
| 209 |
| 210 void ActivityLog::LogUrlAction(const Extension* extension, |
| 211 const UrlAction::UrlActionType verb, |
| 212 const GURL& url, |
| 213 const string16& url_title, |
| 214 const std::string& technical_message, |
| 215 const std::string& extra) { |
| 216 scoped_refptr<UrlAction> action = new UrlAction( |
| 217 extension->id(), |
| 218 base::Time::Now(), |
| 219 verb, |
| 220 url, |
| 221 url_title, |
| 222 technical_message, |
| 223 extra); |
| 224 ScheduleAndForget(&ActivityDatabase::RecordAction, action); |
| 225 |
| 226 // Display the action. |
| 227 ObserverMap::const_iterator iter = observers_.find(extension); |
| 228 if (iter != observers_.end()) { |
| 229 iter->second->Notify(&Observer::OnExtensionActivity, |
| 230 extension, |
| 231 ActivityLog::ACTIVITY_CONTENT_SCRIPT, |
| 232 action->PrettyPrintForDebug()); |
| 233 } |
| 234 if (log_activity_to_stdout_) { |
| 235 LOG(INFO) << action->PrettyPrintForDebug(); |
| 236 } |
| 237 } |
| 238 |
86 void ActivityLog::OnScriptsExecuted( | 239 void ActivityLog::OnScriptsExecuted( |
87 const content::WebContents* web_contents, | 240 const content::WebContents* web_contents, |
88 const ExecutingScriptsMap& extension_ids, | 241 const ExecutingScriptsMap& extension_ids, |
89 int32 on_page_id, | 242 int32 on_page_id, |
90 const GURL& on_url) { | 243 const GURL& on_url) { |
91 Profile* profile = | 244 Profile* profile = |
92 Profile::FromBrowserContext(web_contents->GetBrowserContext()); | 245 Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
93 const ExtensionService* extension_service = | 246 const ExtensionService* extension_service = |
94 ExtensionSystem::Get(profile)->extension_service(); | 247 ExtensionSystem::Get(profile)->extension_service(); |
95 const ExtensionSet* extensions = extension_service->extensions(); | 248 const ExtensionSet* extensions = extension_service->extensions(); |
96 | 249 |
97 for (ExecutingScriptsMap::const_iterator it = extension_ids.begin(); | 250 for (ExecutingScriptsMap::const_iterator it = extension_ids.begin(); |
98 it != extension_ids.end(); ++it) { | 251 it != extension_ids.end(); ++it) { |
99 const Extension* extension = extensions->GetByID(it->first); | 252 const Extension* extension = extensions->GetByID(it->first); |
100 if (!extension || !HasObservers(extension)) | 253 if (!extension || !HasObservers(extension)) |
101 continue; | 254 continue; |
102 | 255 |
103 for (std::set<std::string>::const_iterator it2 = it->second.begin(); | 256 // If OnScriptsExecuted is fired because of tabs.executeScript, the list |
104 it2 != it->second.end(); ++it2) { | 257 // of content scripts will be empty. We don't want to log it because |
105 std::vector<std::string> messages; | 258 // the call to tabs.executeScript will have already been logged anyway. |
106 messages.push_back(on_url.spec()); | 259 if (!it->second.empty()) { |
107 messages.push_back(*it2); | 260 std::string ext_scripts_str = ""; |
108 Log(extension, ActivityLog::ACTIVITY_CONTENT_SCRIPT, messages); | 261 for (std::set<std::string>::const_iterator it2 = it->second.begin(); |
| 262 it2 != it->second.end(); ++it2) { |
| 263 ext_scripts_str += *it2; |
| 264 ext_scripts_str += " "; |
| 265 } |
| 266 LogUrlAction(extension, |
| 267 UrlAction::INSERTED, |
| 268 on_url, |
| 269 web_contents->GetTitle(), |
| 270 ext_scripts_str, |
| 271 ""); |
109 } | 272 } |
110 } | 273 } |
111 } | 274 } |
112 | 275 |
| 276 void ActivityLog::KillActivityLogDatabase() { |
| 277 if (db_.get()) { |
| 278 ScheduleAndForget(&ActivityDatabase::KillDatabase); |
| 279 } |
| 280 } |
| 281 |
113 // static | 282 // static |
114 const char* ActivityLog::ActivityToString(Activity activity) { | 283 const char* ActivityLog::ActivityToString(Activity activity) { |
115 switch (activity) { | 284 switch (activity) { |
116 case ActivityLog::ACTIVITY_EXTENSION_API_CALL: | 285 case ActivityLog::ACTIVITY_EXTENSION_API_CALL: |
117 return "api_call"; | 286 return "api_call"; |
118 case ActivityLog::ACTIVITY_EXTENSION_API_BLOCK: | 287 case ActivityLog::ACTIVITY_EXTENSION_API_BLOCK: |
119 return "api_block"; | 288 return "api_block"; |
120 case ActivityLog::ACTIVITY_CONTENT_SCRIPT: | 289 case ActivityLog::ACTIVITY_CONTENT_SCRIPT: |
121 return "content_script"; | 290 return "content_script"; |
122 default: | 291 default: |
123 NOTREACHED(); | 292 NOTREACHED(); |
124 return ""; | 293 return ""; |
125 } | 294 } |
126 } | 295 } |
127 | 296 |
128 } // namespace extensions | 297 } // namespace extensions |
| 298 |
OLD | NEW |