Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(329)

Side by Side Diff: chrome/browser/extensions/activity_log.cc

Issue 11421192: Save extension activity log to a file. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Another small fix Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698