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/blocked_actions.h" | |
10 #include "chrome/browser/extensions/extension_service.h" | 14 #include "chrome/browser/extensions/extension_service.h" |
11 #include "chrome/browser/extensions/extension_system.h" | 15 #include "chrome/browser/extensions/extension_system.h" |
16 #include "chrome/browser/extensions/manager_actions.h" | |
12 #include "chrome/browser/profiles/profile.h" | 17 #include "chrome/browser/profiles/profile.h" |
18 #include "chrome/browser/profiles/profile_dependency_manager.h" | |
19 #include "chrome/browser/profiles/profile_keyed_service_factory.h" | |
20 #include "chrome/common/chrome_constants.h" | |
13 #include "chrome/common/chrome_switches.h" | 21 #include "chrome/common/chrome_switches.h" |
14 #include "chrome/common/extensions/extension.h" | 22 #include "chrome/common/extensions/extension.h" |
15 #include "content/public/browser/web_contents.h" | 23 #include "content/public/browser/web_contents.h" |
16 #include "googleurl/src/gurl.h" | 24 #include "googleurl/src/gurl.h" |
25 #include "sql/error_delegate_util.h" | |
26 #include "third_party/re2/re2/re2.h" | |
27 | |
28 namespace { | |
29 | |
30 static const char* kActivityThreadName = "Chrome_ActivityLogThread"; | |
Eric Dingle
2012/12/13 20:37:34
No need for static keyword in this context.
felt
2012/12/15 02:51:52
Done.
| |
31 | |
32 // Concatenate an API call with its arguments. | |
33 std::string MakeCallSignature(const std::string& name, const ListValue& args) { | |
34 std::string call_signature = name + "("; | |
35 ListValue::const_iterator it = args.begin(); | |
36 for (; it != args.end(); ++it) { | |
37 std::string arg; | |
38 JSONStringValueSerializer serializer(&arg); | |
39 if (serializer.SerializeAndOmitBinaryValues(**it)) { | |
40 if (it != args.begin()) | |
41 call_signature += ", "; | |
42 call_signature += arg; | |
43 } | |
44 } | |
45 call_signature += ")"; | |
46 return call_signature; | |
47 } | |
48 | |
49 } // namespace | |
17 | 50 |
18 namespace extensions { | 51 namespace extensions { |
19 | 52 |
20 ActivityLog::ActivityLog() { | 53 namespace { |
54 | |
55 // Each profile has different extensions, so we keep a different database for | |
56 // each profile. | |
57 class ActivityLogFactory : public ProfileKeyedServiceFactory { | |
Eric Dingle
2012/12/13 20:37:34
All classes should have at least one constructor a
felt
2012/12/15 02:51:52
Done.
| |
58 public: | |
59 static ActivityLog* GetForProfile(Profile* profile) { | |
60 return static_cast<ActivityLog*>( | |
61 GetInstance()->GetServiceForProfile(profile, true)); | |
62 } | |
63 | |
64 static ActivityLogFactory* GetInstance(); | |
65 | |
66 private: | |
67 friend struct DefaultSingletonTraits<ActivityLogFactory>; | |
68 | |
69 ActivityLogFactory() | |
70 : ProfileKeyedServiceFactory("ActivityLog", | |
71 ProfileDependencyManager::GetInstance()) { } | |
72 | |
73 virtual ProfileKeyedService* BuildServiceInstanceFor( | |
74 Profile* profile) const OVERRIDE { | |
75 return new ActivityLog(profile); | |
76 } | |
77 | |
78 virtual bool ServiceRedirectedInIncognito() const OVERRIDE { | |
79 return true; | |
80 } | |
81 }; | |
82 | |
83 ActivityLogFactory* ActivityLogFactory::GetInstance() { | |
84 return Singleton<ActivityLogFactory>::get(); | |
85 } | |
86 | |
87 // This handles errors from the database. | |
88 class KillActivityDatabaseErrorDelegate : public sql::ErrorDelegate { | |
89 public: | |
90 explicit KillActivityDatabaseErrorDelegate(ActivityLog* backend) | |
91 : backend_(backend), | |
92 scheduled_death_(false) { } | |
93 | |
94 virtual int OnError(int error, | |
95 sql::Connection* connection, | |
96 sql::Statement* stmt) OVERRIDE { | |
97 if (!scheduled_death_ && sql::IsErrorCatastrophic(error)) { | |
98 scheduled_death_ = true; | |
99 backend_->KillActivityLogDatabase(); | |
100 } | |
101 return error; | |
102 } | |
103 | |
104 bool scheduled_death() const { | |
105 return scheduled_death_; | |
106 } | |
107 | |
108 private: | |
109 ActivityLog* backend_; | |
110 bool scheduled_death_; | |
111 | |
112 DISALLOW_COPY_AND_ASSIGN(KillActivityDatabaseErrorDelegate); | |
113 }; | |
114 | |
115 } // namespace | |
116 | |
117 // Use GetInstance instead of directly creating an ActivityLog. | |
118 ActivityLog::ActivityLog(Profile* profile) | |
119 : thread_(new base::Thread(kActivityThreadName)) { | |
Eric Dingle
2012/12/13 20:37:34
I'm not saying that this is wrong, but you should
felt
2012/12/17 22:45:09
Now using the DB thread.
| |
120 | |
Eric Dingle
2012/12/13 20:37:34
Remove extra blank line.
felt
2012/12/15 02:51:52
Done.
| |
121 log_activity_to_stdout_ = CommandLine::ForCurrentProcess()-> | |
122 HasSwitch(switches::kEnableExtensionActivityLogging); | |
123 | |
124 // If the database cannot be initialized, we keep chugging along and still | |
125 // log actions for displaying in the UI. | |
126 db_ = new ActivityDatabase(); | |
Eric Dingle
2012/12/13 20:37:34
Would suggest using db_.reset(new ActivityDatabase
felt
2012/12/15 02:51:52
The docs in base/memory/ref_counted.h say to use a
| |
127 KillActivityDatabaseErrorDelegate* error_delegate = | |
128 new KillActivityDatabaseErrorDelegate(this); | |
129 FilePath base_dir = profile->GetPath(); | |
130 FilePath database_name = base_dir.Append(chrome::kActivityLogFilename); | |
131 sql::InitStatus status = db_->Init(database_name, error_delegate); | |
132 if (status == sql::INIT_OK) { | |
133 thread_->Start(); | |
134 } else { | |
135 LOG(ERROR) << "Couldn't initialize the activity log database."; | |
136 if (error_delegate->scheduled_death()) { | |
137 KillActivityLogDatabase(); | |
138 } | |
139 } | |
140 } | |
141 | |
142 // If you call this constructor, none of the actions will be recorded in the | |
143 // database, but they will still be displayed in the UI. You should use | |
144 // GetInstance instead of directly creating an ActivityLog. | |
145 ActivityLog::ActivityLog() | |
146 : thread_(NULL) { | |
21 log_activity_to_stdout_ = CommandLine::ForCurrentProcess()-> | 147 log_activity_to_stdout_ = CommandLine::ForCurrentProcess()-> |
22 HasSwitch(switches::kEnableExtensionActivityLogging); | 148 HasSwitch(switches::kEnableExtensionActivityLogging); |
23 } | 149 } |
24 | 150 |
25 ActivityLog::~ActivityLog() { | 151 ActivityLog::~ActivityLog() { |
152 if (thread_) { | |
153 // Delete and defensively NULL the thread. | |
154 base::Thread* thread = thread_; | |
155 thread_ = NULL; | |
156 delete thread; | |
157 } | |
26 } | 158 } |
27 | 159 |
28 // static | 160 // static |
29 ActivityLog* ActivityLog::GetInstance() { | 161 ActivityLog* ActivityLog::GetInstance(Profile* profile) { |
30 return Singleton<ActivityLog>::get(); | 162 return ActivityLogFactory::GetForProfile(profile); |
31 } | 163 } |
32 | 164 |
33 void ActivityLog::AddObserver(const Extension* extension, | 165 void ActivityLog::AddObserver(const Extension* extension, |
34 ActivityLog::Observer* observer) { | 166 ActivityLog::Observer* observer) { |
35 base::AutoLock scoped_lock(lock_); | 167 base::AutoLock scoped_lock(lock_); |
36 | 168 |
37 if (observers_.count(extension) == 0) { | 169 if (observers_.count(extension) == 0) { |
38 observers_[extension] = new ObserverListThreadSafe<Observer>; | 170 observers_[extension] = new ObserverListThreadSafe<Observer>; |
39 } | 171 } |
40 | 172 |
41 observers_[extension]->AddObserver(observer); | 173 observers_[extension]->AddObserver(observer); |
42 } | 174 } |
43 | 175 |
44 void ActivityLog::RemoveObserver(const Extension* extension, | 176 void ActivityLog::RemoveObserver(const Extension* extension, |
45 ActivityLog::Observer* observer) { | 177 ActivityLog::Observer* observer) { |
46 base::AutoLock scoped_lock(lock_); | 178 base::AutoLock scoped_lock(lock_); |
47 | 179 |
48 if (observers_.count(extension) == 1) { | 180 if (observers_.count(extension) == 1) { |
49 observers_[extension]->RemoveObserver(observer); | 181 observers_[extension]->RemoveObserver(observer); |
50 } | 182 } |
51 } | 183 } |
52 | 184 |
53 // Extension* | |
54 bool ActivityLog::HasObservers(const Extension* extension) const { | 185 bool ActivityLog::HasObservers(const Extension* extension) const { |
55 base::AutoLock scoped_lock(lock_); | 186 base::AutoLock scoped_lock(lock_); |
56 | 187 |
57 // We also return true if extension activity logging is enabled since in that | 188 // We also return true if extension activity logging is enabled since in that |
58 // case this class is observing all extensions. | 189 // case this class is observing all extensions. |
59 return observers_.count(extension) > 0 || log_activity_to_stdout_; | 190 return observers_.count(extension) > 0 || log_activity_to_stdout_; |
60 } | 191 } |
61 | 192 |
62 void ActivityLog::Log(const Extension* extension, | 193 void ActivityLog::LogManagerAction(const Extension* extension, |
63 Activity activity, | 194 const std::string& name, |
64 const std::string& message) const { | 195 const ListValue& args) { |
65 std::vector<std::string> messages(1, message); | 196 base::AutoLock scoped_lock(lock_); |
66 Log(extension, activity, messages); | 197 std::string verb; |
Eric Dingle
2012/12/13 20:37:34
std::string manager, verb;
felt
2012/12/15 02:51:52
Done.
| |
67 } | 198 std::string manager; |
199 bool matches = RE2::FullMatch(name, "(.*?)\\.(.*)", &manager, &verb); | |
200 if (matches) { | |
201 std::string call_signature = MakeCallSignature(name, args); | |
202 scoped_refptr<ManagerAction> action = new ManagerAction( | |
203 extension->id(), | |
204 ManagerAction::StringAsActionType(verb), | |
205 ManagerAction::StringAsTargetType(manager), | |
206 call_signature, | |
207 base::Time::Now()); | |
208 ScheduleAndForget(&ActivityDatabase::RecordManagerAction, action); | |
68 | 209 |
69 void ActivityLog::Log(const Extension* extension, | 210 // Display the action. |
70 Activity activity, | 211 ObserverMap::const_iterator iter = observers_.find(extension); |
71 const std::vector<std::string>& messages) const { | 212 if (iter != observers_.end()) { |
72 base::AutoLock scoped_lock(lock_); | 213 iter->second->Notify(&Observer::OnExtensionActivity, |
73 | 214 extension, |
74 ObserverMap::const_iterator iter = observers_.find(extension); | 215 ActivityLog::ACTIVITY_EXTENSION_API_CALL, |
75 if (iter != observers_.end()) { | 216 std::vector<std::string>(1, call_signature)); |
76 iter->second->Notify(&Observer::OnExtensionActivity, extension, activity, | 217 } |
77 messages); | 218 if (log_activity_to_stdout_) { |
78 } | 219 LOG(INFO) << action->PrettyPrintForDebug(); |
79 | 220 } |
80 if (log_activity_to_stdout_) { | 221 } else { |
81 LOG(INFO) << extension->id() << ":" << ActivityToString(activity) << ":" << | 222 LOG(ERROR) << "Unknown API call! " << name; |
82 JoinString(messages, ' '); | |
83 } | 223 } |
84 } | 224 } |
85 | 225 |
226 void ActivityLog::LogBlockedAction(const Extension* extension, | |
Eric Dingle
2012/12/13 20:37:34
You don't notify the observers on blocked calls?
felt
2012/12/15 02:51:52
Whoops. Done.
| |
227 const std::string& blocked_name, | |
228 const ListValue& args, | |
229 const char* reason) { | |
230 base::AutoLock scoped_lock(lock_); | |
231 std::string blocked_call = MakeCallSignature(blocked_name, args); | |
232 scoped_refptr<BlockedAction> action = new BlockedAction(extension->id(), | |
233 blocked_call, | |
234 std::string(reason), | |
235 base::Time::Now()); | |
236 ScheduleAndForget(&ActivityDatabase::RecordBlockedAction, action); | |
237 if (log_activity_to_stdout_) { | |
238 LOG(INFO) << action->PrettyPrintForDebug(); | |
239 } | |
240 } | |
241 | |
242 void ActivityLog::LogUrlAction(const Extension* extension, | |
243 const GURL& url, | |
244 const string16& url_title, | |
245 std::string& message, | |
246 const UrlAction::UrlActionType verb) { | |
247 base::AutoLock scoped_lock(lock_); | |
248 scoped_refptr<UrlAction> action = new UrlAction( | |
249 extension->id(), | |
250 verb, | |
251 url, | |
252 url_title, | |
253 message, | |
254 base::Time::Now()); | |
255 ScheduleAndForget(&ActivityDatabase::RecordUrlAction, action); | |
256 | |
257 // Display the action. | |
258 ObserverMap::const_iterator iter = observers_.find(extension); | |
259 if (iter != observers_.end()) { | |
260 iter->second->Notify(&Observer::OnExtensionActivity, | |
261 extension, | |
262 ActivityLog::ACTIVITY_CONTENT_SCRIPT, | |
Eric Dingle
2012/12/13 20:37:34
I assume that you'll notify for a different kind o
felt
2012/12/15 02:51:52
Yes, I'm going to change how this works as part of
| |
263 std::vector<std::string>(1, | |
264 action->PrettyPrintForDebug())); | |
265 } | |
266 if (log_activity_to_stdout_) { | |
267 LOG(INFO) << action->PrettyPrintForDebug(); | |
268 } | |
269 } | |
270 | |
86 void ActivityLog::OnScriptsExecuted( | 271 void ActivityLog::OnScriptsExecuted( |
87 const content::WebContents* web_contents, | 272 const content::WebContents* web_contents, |
88 const ExecutingScriptsMap& extension_ids, | 273 const ExecutingScriptsMap& extension_ids, |
89 int32 on_page_id, | 274 int32 on_page_id, |
90 const GURL& on_url) { | 275 const GURL& on_url) { |
91 Profile* profile = | 276 Profile* profile = |
92 Profile::FromBrowserContext(web_contents->GetBrowserContext()); | 277 Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
93 const ExtensionService* extension_service = | 278 const ExtensionService* extension_service = |
94 ExtensionSystem::Get(profile)->extension_service(); | 279 ExtensionSystem::Get(profile)->extension_service(); |
95 const ExtensionSet* extensions = extension_service->extensions(); | 280 const ExtensionSet* extensions = extension_service->extensions(); |
96 | 281 |
97 for (ExecutingScriptsMap::const_iterator it = extension_ids.begin(); | 282 for (ExecutingScriptsMap::const_iterator it = extension_ids.begin(); |
98 it != extension_ids.end(); ++it) { | 283 it != extension_ids.end(); ++it) { |
99 const Extension* extension = extensions->GetByID(it->first); | 284 const Extension* extension = extensions->GetByID(it->first); |
100 if (!extension || !HasObservers(extension)) | 285 if (!extension || !HasObservers(extension)) |
101 continue; | 286 continue; |
102 | 287 |
103 for (std::set<std::string>::const_iterator it2 = it->second.begin(); | 288 // If OnScriptsExecuted is fired because of tabs.executeScript, the list |
104 it2 != it->second.end(); ++it2) { | 289 // of content scripts will be empty. We don't want to log it because |
105 std::vector<std::string> messages; | 290 // the call to tabs.executeScript will have already been logged anyway. |
106 messages.push_back(on_url.spec()); | 291 if (!it->second.empty()) { |
107 messages.push_back(*it2); | 292 std::string ext_scripts_str = ""; |
108 Log(extension, ActivityLog::ACTIVITY_CONTENT_SCRIPT, messages); | 293 for (std::set<std::string>::const_iterator it2 = it->second.begin(); |
294 it2 != it->second.end(); ++it2) { | |
295 ext_scripts_str += *it2; | |
296 ext_scripts_str += " "; | |
297 } | |
298 LogUrlAction(extension, | |
299 on_url, | |
300 web_contents->GetTitle(), | |
301 ext_scripts_str, | |
302 UrlAction::INSERTED); | |
109 } | 303 } |
110 } | 304 } |
111 } | 305 } |
112 | 306 |
307 void ActivityLog::KillActivityLogDatabase() { | |
308 if (db_.get()) { | |
309 ScheduleAndForget(&ActivityDatabase::KillDatabase); | |
310 } | |
311 } | |
312 | |
113 // static | 313 // static |
114 const char* ActivityLog::ActivityToString(Activity activity) { | 314 const char* ActivityLog::ActivityToString(Activity activity) { |
115 switch (activity) { | 315 switch (activity) { |
116 case ActivityLog::ACTIVITY_EXTENSION_API_CALL: | 316 case ActivityLog::ACTIVITY_EXTENSION_API_CALL: |
117 return "api_call"; | 317 return "api_call"; |
118 case ActivityLog::ACTIVITY_EXTENSION_API_BLOCK: | 318 case ActivityLog::ACTIVITY_EXTENSION_API_BLOCK: |
119 return "api_block"; | 319 return "api_block"; |
120 case ActivityLog::ACTIVITY_CONTENT_SCRIPT: | 320 case ActivityLog::ACTIVITY_CONTENT_SCRIPT: |
121 return "content_script"; | 321 return "content_script"; |
122 default: | 322 default: |
123 NOTREACHED(); | 323 NOTREACHED(); |
124 return ""; | 324 return ""; |
125 } | 325 } |
126 } | 326 } |
127 | 327 |
128 } // namespace extensions | 328 } // namespace extensions |
OLD | NEW |