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 <set> | 5 #include <set> |
6 #include <vector> | 6 #include <vector> |
7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
8 #include "base/json/json_string_value_serializer.h" | 8 #include "base/json/json_string_value_serializer.h" |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/strings/string_util.h" | 10 #include "base/strings/string_util.h" |
11 #include "base/threading/thread_checker.h" | 11 #include "base/threading/thread_checker.h" |
12 #include "chrome/browser/extensions/activity_log/activity_log.h" | 12 #include "chrome/browser/extensions/activity_log/activity_log.h" |
13 #include "chrome/browser/extensions/activity_log/api_actions.h" | 13 #include "chrome/browser/extensions/activity_log/api_actions.h" |
14 #include "chrome/browser/extensions/activity_log/blocked_actions.h" | 14 #include "chrome/browser/extensions/activity_log/blocked_actions.h" |
| 15 #include "chrome/browser/extensions/activity_log/stream_noargs_ui_policy.h" |
15 #include "chrome/browser/extensions/api/activity_log_private/activity_log_privat
e_api.h" | 16 #include "chrome/browser/extensions/api/activity_log_private/activity_log_privat
e_api.h" |
16 #include "chrome/browser/extensions/extension_service.h" | 17 #include "chrome/browser/extensions/extension_service.h" |
17 #include "chrome/browser/extensions/extension_system.h" | 18 #include "chrome/browser/extensions/extension_system.h" |
18 #include "chrome/browser/extensions/extension_system_factory.h" | 19 #include "chrome/browser/extensions/extension_system_factory.h" |
19 #include "chrome/browser/extensions/install_tracker_factory.h" | 20 #include "chrome/browser/extensions/install_tracker_factory.h" |
20 #include "chrome/browser/prerender/prerender_manager.h" | 21 #include "chrome/browser/prerender/prerender_manager.h" |
21 #include "chrome/browser/prerender/prerender_manager_factory.h" | 22 #include "chrome/browser/prerender/prerender_manager_factory.h" |
22 #include "chrome/browser/profiles/incognito_helpers.h" | 23 #include "chrome/browser/profiles/incognito_helpers.h" |
23 #include "chrome/common/chrome_constants.h" | 24 #include "chrome/common/chrome_constants.h" |
24 #include "chrome/common/chrome_switches.h" | 25 #include "chrome/common/chrome_switches.h" |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
121 BrowserContextDependencyManager::GetInstance()) { | 122 BrowserContextDependencyManager::GetInstance()) { |
122 DependsOn(ExtensionSystemFactory::GetInstance()); | 123 DependsOn(ExtensionSystemFactory::GetInstance()); |
123 DependsOn(InstallTrackerFactory::GetInstance()); | 124 DependsOn(InstallTrackerFactory::GetInstance()); |
124 } | 125 } |
125 | 126 |
126 ActivityLogFactory::~ActivityLogFactory() { | 127 ActivityLogFactory::~ActivityLogFactory() { |
127 } | 128 } |
128 | 129 |
129 // ActivityLog | 130 // ActivityLog |
130 | 131 |
| 132 void ActivityLog::SetDefaultPolicy(ActivityLogPolicy::PolicyType policy_type) { |
| 133 if (policy_type != policy_type_ && IsLogEnabled()) { |
| 134 delete policy_; |
| 135 switch (policy_type) { |
| 136 case ActivityLogPolicy::POLICY_FULLSTREAM: |
| 137 policy_ = new FullStreamUIPolicy(profile_); |
| 138 break; |
| 139 case ActivityLogPolicy::POLICY_NOARGS: |
| 140 policy_ = new StreamWithoutArgsUIPolicy(profile_); |
| 141 break; |
| 142 default: |
| 143 if (testing_mode_) |
| 144 LOG(INFO) << "Calling SetDefaultPolicy with invalid policy type?"; |
| 145 } |
| 146 policy_type_ = policy_type; |
| 147 } |
| 148 } |
| 149 |
131 // Use GetInstance instead of directly creating an ActivityLog. | 150 // Use GetInstance instead of directly creating an ActivityLog. |
132 ActivityLog::ActivityLog(Profile* profile) | 151 ActivityLog::ActivityLog(Profile* profile) |
133 : profile_(profile), | 152 : policy_(NULL), |
| 153 policy_type_(ActivityLogPolicy::POLICY_INVALID), |
| 154 profile_(profile), |
134 first_time_checking_(true), | 155 first_time_checking_(true), |
135 tracker_(NULL), | 156 has_threads_(true), |
136 has_threads_(true) { | 157 tracker_(NULL) { |
137 enabled_ = IsLogEnabledOnAnyProfile(); | 158 enabled_ = IsLogEnabledOnAnyProfile(); |
138 | 159 |
139 // enable-extension-activity-log-testing | |
140 // This controls whether arguments are collected. | |
141 // It also controls whether logging statements are printed. | |
142 testing_mode_ = CommandLine::ForCurrentProcess()->HasSwitch( | |
143 switches::kEnableExtensionActivityLogTesting); | |
144 if (!testing_mode_) { | |
145 for (int i = 0; i < APIAction::kSizeAlwaysLog; i++) { | |
146 arg_whitelist_api_.insert(std::string(APIAction::kAlwaysLog[i])); | |
147 } | |
148 } | |
149 | |
150 // Check that the right threads exist. If not, we shouldn't try to do things | 160 // Check that the right threads exist. If not, we shouldn't try to do things |
151 // that require them. | 161 // that require them. |
152 if (!BrowserThread::IsMessageLoopValid(BrowserThread::DB) || | 162 if (!BrowserThread::IsMessageLoopValid(BrowserThread::DB) || |
153 !BrowserThread::IsMessageLoopValid(BrowserThread::FILE) || | 163 !BrowserThread::IsMessageLoopValid(BrowserThread::FILE) || |
154 !BrowserThread::IsMessageLoopValid(BrowserThread::IO)) { | 164 !BrowserThread::IsMessageLoopValid(BrowserThread::IO)) { |
155 LOG(ERROR) << "Missing threads, disabling Activity Logging!"; | 165 LOG(ERROR) << "Missing threads, disabling Activity Logging!"; |
156 has_threads_ = false; | 166 has_threads_ = false; |
157 } | 167 } |
158 | 168 |
159 observers_ = new ObserverListThreadSafe<Observer>; | 169 observers_ = new ObserverListThreadSafe<Observer>; |
160 | |
161 // We initialize the database whether or not the AL is enabled, since we might | |
162 // be enabled later on. If the database cannot be initialized for some | |
163 // reason, we keep chugging along but nothing will get recorded. If the UI is | |
164 // available, things will still get sent to the UI even if nothing | |
165 // is being written to the database. | |
166 db_ = new ActivityDatabase(); | |
167 if (!has_threads_) return; | |
168 base::FilePath base_dir = profile->GetPath(); | |
169 base::FilePath database_name = base_dir.Append( | |
170 chrome::kExtensionActivityLogFilename); | |
171 ScheduleAndForget(&ActivityDatabase::Init, database_name); | |
172 } | 170 } |
173 | 171 |
174 void ActivityLog::Shutdown() { | 172 void ActivityLog::Shutdown() { |
175 if (!first_time_checking_ && tracker_) tracker_->RemoveObserver(this); | 173 if (!first_time_checking_ && tracker_) tracker_->RemoveObserver(this); |
176 } | 174 } |
177 | 175 |
178 ActivityLog::~ActivityLog() { | 176 ActivityLog::~ActivityLog() { |
179 if (has_threads_) | 177 delete policy_; |
180 ScheduleAndForget(&ActivityDatabase::Close); | |
181 else | |
182 db_->Close(); | |
183 } | 178 } |
184 | 179 |
185 // We can't register for the InstallTrackerFactory events or talk to the | 180 // We can't register for the InstallTrackerFactory events or talk to the |
186 // extension service in the constructor, so we do that here the first time | 181 // extension service in the constructor, so we do that here the first time |
187 // this is called (as identified by first_time_checking_). | 182 // this is called (as identified by first_time_checking_). |
188 bool ActivityLog::IsLogEnabled() { | 183 bool ActivityLog::IsLogEnabled() { |
189 if (!first_time_checking_) return enabled_; | 184 if (!first_time_checking_) return enabled_; |
190 if (!has_threads_) return false; | 185 if (!has_threads_) return false; |
191 tracker_ = InstallTrackerFactory::GetForProfile(profile_); | 186 tracker_ = InstallTrackerFactory::GetForProfile(profile_); |
192 tracker_->AddObserver(this); | 187 tracker_->AddObserver(this); |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
243 const std::string& api_call, | 238 const std::string& api_call, |
244 ListValue* args, | 239 ListValue* args, |
245 const std::string& extra, | 240 const std::string& extra, |
246 const APIAction::Type type) { | 241 const APIAction::Type type) { |
247 std::string verb, manager; | 242 std::string verb, manager; |
248 bool matches = RE2::FullMatch(api_call, "(.*?)\\.(.*)", &manager, &verb); | 243 bool matches = RE2::FullMatch(api_call, "(.*?)\\.(.*)", &manager, &verb); |
249 if (matches) { | 244 if (matches) { |
250 if (!args->empty() && manager == "tabs") { | 245 if (!args->empty() && manager == "tabs") { |
251 APIAction::LookupTabId(api_call, args, profile_); | 246 APIAction::LookupTabId(api_call, args, profile_); |
252 } | 247 } |
| 248 |
| 249 if (policy_) { |
| 250 scoped_ptr<base::DictionaryValue> details(new DictionaryValue()); |
| 251 std::string key = policy_->GetKey(ActivityLogPolicy::PARAM_KEY_EXTRA); |
| 252 details->SetString(key, extra); |
| 253 DCHECK((type == APIAction::CALL || type == APIAction::EVENT_CALLBACK) && |
| 254 "Unexpected APIAction call type."); |
| 255 policy_->ProcessAction( |
| 256 type == APIAction::CALL ? ActivityLogPolicy::ACTION_API : |
| 257 ActivityLogPolicy::ACTION_EVENT, |
| 258 extension_id, |
| 259 api_call, |
| 260 GURL(), |
| 261 args, |
| 262 details.get()); |
| 263 } |
| 264 |
| 265 // TODO(felt) Logging should be done more efficiently, so that it |
| 266 // doesn't require construction of the action object. |
253 scoped_refptr<APIAction> action = new APIAction( | 267 scoped_refptr<APIAction> action = new APIAction( |
254 extension_id, | 268 extension_id, |
255 base::Time::Now(), | 269 base::Time::Now(), |
256 type, | 270 type, |
257 api_call, | 271 api_call, |
258 MakeArgList(args), | 272 MakeArgList(args), |
259 extra); | 273 extra); |
260 ScheduleAndForget(&ActivityDatabase::RecordAction, action); | 274 |
261 observers_->Notify(&Observer::OnExtensionActivity, action); | 275 observers_->Notify(&Observer::OnExtensionActivity, action); |
262 if (testing_mode_) LOG(INFO) << action->PrintForDebug(); | 276 if (testing_mode_) LOG(INFO) << action->PrintForDebug(); |
263 } else { | 277 } else { |
264 LOG(ERROR) << "Unknown API call! " << api_call; | 278 LOG(ERROR) << "Unknown API call! " << api_call; |
265 } | 279 } |
266 } | 280 } |
267 | 281 |
268 // A wrapper around LogAPIActionInternal, but we know it's an API call. | 282 // A wrapper around LogAPIActionInternal, but we know it's an API call. |
269 void ActivityLog::LogAPIAction(const std::string& extension_id, | 283 void ActivityLog::LogAPIAction(const std::string& extension_id, |
270 const std::string& api_call, | 284 const std::string& api_call, |
271 ListValue* args, | 285 ListValue* args, |
272 const std::string& extra) { | 286 const std::string& extra) { |
273 if (!IsLogEnabled() || | 287 if (!IsLogEnabled() || |
274 ActivityLogAPI::IsExtensionWhitelisted(extension_id)) return; | 288 ActivityLogAPI::IsExtensionWhitelisted(extension_id)) return; |
275 if (!testing_mode_ && | |
276 arg_whitelist_api_.find(api_call) == arg_whitelist_api_.end()) | |
277 args->Clear(); | |
278 LogAPIActionInternal(extension_id, | 289 LogAPIActionInternal(extension_id, |
279 api_call, | 290 api_call, |
280 args, | 291 args, |
281 extra, | 292 extra, |
282 APIAction::CALL); | 293 APIAction::CALL); |
283 } | 294 } |
284 | 295 |
285 // A wrapper around LogAPIActionInternal, but we know it's actually an event | 296 // A wrapper around LogAPIActionInternal, but we know it's actually an event |
286 // being fired and triggering extension code. Having the two separate methods | 297 // being fired and triggering extension code. Having the two separate methods |
287 // (LogAPIAction vs LogEventAction) lets us hide how we actually choose to | 298 // (LogAPIAction vs LogEventAction) lets us hide how we actually choose to |
288 // handle them. Right now they're being handled almost the same. | 299 // handle them. Right now they're being handled almost the same. |
289 void ActivityLog::LogEventAction(const std::string& extension_id, | 300 void ActivityLog::LogEventAction(const std::string& extension_id, |
290 const std::string& api_call, | 301 const std::string& api_call, |
291 ListValue* args, | 302 ListValue* args, |
292 const std::string& extra) { | 303 const std::string& extra) { |
293 if (!IsLogEnabled() || | 304 if (!IsLogEnabled() || |
294 ActivityLogAPI::IsExtensionWhitelisted(extension_id)) return; | 305 ActivityLogAPI::IsExtensionWhitelisted(extension_id)) return; |
295 if (!testing_mode_ && | |
296 arg_whitelist_api_.find(api_call) == arg_whitelist_api_.end()) | |
297 args->Clear(); | |
298 LogAPIActionInternal(extension_id, | 306 LogAPIActionInternal(extension_id, |
299 api_call, | 307 api_call, |
300 args, | 308 args, |
301 extra, | 309 extra, |
302 APIAction::EVENT_CALLBACK); | 310 APIAction::EVENT_CALLBACK); |
303 } | 311 } |
304 | 312 |
305 void ActivityLog::LogBlockedAction(const std::string& extension_id, | 313 void ActivityLog::LogBlockedAction(const std::string& extension_id, |
306 const std::string& blocked_call, | 314 const std::string& blocked_call, |
307 ListValue* args, | 315 ListValue* args, |
308 BlockedAction::Reason reason, | 316 BlockedAction::Reason reason, |
309 const std::string& extra) { | 317 const std::string& extra) { |
310 if (!IsLogEnabled() || | 318 if (!IsLogEnabled() || |
311 ActivityLogAPI::IsExtensionWhitelisted(extension_id)) return; | 319 ActivityLogAPI::IsExtensionWhitelisted(extension_id)) return; |
312 if (!testing_mode_ && | 320 |
313 arg_whitelist_api_.find(blocked_call) == arg_whitelist_api_.end()) | 321 if (policy_) { |
314 args->Clear(); | 322 scoped_ptr<base::DictionaryValue> details(new DictionaryValue()); |
| 323 std::string key = policy_->GetKey(ActivityLogPolicy::PARAM_KEY_REASON); |
| 324 details->SetInteger(key, static_cast<int>(reason)); |
| 325 key = policy_->GetKey(ActivityLogPolicy::PARAM_KEY_EXTRA); |
| 326 details->SetString(key, extra); |
| 327 policy_->ProcessAction( |
| 328 ActivityLogPolicy::ACTION_BLOCKED, |
| 329 extension_id, |
| 330 blocked_call, |
| 331 GURL(), |
| 332 args, |
| 333 details.get()); |
| 334 } |
| 335 |
315 scoped_refptr<BlockedAction> action = new BlockedAction(extension_id, | 336 scoped_refptr<BlockedAction> action = new BlockedAction(extension_id, |
316 base::Time::Now(), | 337 base::Time::Now(), |
317 blocked_call, | 338 blocked_call, |
318 MakeArgList(args), | 339 MakeArgList(args), |
319 reason, | 340 reason, |
320 extra); | 341 extra); |
321 ScheduleAndForget(&ActivityDatabase::RecordAction, action); | |
322 observers_->Notify(&Observer::OnExtensionActivity, action); | 342 observers_->Notify(&Observer::OnExtensionActivity, action); |
323 if (testing_mode_) LOG(INFO) << action->PrintForDebug(); | 343 if (testing_mode_) LOG(INFO) << action->PrintForDebug(); |
324 } | 344 } |
325 | 345 |
326 void ActivityLog::LogDOMAction(const std::string& extension_id, | 346 void ActivityLog::LogDOMAction(const std::string& extension_id, |
327 const GURL& url, | 347 const GURL& url, |
328 const string16& url_title, | 348 const string16& url_title, |
329 const std::string& api_call, | 349 const std::string& api_call, |
330 const ListValue* args, | 350 const ListValue* args, |
331 DomActionType::Type call_type, | 351 DomActionType::Type call_type, |
332 const std::string& extra) { | 352 const std::string& extra) { |
333 if (!IsLogEnabled() || | 353 if (!IsLogEnabled() || |
334 ActivityLogAPI::IsExtensionWhitelisted(extension_id)) return; | 354 ActivityLogAPI::IsExtensionWhitelisted(extension_id)) return; |
335 if (call_type == DomActionType::METHOD && api_call == "XMLHttpRequest.open") | 355 if (call_type == DomActionType::METHOD && api_call == "XMLHttpRequest.open") |
336 call_type = DomActionType::XHR; | 356 call_type = DomActionType::XHR; |
| 357 |
| 358 if (policy_) { |
| 359 scoped_ptr<base::DictionaryValue> details(new DictionaryValue()); |
| 360 std::string key = policy_->GetKey(ActivityLogPolicy::PARAM_KEY_DOM_ACTION); |
| 361 details->SetInteger(key, static_cast<int>(call_type)); |
| 362 key = policy_->GetKey(ActivityLogPolicy::PARAM_KEY_URL_TITLE); |
| 363 details->SetString(key, url_title); |
| 364 key = policy_->GetKey(ActivityLogPolicy::PARAM_KEY_EXTRA); |
| 365 details->SetString(key, extra); |
| 366 policy_->ProcessAction( |
| 367 ActivityLogPolicy::ACTION_DOM, |
| 368 extension_id, |
| 369 api_call, |
| 370 url, |
| 371 args, |
| 372 details.get()); |
| 373 } |
| 374 |
| 375 |
| 376 // TODO(felt) Logging should be done more efficiently, so that it |
| 377 // doesn't require construction of the action object. |
337 scoped_refptr<DOMAction> action = new DOMAction( | 378 scoped_refptr<DOMAction> action = new DOMAction( |
338 extension_id, | 379 extension_id, |
339 base::Time::Now(), | 380 base::Time::Now(), |
340 call_type, | 381 call_type, |
341 url, | 382 url, |
342 url_title, | 383 url_title, |
343 api_call, | 384 api_call, |
344 MakeArgList(args), | 385 MakeArgList(args), |
345 extra); | 386 extra); |
346 ScheduleAndForget(&ActivityDatabase::RecordAction, action); | |
347 observers_->Notify(&Observer::OnExtensionActivity, action); | 387 observers_->Notify(&Observer::OnExtensionActivity, action); |
348 if (testing_mode_) LOG(INFO) << action->PrintForDebug(); | 388 if (testing_mode_) LOG(INFO) << action->PrintForDebug(); |
349 } | 389 } |
350 | 390 |
351 void ActivityLog::LogWebRequestAction(const std::string& extension_id, | 391 void ActivityLog::LogWebRequestAction(const std::string& extension_id, |
352 const GURL& url, | 392 const GURL& url, |
353 const std::string& api_call, | 393 const std::string& api_call, |
354 scoped_ptr<DictionaryValue> details, | 394 scoped_ptr<DictionaryValue> details, |
355 const std::string& extra) { | 395 const std::string& extra) { |
356 string16 null_title; | 396 string16 null_title; |
357 if (!IsLogEnabled() || | 397 if (!IsLogEnabled() || |
358 ActivityLogAPI::IsExtensionWhitelisted(extension_id)) return; | 398 ActivityLogAPI::IsExtensionWhitelisted(extension_id)) return; |
359 | 399 |
360 // Strip details of the web request modifications (for privacy reasons), | 400 std::string details_string; |
361 // unless testing is enabled. | 401 if (policy_) { |
362 if (!testing_mode_) { | 402 scoped_ptr<base::DictionaryValue> details(new DictionaryValue()); |
363 DictionaryValue::Iterator details_iterator(*details); | 403 std::string key = policy_->GetKey( |
364 while (!details_iterator.IsAtEnd()) { | 404 ActivityLogPolicy::PARAM_KEY_DETAILS_STRING); |
365 details->SetBoolean(details_iterator.key(), true); | 405 details->SetString(key, details_string); |
366 details_iterator.Advance(); | 406 key = policy_->GetKey(ActivityLogPolicy::PARAM_KEY_EXTRA); |
367 } | 407 details->SetString(key, extra); |
| 408 policy_->ProcessAction( |
| 409 ActivityLogPolicy::ACTION_WEB_REQUEST, |
| 410 extension_id, |
| 411 api_call, |
| 412 url, |
| 413 NULL, |
| 414 details.get()); |
368 } | 415 } |
369 std::string details_string; | 416 |
370 JSONStringValueSerializer serializer(&details_string); | 417 JSONStringValueSerializer serializer(&details_string); |
371 serializer.SerializeAndOmitBinaryValues(*details); | 418 serializer.SerializeAndOmitBinaryValues(*details); |
372 | 419 |
| 420 // TODO(felt) Logging should be done more efficiently, so that it |
| 421 // doesn't require construction of the action object. |
373 scoped_refptr<DOMAction> action = new DOMAction( | 422 scoped_refptr<DOMAction> action = new DOMAction( |
374 extension_id, | 423 extension_id, |
375 base::Time::Now(), | 424 base::Time::Now(), |
376 DomActionType::WEBREQUEST, | 425 DomActionType::WEBREQUEST, |
377 url, | 426 url, |
378 null_title, | 427 null_title, |
379 api_call, | 428 api_call, |
380 details_string, | 429 details_string, |
381 extra); | 430 extra); |
382 ScheduleAndForget(&ActivityDatabase::RecordAction, action); | |
383 observers_->Notify(&Observer::OnExtensionActivity, action); | 431 observers_->Notify(&Observer::OnExtensionActivity, action); |
384 if (testing_mode_) LOG(INFO) << action->PrintForDebug(); | 432 if (testing_mode_) LOG(INFO) << action->PrintForDebug(); |
385 } | 433 } |
386 | 434 |
387 void ActivityLog::GetActions( | 435 void ActivityLog::GetActions( |
388 const std::string& extension_id, | 436 const std::string& extension_id, |
389 const int day, | 437 const int day, |
390 const base::Callback | 438 const base::Callback |
391 <void(scoped_ptr<std::vector<scoped_refptr<Action> > >)>& callback) { | 439 <void(scoped_ptr<std::vector<scoped_refptr<Action> > >)>& callback) { |
392 if (!has_threads_) return; | 440 if (policy_) { |
393 BrowserThread::PostTaskAndReplyWithResult( | 441 policy_->ReadData(extension_id, day, callback); |
394 BrowserThread::DB, | 442 } |
395 FROM_HERE, | |
396 base::Bind(&ActivityDatabase::GetActions, | |
397 base::Unretained(db_), | |
398 extension_id, | |
399 day), | |
400 callback); | |
401 } | 443 } |
402 | 444 |
403 void ActivityLog::OnScriptsExecuted( | 445 void ActivityLog::OnScriptsExecuted( |
404 const content::WebContents* web_contents, | 446 const content::WebContents* web_contents, |
405 const ExecutingScriptsMap& extension_ids, | 447 const ExecutingScriptsMap& extension_ids, |
406 int32 on_page_id, | 448 int32 on_page_id, |
407 const GURL& on_url) { | 449 const GURL& on_url) { |
408 if (!IsLogEnabled()) return; | 450 if (!IsLogEnabled()) return; |
409 Profile* profile = | 451 Profile* profile = |
410 Profile::FromBrowserContext(web_contents->GetBrowserContext()); | 452 Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
444 web_contents->GetTitle(), | 486 web_contents->GetTitle(), |
445 std::string(), // no api call here | 487 std::string(), // no api call here |
446 script_names.get(), | 488 script_names.get(), |
447 DomActionType::INSERTED, | 489 DomActionType::INSERTED, |
448 extra); | 490 extra); |
449 } | 491 } |
450 } | 492 } |
451 } | 493 } |
452 | 494 |
453 } // namespace extensions | 495 } // namespace extensions |
OLD | NEW |