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 3 // found in the LICENSE file. 4 5 #include "chrome/browser/extensions/activity_log/activity_log.h" 6 7 #include <set> 8 #include <vector> 9 10 #include "base/command_line.h" 11 #include "base/json/json_string_value_serializer.h" 12 #include "base/logging.h" 13 #include "base/strings/string_util.h" 14 #include "base/strings/utf_string_conversions.h" 15 #include "base/threading/thread_checker.h" 16 #include "chrome/browser/extensions/activity_log/activity_action_constants.h" 17 #include "chrome/browser/extensions/activity_log/counting_policy.h" 18 #include "chrome/browser/extensions/activity_log/fullstream_ui_policy.h" 19 #include "chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h" 20 #include "chrome/browser/extensions/extension_service.h" 21 #include "chrome/browser/extensions/extension_system.h" 22 #include "chrome/browser/extensions/extension_system_factory.h" 23 #include "chrome/browser/extensions/extension_tab_util.h" 24 #include "chrome/browser/extensions/install_tracker_factory.h" 25 #include "chrome/browser/prefs/pref_service_syncable.h" 26 #include "chrome/browser/prerender/prerender_manager.h" 27 #include "chrome/browser/prerender/prerender_manager_factory.h" 28 #include "chrome/browser/profiles/incognito_helpers.h" 29 #include "chrome/browser/ui/browser.h" 30 #include "chrome/common/chrome_constants.h" 31 #include "chrome/common/chrome_switches.h" 32 #include "chrome/common/pref_names.h" 33 #include "components/browser_context_keyed_service/browser_context_dependency_manager.h" 34 #include "content/public/browser/web_contents.h" 35 #include "extensions/common/extension.h" 36 #include "third_party/re2/re2/re2.h" 37 #include "url/gurl.h" 38 39 #if !defined(OS_ANDROID) 40 #include "chrome/browser/extensions/activity_log/uma_policy.h" 41 #endif 42 43 namespace constants = activity_log_constants; 44 45 namespace { 46 47 using extensions::Action; 48 using constants::kArgUrlPlaceholder; 49 50 // If DOM API methods start with this string, we flag them as being of type 51 // DomActionType::XHR. 52 const char kDomXhrPrefix[] = "XMLHttpRequest."; 53 54 // Specifies a possible action to take to get an extracted URL in the ApiInfo 55 // structure below. 56 enum Transformation { 57 NONE, 58 DICT_LOOKUP, 59 LOOKUP_TAB_ID, 60 }; 61 62 // Information about specific Chrome and DOM APIs, such as which contain 63 // arguments that should be extracted into the arg_url field of an Action. 64 struct ApiInfo { 65 // The lookup key consists of the action_type and api_name in the Action 66 // object. 67 Action::ActionType action_type; 68 const char* api_name; 69 70 // If non-negative, an index into args might contain a URL to be extracted 71 // into arg_url. 72 int arg_url_index; 73 74 // A transformation to apply to the data found at index arg_url_index in the 75 // argument list. 76 // 77 // If NONE, the data is expected to be a string which is treated as a URL. 78 // 79 // If LOOKUP_TAB_ID, the data is either an integer which is treated as a tab 80 // ID and translated (in the context of a provided Profile), or a list of tab 81 // IDs which are translated. 82 // 83 // If DICT_LOOKUP, the data is expected to be a dictionary, and 84 // arg_url_dict_path is a path (list of keys delimited by ".") where a URL 85 // string is to be found. 86 Transformation arg_url_transform; 87 const char* arg_url_dict_path; 88 }; 89 90 static const ApiInfo kApiInfoTable[] = { 91 // Tabs APIs that require tab ID translation 92 {Action::ACTION_API_CALL, "tabs.connect", 0, LOOKUP_TAB_ID, NULL}, 93 {Action::ACTION_API_CALL, "tabs.detectLanguage", 0, LOOKUP_TAB_ID, NULL}, 94 {Action::ACTION_API_CALL, "tabs.duplicate", 0, LOOKUP_TAB_ID, NULL}, 95 {Action::ACTION_API_CALL, "tabs.executeScript", 0, LOOKUP_TAB_ID, NULL}, 96 {Action::ACTION_API_CALL, "tabs.get", 0, LOOKUP_TAB_ID, NULL}, 97 {Action::ACTION_API_CALL, "tabs.insertCSS", 0, LOOKUP_TAB_ID, NULL}, 98 {Action::ACTION_API_CALL, "tabs.move", 0, LOOKUP_TAB_ID, NULL}, 99 {Action::ACTION_API_CALL, "tabs.reload", 0, LOOKUP_TAB_ID, NULL}, 100 {Action::ACTION_API_CALL, "tabs.remove", 0, LOOKUP_TAB_ID, NULL}, 101 {Action::ACTION_API_CALL, "tabs.sendMessage", 0, LOOKUP_TAB_ID, NULL}, 102 {Action::ACTION_API_CALL, "tabs.update", 0, LOOKUP_TAB_ID, NULL}, 103 104 {Action::ACTION_API_EVENT, "tabs.onUpdated", 0, LOOKUP_TAB_ID, NULL}, 105 {Action::ACTION_API_EVENT, "tabs.onMoved", 0, LOOKUP_TAB_ID, NULL}, 106 {Action::ACTION_API_EVENT, "tabs.onDetached", 0, LOOKUP_TAB_ID, NULL}, 107 {Action::ACTION_API_EVENT, "tabs.onAttached", 0, LOOKUP_TAB_ID, NULL}, 108 {Action::ACTION_API_EVENT, "tabs.onRemoved", 0, LOOKUP_TAB_ID, NULL}, 109 {Action::ACTION_API_EVENT, "tabs.onReplaced", 0, LOOKUP_TAB_ID, NULL}, 110 111 // Other APIs that accept URLs as strings 112 {Action::ACTION_API_CALL, "bookmarks.create", 0, DICT_LOOKUP, "url"}, 113 {Action::ACTION_API_CALL, "bookmarks.update", 1, DICT_LOOKUP, "url"}, 114 {Action::ACTION_API_CALL, "cookies.get", 0, DICT_LOOKUP, "url"}, 115 {Action::ACTION_API_CALL, "cookies.getAll", 0, DICT_LOOKUP, "url"}, 116 {Action::ACTION_API_CALL, "cookies.remove", 0, DICT_LOOKUP, "url"}, 117 {Action::ACTION_API_CALL, "cookies.set", 0, DICT_LOOKUP, "url"}, 118 {Action::ACTION_API_CALL, "downloads.download", 0, DICT_LOOKUP, "url"}, 119 {Action::ACTION_API_CALL, "history.addUrl", 0, DICT_LOOKUP, "url"}, 120 {Action::ACTION_API_CALL, "history.deleteUrl", 0, DICT_LOOKUP, "url"}, 121 {Action::ACTION_API_CALL, "history.getVisits", 0, DICT_LOOKUP, "url"}, 122 {Action::ACTION_API_CALL, "webstore.install", 0, NONE, NULL}, 123 {Action::ACTION_API_CALL, "windows.create", 0, DICT_LOOKUP, "url"}, 124 125 {Action::ACTION_DOM_ACCESS, "Document.location", 0, NONE, NULL}, 126 {Action::ACTION_DOM_ACCESS, "Location.assign", 0, NONE, NULL}, 127 {Action::ACTION_DOM_ACCESS, "Location.replace", 0, NONE, NULL}, 128 {Action::ACTION_DOM_ACCESS, "Window.location", 0, NONE, NULL}, 129 {Action::ACTION_DOM_ACCESS, "XMLHttpRequest.open", 1, NONE, NULL}, 130 }; 131 132 // A singleton class which provides lookups into the kApiInfoTable data 133 // structure. It inserts all data into a map on first lookup. 134 class ApiInfoDatabase { 135 public: 136 static ApiInfoDatabase* GetInstance() { 137 return Singleton<ApiInfoDatabase>::get(); 138 } 139 140 // Retrieves an ApiInfo record for the given Action type. Returns either a 141 // pointer to the record, or NULL if no such record was found. 142 const ApiInfo* Lookup(Action::ActionType action_type, 143 const std::string& api_name) const { 144 std::map<std::string, const ApiInfo*>::const_iterator i = 145 api_database_.find(api_name); 146 if (i == api_database_.end()) 147 return NULL; 148 if (i->second->action_type != action_type) 149 return NULL; 150 return i->second; 151 } 152 153 private: 154 ApiInfoDatabase() { 155 for (size_t i = 0; i < arraysize(kApiInfoTable); i++) { 156 const ApiInfo* info = &kApiInfoTable[i]; 157 api_database_[info->api_name] = info; 158 } 159 } 160 virtual ~ApiInfoDatabase() {} 161 162 // The map is keyed by API name only, since API names aren't be repeated 163 // across multiple action types in kApiInfoTable. However, the action type 164 // should still be checked before returning a positive match. 165 std::map<std::string, const ApiInfo*> api_database_; 166 167 friend struct DefaultSingletonTraits<ApiInfoDatabase>; 168 DISALLOW_COPY_AND_ASSIGN(ApiInfoDatabase); 169 }; 170 171 // Gets the URL for a given tab ID. Helper method for ExtractUrls. Returns 172 // true if able to perform the lookup. The URL is stored to *url, and 173 // *is_incognito is set to indicate whether the URL is for an incognito tab. 174 bool GetUrlForTabId(int tab_id, 175 Profile* profile, 176 GURL* url, 177 bool* is_incognito) { 178 content::WebContents* contents = NULL; 179 Browser* browser = NULL; 180 bool found = extensions::ExtensionTabUtil::GetTabById( 181 tab_id, 182 profile, 183 true, // Search incognito tabs, too. 184 &browser, 185 NULL, 186 &contents, 187 NULL); 188 189 if (found) { 190 *url = contents->GetURL(); 191 *is_incognito = browser->profile()->IsOffTheRecord(); 192 return true; 193 } else { 194 return false; 195 } 196 } 197 198 // Resolves an argument URL relative to a base page URL. If the page URL is 199 // not valid, then only absolute argument URLs are supported. 200 bool ResolveUrl(const GURL& base, const std::string& arg, GURL* arg_out) { 201 if (base.is_valid()) 202 *arg_out = base.Resolve(arg); 203 else 204 *arg_out = GURL(arg); 205 206 return arg_out->is_valid(); 207 } 208 209 // Performs processing of the Action object to extract URLs from the argument 210 // list and translate tab IDs to URLs, according to the API call metadata in 211 // kApiInfoTable. Mutates the Action object in place. There is a small chance 212 // that the tab id->URL translation could be wrong, if the tab has already been 213 // navigated by the time of invocation. 214 // 215 // Any extracted URL is stored into the arg_url field of the action, and the 216 // URL in the argument list is replaced with the marker value "<arg_url>". For 217 // APIs that take a list of tab IDs, extracts the first valid URL into arg_url 218 // and overwrites the other tab IDs in the argument list with the translated 219 // URL. 220 void ExtractUrls(scoped_refptr<Action> action, Profile* profile) { 221 const ApiInfo* api_info = ApiInfoDatabase::GetInstance()->Lookup( 222 action->action_type(), action->api_name()); 223 if (api_info == NULL) 224 return; 225 226 int url_index = api_info->arg_url_index; 227 228 if (!action->args() || url_index < 0 || 229 static_cast<size_t>(url_index) >= action->args()->GetSize()) 230 return; 231 232 // Do not overwrite an existing arg_url value in the Action, so that callers 233 // have the option of doing custom arg_url extraction. 234 if (action->arg_url().is_valid()) 235 return; 236 237 GURL arg_url; 238 bool arg_incognito = action->page_incognito(); 239 240 switch (api_info->arg_url_transform) { 241 case NONE: { 242 // No translation needed; just extract the URL directly from a raw string 243 // or from a dictionary. Succeeds if we can find a string in the 244 // argument list and that the string resolves to a valid URL. 245 std::string url_string; 246 if (action->args()->GetString(url_index, &url_string) && 247 ResolveUrl(action->page_url(), url_string, &arg_url)) { 248 action->mutable_args()->Set(url_index, 249 new StringValue(kArgUrlPlaceholder)); 250 } 251 break; 252 } 253 254 case DICT_LOOKUP: { 255 CHECK(api_info->arg_url_dict_path); 256 // Look up the URL from a dictionary at the specified location. Succeeds 257 // if we can find a dictionary in the argument list, the dictionary 258 // contains the specified key, and the corresponding value resolves to a 259 // valid URL. 260 DictionaryValue* dict = NULL; 261 std::string url_string; 262 if (action->mutable_args()->GetDictionary(url_index, &dict) && 263 dict->GetString(api_info->arg_url_dict_path, &url_string) && 264 ResolveUrl(action->page_url(), url_string, &arg_url)) { 265 dict->SetString(api_info->arg_url_dict_path, kArgUrlPlaceholder); 266 } 267 break; 268 } 269 270 case LOOKUP_TAB_ID: { 271 // Translation of tab IDs to URLs has been requested. There are two 272 // cases to consider: either a single integer or a list of integers (when 273 // multiple tabs are manipulated). 274 int tab_id; 275 base::ListValue* tab_list = NULL; 276 if (action->args()->GetInteger(url_index, &tab_id)) { 277 // Single tab ID to translate. 278 GetUrlForTabId(tab_id, profile, &arg_url, &arg_incognito); 279 if (arg_url.is_valid()) { 280 action->mutable_args()->Set(url_index, 281 new StringValue(kArgUrlPlaceholder)); 282 } 283 } else if (action->mutable_args()->GetList(url_index, &tab_list)) { 284 // A list of possible IDs to translate. Work through in reverse order 285 // so the last one translated is left in arg_url. 286 int extracted_index = -1; // Which list item is copied to arg_url? 287 for (int i = tab_list->GetSize() - 1; i >= 0; --i) { 288 if (tab_list->GetInteger(i, &tab_id) && 289 GetUrlForTabId(tab_id, profile, &arg_url, &arg_incognito)) { 290 if (!arg_incognito) 291 tab_list->Set(i, new base::StringValue(arg_url.spec())); 292 extracted_index = i; 293 } 294 } 295 if (extracted_index >= 0) 296 tab_list->Set(extracted_index, new StringValue(kArgUrlPlaceholder)); 297 } 298 break; 299 } 300 301 default: 302 NOTREACHED(); 303 } 304 305 if (arg_url.is_valid()) { 306 action->set_arg_incognito(arg_incognito); 307 action->set_arg_url(arg_url); 308 } 309 } 310 311 } // namespace 312 313 namespace extensions { 314 315 // ActivityLogFactory 316 317 ActivityLogFactory* ActivityLogFactory::GetInstance() { 318 return Singleton<ActivityLogFactory>::get(); 319 } 320 321 BrowserContextKeyedService* ActivityLogFactory::BuildServiceInstanceFor( 322 content::BrowserContext* profile) const { 323 return new ActivityLog(static_cast<Profile*>(profile)); 324 } 325 326 content::BrowserContext* ActivityLogFactory::GetBrowserContextToUse( 327 content::BrowserContext* context) const { 328 return chrome::GetBrowserContextRedirectedInIncognito(context); 329 } 330 331 ActivityLogFactory::ActivityLogFactory() 332 : BrowserContextKeyedServiceFactory( 333 "ActivityLog", 334 BrowserContextDependencyManager::GetInstance()) { 335 DependsOn(ExtensionSystemFactory::GetInstance()); 336 DependsOn(InstallTrackerFactory::GetInstance()); 337 } 338 339 // static 340 ActivityLog* ActivityLog::GetInstance(content::BrowserContext* context) { 341 return ActivityLogFactory::GetForBrowserContext(context); 342 } 343 344 ActivityLogFactory::~ActivityLogFactory() { 345 } 346 347 // ActivityLog 348 349 // SET THINGS UP. -------------------------------------------------------------- 350 351 // Use GetInstance instead of directly creating an ActivityLog. 352 ActivityLog::ActivityLog(Profile* profile) 353 : database_policy_(NULL), 354 database_policy_type_(ActivityLogPolicy::POLICY_INVALID), 355 uma_policy_(NULL), 356 profile_(profile), 357 db_enabled_(false), 358 testing_mode_(false), 359 has_threads_(true), 360 tracker_(NULL), 361 watchdog_apps_active_(0) { 362 // This controls whether logging statements are printed & which policy is set. 363 testing_mode_ = CommandLine::ForCurrentProcess()->HasSwitch( 364 switches::kEnableExtensionActivityLogTesting); 365 366 // Check if the watchdog extension is previously installed and active. 367 // It was originally a boolean, but we've had to move to an integer. Handle 368 // the legacy case. 369 // TODO(felt): In M34, remove the legacy code & old pref. 370 if (profile_->GetPrefs()->GetBoolean(prefs::kWatchdogExtensionActiveOld)) 371 profile_->GetPrefs()->SetInteger(prefs::kWatchdogExtensionActive, 1); 372 watchdog_apps_active_ = 373 profile_->GetPrefs()->GetInteger(prefs::kWatchdogExtensionActive); 374 375 observers_ = new ObserverListThreadSafe<Observer>; 376 377 // Check that the right threads exist for logging to the database. 378 // If not, we shouldn't try to do things that require them. 379 if (!BrowserThread::IsMessageLoopValid(BrowserThread::DB) || 380 !BrowserThread::IsMessageLoopValid(BrowserThread::FILE) || 381 !BrowserThread::IsMessageLoopValid(BrowserThread::IO)) { 382 has_threads_ = false; 383 } 384 385 db_enabled_ = has_threads_ 386 && (CommandLine::ForCurrentProcess()-> 387 HasSwitch(switches::kEnableExtensionActivityLogging) 388 || watchdog_apps_active_); 389 390 ExtensionSystem::Get(profile_)->ready().Post( 391 FROM_HERE, 392 base::Bind(&ActivityLog::InitInstallTracker, base::Unretained(this))); 393 394 EventRouter* event_router = ExtensionSystem::Get(profile_)->event_router(); 395 if (event_router) 396 event_router->SetEventDispatchObserver(this); 397 398 // None of this should run on Android since the AL is behind ENABLE_EXTENSION 399 // checks. However, UmaPolicy can't even compile on Android because it uses 400 // BrowserList and related classes that aren't compiled for Android. 401 #if !defined(OS_ANDROID) 402 if (!profile->IsOffTheRecord()) 403 uma_policy_ = new UmaPolicy(profile_); 404 #endif 405 406 ChooseDatabasePolicy(); 407 } 408 409 void ActivityLog::SetDatabasePolicy( 410 ActivityLogPolicy::PolicyType policy_type) { 411 if (database_policy_type_ == policy_type) 412 return; 413 if (!IsDatabaseEnabled() && !IsWatchdogAppActive()) 414 return; 415 416 // Deleting the old policy takes place asynchronously, on the database 417 // thread. Initializing a new policy below similarly happens 418 // asynchronously. Since the two operations are both queued for the 419 // database, the queue ordering should ensure that the deletion completes 420 // before database initialization occurs. 421 // 422 // However, changing policies at runtime is still not recommended, and 423 // likely only should be done for unit tests. 424 if (database_policy_) 425 database_policy_->Close(); 426 427 switch (policy_type) { 428 case ActivityLogPolicy::POLICY_FULLSTREAM: 429 database_policy_ = new FullStreamUIPolicy(profile_); 430 break; 431 case ActivityLogPolicy::POLICY_COUNTS: 432 database_policy_ = new CountingPolicy(profile_); 433 break; 434 default: 435 NOTREACHED(); 436 } 437 database_policy_->Init(); 438 database_policy_type_ = policy_type; 439 } 440 441 // SHUT DOWN. ------------------------------------------------------------------ 442 443 void ActivityLog::Shutdown() { 444 if (tracker_) tracker_->RemoveObserver(this); 445 } 446 447 ActivityLog::~ActivityLog() { 448 if (uma_policy_) 449 uma_policy_->Close(); 450 if (database_policy_) 451 database_policy_->Close(); 452 } 453 454 // MAINTAIN STATUS. ------------------------------------------------------------ 455 456 void ActivityLog::InitInstallTracker() { 457 tracker_ = InstallTrackerFactory::GetForProfile(profile_); 458 tracker_->AddObserver(this); 459 } 460 461 void ActivityLog::ChooseDatabasePolicy() { 462 if (!(IsDatabaseEnabled() || IsWatchdogAppActive())) 463 return; 464 if (testing_mode_) 465 SetDatabasePolicy(ActivityLogPolicy::POLICY_FULLSTREAM); 466 else 467 SetDatabasePolicy(ActivityLogPolicy::POLICY_COUNTS); 468 } 469 470 bool ActivityLog::IsDatabaseEnabled() { 471 // Make sure we are not enabled when there are no threads. 472 DCHECK(has_threads_ || !db_enabled_); 473 return db_enabled_; 474 } 475 476 bool ActivityLog::IsWatchdogAppActive() { 477 return (watchdog_apps_active_ > 0); 478 } 479 480 void ActivityLog::SetWatchdogAppActive(bool active) { 481 watchdog_apps_active_ = active ? 1 : 0; 482 } 483 484 void ActivityLog::OnExtensionLoaded(const Extension* extension) { 485 if (!ActivityLogAPI::IsExtensionWhitelisted(extension->id())) return; 486 if (has_threads_) 487 db_enabled_ = true; 488 watchdog_apps_active_++; 489 profile_->GetPrefs()->SetInteger(prefs::kWatchdogExtensionActive, 490 watchdog_apps_active_); 491 if (watchdog_apps_active_ == 1) 492 ChooseDatabasePolicy(); 493 } 494 495 void ActivityLog::OnExtensionUnloaded(const Extension* extension) { 496 if (!ActivityLogAPI::IsExtensionWhitelisted(extension->id())) return; 497 watchdog_apps_active_--; 498 profile_->GetPrefs()->SetInteger(prefs::kWatchdogExtensionActive, 499 watchdog_apps_active_); 500 if (watchdog_apps_active_ == 0 && 501 !CommandLine::ForCurrentProcess()->HasSwitch( 502 switches::kEnableExtensionActivityLogging)) { 503 db_enabled_ = false; 504 } 505 } 506 507 // OnExtensionUnloaded will also be called right before this. 508 void ActivityLog::OnExtensionUninstalled(const Extension* extension) { 509 if (ActivityLogAPI::IsExtensionWhitelisted(extension->id()) && 510 !CommandLine::ForCurrentProcess()->HasSwitch( 511 switches::kEnableExtensionActivityLogging) && 512 watchdog_apps_active_ == 0) { 513 DeleteDatabase(); 514 } else if (database_policy_) { 515 database_policy_->RemoveExtensionData(extension->id()); 516 } 517 } 518 519 void ActivityLog::AddObserver(ActivityLog::Observer* observer) { 520 observers_->AddObserver(observer); 521 } 522 523 void ActivityLog::RemoveObserver(ActivityLog::Observer* observer) { 524 observers_->RemoveObserver(observer); 525 } 526 527 // static 528 void ActivityLog::RegisterProfilePrefs( 529 user_prefs::PrefRegistrySyncable* registry) { 530 registry->RegisterIntegerPref( 531 prefs::kWatchdogExtensionActive, 532 false, 533 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 534 registry->RegisterBooleanPref( 535 prefs::kWatchdogExtensionActiveOld, 536 false, 537 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 538 } 539 540 // LOG ACTIONS. ---------------------------------------------------------------- 541 542 void ActivityLog::LogAction(scoped_refptr<Action> action) { 543 if (ActivityLogAPI::IsExtensionWhitelisted(action->extension_id())) 544 return; 545 546 // Perform some preprocessing of the Action data: convert tab IDs to URLs and 547 // mask out incognito URLs if appropriate. 548 ExtractUrls(action, profile_); 549 550 // Mark DOM XHR requests as such, for easier processing later. 551 if (action->action_type() == Action::ACTION_DOM_ACCESS && 552 StartsWithASCII(action->api_name(), kDomXhrPrefix, true) && 553 action->other()) { 554 DictionaryValue* other = action->mutable_other(); 555 int dom_verb = -1; 556 if (other->GetInteger(constants::kActionDomVerb, &dom_verb) && 557 dom_verb == DomActionType::METHOD) { 558 other->SetInteger(constants::kActionDomVerb, DomActionType::XHR); 559 } 560 } 561 562 if (uma_policy_) 563 uma_policy_->ProcessAction(action); 564 if (IsDatabaseEnabled() && database_policy_) 565 database_policy_->ProcessAction(action); 566 if (IsWatchdogAppActive()) 567 observers_->Notify(&Observer::OnExtensionActivity, action); 568 if (testing_mode_) 569 VLOG(1) << action->PrintForDebug(); 570 } 571 572 void ActivityLog::OnScriptsExecuted( 573 const content::WebContents* web_contents, 574 const ExecutingScriptsMap& extension_ids, 575 int32 on_page_id, 576 const GURL& on_url) { 577 Profile* profile = 578 Profile::FromBrowserContext(web_contents->GetBrowserContext()); 579 const ExtensionService* extension_service = 580 ExtensionSystem::Get(profile)->extension_service(); 581 const ExtensionSet* extensions = extension_service->extensions(); 582 const prerender::PrerenderManager* prerender_manager = 583 prerender::PrerenderManagerFactory::GetForProfile( 584 Profile::FromBrowserContext(web_contents->GetBrowserContext())); 585 586 for (ExecutingScriptsMap::const_iterator it = extension_ids.begin(); 587 it != extension_ids.end(); ++it) { 588 const Extension* extension = extensions->GetByID(it->first); 589 if (!extension || ActivityLogAPI::IsExtensionWhitelisted(extension->id())) 590 continue; 591 592 // If OnScriptsExecuted is fired because of tabs.executeScript, the list 593 // of content scripts will be empty. We don't want to log it because 594 // the call to tabs.executeScript will have already been logged anyway. 595 if (!it->second.empty()) { 596 scoped_refptr<Action> action; 597 action = new Action(extension->id(), 598 base::Time::Now(), 599 Action::ACTION_CONTENT_SCRIPT, 600 ""); // no API call here 601 action->set_page_url(on_url); 602 action->set_page_title(base::UTF16ToUTF8(web_contents->GetTitle())); 603 action->set_page_incognito( 604 web_contents->GetBrowserContext()->IsOffTheRecord()); 605 if (prerender_manager && 606 prerender_manager->IsWebContentsPrerendering(web_contents, NULL)) 607 action->mutable_other()->SetBoolean(constants::kActionPrerender, true); 608 for (std::set<std::string>::const_iterator it2 = it->second.begin(); 609 it2 != it->second.end(); 610 ++it2) { 611 action->mutable_args()->AppendString(*it2); 612 } 613 LogAction(action); 614 } 615 } 616 } 617 618 void ActivityLog::OnWillDispatchEvent(scoped_ptr<EventDispatchInfo> details) { 619 scoped_refptr<Action> action = new Action(details->extension_id, 620 base::Time::Now(), 621 Action::ACTION_API_EVENT, 622 details->event_name); 623 action->set_args(details->event_args.Pass()); 624 LogAction(action); 625 } 626 627 // LOOKUP ACTIONS. ------------------------------------------------------------- 628 629 void ActivityLog::GetFilteredActions( 630 const std::string& extension_id, 631 const Action::ActionType type, 632 const std::string& api_name, 633 const std::string& page_url, 634 const std::string& arg_url, 635 const int daysAgo, 636 const base::Callback 637 <void(scoped_ptr<std::vector<scoped_refptr<Action> > >)>& callback) { 638 if (database_policy_) { 639 database_policy_->ReadFilteredData( 640 extension_id, type, api_name, page_url, arg_url, daysAgo, callback); 641 } 642 } 643 644 // DELETE ACTIONS. ------------------------------------------------------------- 645 646 void ActivityLog::RemoveURLs(const std::vector<GURL>& restrict_urls) { 647 if (!database_policy_) 648 return; 649 database_policy_->RemoveURLs(restrict_urls); 650 } 651 652 void ActivityLog::RemoveURLs(const std::set<GURL>& restrict_urls) { 653 if (!database_policy_) 654 return; 655 656 std::vector<GURL> urls; 657 for (std::set<GURL>::const_iterator it = restrict_urls.begin(); 658 it != restrict_urls.end(); ++it) { 659 urls.push_back(*it); 660 } 661 database_policy_->RemoveURLs(urls); 662 } 663 664 void ActivityLog::RemoveURL(const GURL& url) { 665 if (url.is_empty()) 666 return; 667 std::vector<GURL> urls; 668 urls.push_back(url); 669 RemoveURLs(urls); 670 } 671 672 void ActivityLog::DeleteDatabase() { 673 if (!database_policy_) 674 return; 675 database_policy_->DeleteDatabase(); 676 } 677 678 } // namespace extensions 679