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