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> | |
8 #include "base/command_line.h" | 7 #include "base/command_line.h" |
9 #include "base/json/json_string_value_serializer.h" | |
10 #include "base/logging.h" | 8 #include "base/logging.h" |
11 #include "base/string_util.h" | 9 #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" | |
15 #include "chrome/browser/extensions/extension_service.h" | 10 #include "chrome/browser/extensions/extension_service.h" |
16 #include "chrome/browser/extensions/extension_system.h" | 11 #include "chrome/browser/extensions/extension_system.h" |
17 #include "chrome/common/chrome_constants.h" | 12 #include "chrome/browser/profiles/profile.h" |
18 #include "chrome/common/chrome_switches.h" | 13 #include "chrome/common/chrome_switches.h" |
19 #include "chrome/common/extensions/extension.h" | 14 #include "chrome/common/extensions/extension.h" |
20 #include "content/public/browser/web_contents.h" | 15 #include "content/public/browser/web_contents.h" |
21 #include "googleurl/src/gurl.h" | 16 #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 | |
45 | 17 |
46 namespace extensions { | 18 namespace extensions { |
47 | 19 |
48 // This handles errors from the database. | 20 ActivityLog::ActivityLog() { |
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) { | |
102 log_activity_to_stdout_ = CommandLine::ForCurrentProcess()-> | 21 log_activity_to_stdout_ = CommandLine::ForCurrentProcess()-> |
103 HasSwitch(switches::kEnableExtensionActivityLogging); | 22 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) && !defined(OS_ANDROID) | |
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 ScheduleAndForget(&ActivityDatabase::Init, | |
117 database_name, | |
118 error_delegate); | |
119 #endif | |
120 } | 23 } |
121 | 24 |
122 ActivityLog::~ActivityLog() { | 25 ActivityLog::~ActivityLog() { |
123 } | 26 } |
124 | 27 |
125 // static | 28 // static |
126 ActivityLog* ActivityLog::GetInstance(Profile* profile) { | 29 ActivityLog* ActivityLog::GetInstance() { |
127 return ActivityLogFactory::GetForProfile(profile); | 30 return Singleton<ActivityLog>::get(); |
128 } | 31 } |
129 | 32 |
130 void ActivityLog::AddObserver(const Extension* extension, | 33 void ActivityLog::AddObserver(const Extension* extension, |
131 ActivityLog::Observer* observer) { | 34 ActivityLog::Observer* observer) { |
| 35 base::AutoLock scoped_lock(lock_); |
| 36 |
132 if (observers_.count(extension) == 0) { | 37 if (observers_.count(extension) == 0) { |
133 observers_[extension] = new ObserverListThreadSafe<Observer>; | 38 observers_[extension] = new ObserverListThreadSafe<Observer>; |
134 } | 39 } |
135 | 40 |
136 observers_[extension]->AddObserver(observer); | 41 observers_[extension]->AddObserver(observer); |
137 } | 42 } |
138 | 43 |
139 void ActivityLog::RemoveObserver(const Extension* extension, | 44 void ActivityLog::RemoveObserver(const Extension* extension, |
140 ActivityLog::Observer* observer) { | 45 ActivityLog::Observer* observer) { |
| 46 base::AutoLock scoped_lock(lock_); |
| 47 |
141 if (observers_.count(extension) == 1) { | 48 if (observers_.count(extension) == 1) { |
142 observers_[extension]->RemoveObserver(observer); | 49 observers_[extension]->RemoveObserver(observer); |
143 } | 50 } |
144 } | 51 } |
145 | 52 |
| 53 // Extension* |
146 bool ActivityLog::HasObservers(const Extension* extension) const { | 54 bool ActivityLog::HasObservers(const Extension* extension) const { |
| 55 base::AutoLock scoped_lock(lock_); |
| 56 |
147 // We also return true if extension activity logging is enabled since in that | 57 // We also return true if extension activity logging is enabled since in that |
148 // case this class is observing all extensions. | 58 // case this class is observing all extensions. |
149 return observers_.count(extension) > 0 || log_activity_to_stdout_; | 59 return observers_.count(extension) > 0 || log_activity_to_stdout_; |
150 } | 60 } |
151 | 61 |
152 void ActivityLog::LogAPIAction(const Extension* extension, | 62 void ActivityLog::Log(const Extension* extension, |
153 const std::string& name, | 63 Activity activity, |
154 const ListValue* args, | 64 const std::string& message) const { |
155 const std::string& extra) { | 65 std::vector<std::string> messages(1, message); |
156 std::string verb, manager; | 66 Log(extension, activity, messages); |
157 bool matches = RE2::FullMatch(name, "(.*?)\\.(.*)", &manager, &verb); | 67 } |
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); | |
168 | 68 |
169 // Display the action. | 69 void ActivityLog::Log(const Extension* extension, |
170 ObserverMap::const_iterator iter = observers_.find(extension); | 70 Activity activity, |
171 if (iter != observers_.end()) { | 71 const std::vector<std::string>& messages) const { |
172 iter->second->Notify(&Observer::OnExtensionActivity, | 72 base::AutoLock scoped_lock(lock_); |
173 extension, | 73 |
174 ActivityLog::ACTIVITY_EXTENSION_API_CALL, | 74 ObserverMap::const_iterator iter = observers_.find(extension); |
175 call_signature); | 75 if (iter != observers_.end()) { |
176 } | 76 iter->second->Notify(&Observer::OnExtensionActivity, extension, activity, |
177 if (log_activity_to_stdout_) { | 77 messages); |
178 LOG(INFO) << action->PrettyPrintForDebug(); | 78 } |
179 } | 79 |
180 } else { | 80 if (log_activity_to_stdout_) { |
181 LOG(ERROR) << "Unknown API call! " << name; | 81 LOG(INFO) << extension->id() << ":" << ActivityToString(activity) << ":" << |
| 82 JoinString(messages, ' '); |
182 } | 83 } |
183 } | 84 } |
184 | 85 |
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 | |
239 void ActivityLog::OnScriptsExecuted( | 86 void ActivityLog::OnScriptsExecuted( |
240 const content::WebContents* web_contents, | 87 const content::WebContents* web_contents, |
241 const ExecutingScriptsMap& extension_ids, | 88 const ExecutingScriptsMap& extension_ids, |
242 int32 on_page_id, | 89 int32 on_page_id, |
243 const GURL& on_url) { | 90 const GURL& on_url) { |
244 Profile* profile = | 91 Profile* profile = |
245 Profile::FromBrowserContext(web_contents->GetBrowserContext()); | 92 Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
246 const ExtensionService* extension_service = | 93 const ExtensionService* extension_service = |
247 ExtensionSystem::Get(profile)->extension_service(); | 94 ExtensionSystem::Get(profile)->extension_service(); |
248 const ExtensionSet* extensions = extension_service->extensions(); | 95 const ExtensionSet* extensions = extension_service->extensions(); |
249 | 96 |
250 for (ExecutingScriptsMap::const_iterator it = extension_ids.begin(); | 97 for (ExecutingScriptsMap::const_iterator it = extension_ids.begin(); |
251 it != extension_ids.end(); ++it) { | 98 it != extension_ids.end(); ++it) { |
252 const Extension* extension = extensions->GetByID(it->first); | 99 const Extension* extension = extensions->GetByID(it->first); |
253 if (!extension || !HasObservers(extension)) | 100 if (!extension || !HasObservers(extension)) |
254 continue; | 101 continue; |
255 | 102 |
256 // If OnScriptsExecuted is fired because of tabs.executeScript, the list | 103 for (std::set<std::string>::const_iterator it2 = it->second.begin(); |
257 // of content scripts will be empty. We don't want to log it because | 104 it2 != it->second.end(); ++it2) { |
258 // the call to tabs.executeScript will have already been logged anyway. | 105 std::vector<std::string> messages; |
259 if (!it->second.empty()) { | 106 messages.push_back(on_url.spec()); |
260 std::string ext_scripts_str = ""; | 107 messages.push_back(*it2); |
261 for (std::set<std::string>::const_iterator it2 = it->second.begin(); | 108 Log(extension, ActivityLog::ACTIVITY_CONTENT_SCRIPT, messages); |
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 ""); | |
272 } | 109 } |
273 } | 110 } |
274 } | 111 } |
275 | 112 |
276 void ActivityLog::KillActivityLogDatabase() { | |
277 if (db_.get()) { | |
278 ScheduleAndForget(&ActivityDatabase::KillDatabase); | |
279 } | |
280 } | |
281 | |
282 // static | 113 // static |
283 const char* ActivityLog::ActivityToString(Activity activity) { | 114 const char* ActivityLog::ActivityToString(Activity activity) { |
284 switch (activity) { | 115 switch (activity) { |
285 case ActivityLog::ACTIVITY_EXTENSION_API_CALL: | 116 case ActivityLog::ACTIVITY_EXTENSION_API_CALL: |
286 return "api_call"; | 117 return "api_call"; |
287 case ActivityLog::ACTIVITY_EXTENSION_API_BLOCK: | 118 case ActivityLog::ACTIVITY_EXTENSION_API_BLOCK: |
288 return "api_block"; | 119 return "api_block"; |
289 case ActivityLog::ACTIVITY_CONTENT_SCRIPT: | 120 case ActivityLog::ACTIVITY_CONTENT_SCRIPT: |
290 return "content_script"; | 121 return "content_script"; |
291 default: | 122 default: |
292 NOTREACHED(); | 123 NOTREACHED(); |
293 return ""; | 124 return ""; |
294 } | 125 } |
295 } | 126 } |
296 | 127 |
297 } // namespace extensions | 128 } // namespace extensions |
OLD | NEW |