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.
5 #include "chrome/browser/extensions/activity_log/activity_log.h"
10 #include "base/command_line.h"
11 #include "base/json/json_string_value_serializer.h"
12 #include "base/lazy_instance.h"
13 #include "base/logging.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/threading/thread_checker.h"
17 #include "chrome/browser/extensions/activity_log/activity_action_constants.h"
18 #include "chrome/browser/extensions/activity_log/counting_policy.h"
19 #include "chrome/browser/extensions/activity_log/fullstream_ui_policy.h"
20 #include "chrome/browser/extensions/activity_log/uma_policy.h"
21 #include "chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h"
22 #include "chrome/browser/extensions/extension_tab_util.h"
23 #include "chrome/browser/prefs/pref_service_syncable.h"
24 #include "chrome/browser/prerender/prerender_manager.h"
25 #include "chrome/browser/prerender/prerender_manager_factory.h"
26 #include "chrome/browser/profiles/profile.h"
27 #include "chrome/browser/ui/browser.h"
28 #include "chrome/common/chrome_constants.h"
29 #include "chrome/common/chrome_switches.h"
30 #include "chrome/common/pref_names.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "content/public/browser/web_contents.h"
33 #include "extensions/browser/extension_registry.h"
34 #include "extensions/browser/extension_registry_factory.h"
35 #include "extensions/browser/extension_system.h"
36 #include "extensions/browser/extension_system_provider.h"
37 #include "extensions/browser/extensions_browser_client.h"
38 #include "extensions/common/extension.h"
39 #include "extensions/common/one_shot_event.h"
40 #include "third_party/re2/re2/re2.h"
43 namespace constants
= activity_log_constants
;
45 namespace extensions
{
49 using constants::kArgUrlPlaceholder
;
50 using content::BrowserThread
;
52 // If DOM API methods start with this string, we flag them as being of type
53 // DomActionType::XHR.
54 const char kDomXhrPrefix
[] = "XMLHttpRequest.";
56 // Specifies a possible action to take to get an extracted URL in the ApiInfo
64 // Information about specific Chrome and DOM APIs, such as which contain
65 // arguments that should be extracted into the arg_url field of an Action.
67 // The lookup key consists of the action_type and api_name in the Action
69 Action::ActionType action_type
;
72 // If non-negative, an index into args might contain a URL to be extracted
76 // A transformation to apply to the data found at index arg_url_index in the
79 // If NONE, the data is expected to be a string which is treated as a URL.
81 // If LOOKUP_TAB_ID, the data is either an integer which is treated as a tab
82 // ID and translated (in the context of a provided Profile), or a list of tab
83 // IDs which are translated.
85 // If DICT_LOOKUP, the data is expected to be a dictionary, and
86 // arg_url_dict_path is a path (list of keys delimited by ".") where a URL
87 // string is to be found.
88 Transformation arg_url_transform
;
89 const char* arg_url_dict_path
;
92 static const ApiInfo kApiInfoTable
[] = {
93 // Tabs APIs that require tab ID translation
94 {Action::ACTION_API_CALL
, "tabs.connect", 0, LOOKUP_TAB_ID
, NULL
},
95 {Action::ACTION_API_CALL
, "tabs.detectLanguage", 0, LOOKUP_TAB_ID
, NULL
},
96 {Action::ACTION_API_CALL
, "tabs.duplicate", 0, LOOKUP_TAB_ID
, NULL
},
97 {Action::ACTION_API_CALL
, "tabs.executeScript", 0, LOOKUP_TAB_ID
, NULL
},
98 {Action::ACTION_API_CALL
, "tabs.get", 0, LOOKUP_TAB_ID
, NULL
},
99 {Action::ACTION_API_CALL
, "tabs.insertCSS", 0, LOOKUP_TAB_ID
, NULL
},
100 {Action::ACTION_API_CALL
, "tabs.move", 0, LOOKUP_TAB_ID
, NULL
},
101 {Action::ACTION_API_CALL
, "tabs.reload", 0, LOOKUP_TAB_ID
, NULL
},
102 {Action::ACTION_API_CALL
, "tabs.remove", 0, LOOKUP_TAB_ID
, NULL
},
103 {Action::ACTION_API_CALL
, "tabs.sendMessage", 0, LOOKUP_TAB_ID
, NULL
},
104 {Action::ACTION_API_CALL
, "tabs.update", 0, LOOKUP_TAB_ID
, NULL
},
105 {Action::ACTION_API_EVENT
, "tabs.onUpdated", 0, LOOKUP_TAB_ID
, NULL
},
106 {Action::ACTION_API_EVENT
, "tabs.onMoved", 0, LOOKUP_TAB_ID
, NULL
},
107 {Action::ACTION_API_EVENT
, "tabs.onDetached", 0, LOOKUP_TAB_ID
, NULL
},
108 {Action::ACTION_API_EVENT
, "tabs.onAttached", 0, LOOKUP_TAB_ID
, NULL
},
109 {Action::ACTION_API_EVENT
, "tabs.onRemoved", 0, LOOKUP_TAB_ID
, NULL
},
110 {Action::ACTION_API_EVENT
, "tabs.onReplaced", 0, LOOKUP_TAB_ID
, NULL
},
112 // Other APIs that accept URLs as strings
113 {Action::ACTION_API_CALL
, "bookmarks.create", 0, DICT_LOOKUP
, "url"},
114 {Action::ACTION_API_CALL
, "bookmarks.update", 1, DICT_LOOKUP
, "url"},
115 {Action::ACTION_API_CALL
, "cookies.get", 0, DICT_LOOKUP
, "url"},
116 {Action::ACTION_API_CALL
, "cookies.getAll", 0, DICT_LOOKUP
, "url"},
117 {Action::ACTION_API_CALL
, "cookies.remove", 0, DICT_LOOKUP
, "url"},
118 {Action::ACTION_API_CALL
, "cookies.set", 0, DICT_LOOKUP
, "url"},
119 {Action::ACTION_API_CALL
, "downloads.download", 0, DICT_LOOKUP
, "url"},
120 {Action::ACTION_API_CALL
, "history.addUrl", 0, DICT_LOOKUP
, "url"},
121 {Action::ACTION_API_CALL
, "history.deleteUrl", 0, DICT_LOOKUP
, "url"},
122 {Action::ACTION_API_CALL
, "history.getVisits", 0, DICT_LOOKUP
, "url"},
123 {Action::ACTION_API_CALL
, "webstore.install", 0, NONE
, NULL
},
124 {Action::ACTION_API_CALL
, "windows.create", 0, DICT_LOOKUP
, "url"},
125 {Action::ACTION_DOM_ACCESS
, "Document.location", 0, NONE
, NULL
},
126 {Action::ACTION_DOM_ACCESS
, "HTMLAnchorElement.href", 0, NONE
, NULL
},
127 {Action::ACTION_DOM_ACCESS
, "HTMLButtonElement.formAction", 0, NONE
, NULL
},
128 {Action::ACTION_DOM_ACCESS
, "HTMLEmbedElement.src", 0, NONE
, NULL
},
129 {Action::ACTION_DOM_ACCESS
, "HTMLFormElement.action", 0, NONE
, NULL
},
130 {Action::ACTION_DOM_ACCESS
, "HTMLFrameElement.src", 0, NONE
, NULL
},
131 {Action::ACTION_DOM_ACCESS
, "HTMLHtmlElement.manifest", 0, NONE
, NULL
},
132 {Action::ACTION_DOM_ACCESS
, "HTMLIFrameElement.src", 0, NONE
, NULL
},
133 {Action::ACTION_DOM_ACCESS
, "HTMLImageElement.longDesc", 0, NONE
, NULL
},
134 {Action::ACTION_DOM_ACCESS
, "HTMLImageElement.src", 0, NONE
, NULL
},
135 {Action::ACTION_DOM_ACCESS
, "HTMLImageElement.lowsrc", 0, NONE
, NULL
},
136 {Action::ACTION_DOM_ACCESS
, "HTMLInputElement.formAction", 0, NONE
, NULL
},
137 {Action::ACTION_DOM_ACCESS
, "HTMLInputElement.src", 0, NONE
, NULL
},
138 {Action::ACTION_DOM_ACCESS
, "HTMLLinkElement.href", 0, NONE
, NULL
},
139 {Action::ACTION_DOM_ACCESS
, "HTMLMediaElement.src", 0, NONE
, NULL
},
140 {Action::ACTION_DOM_ACCESS
, "HTMLMediaElement.currentSrc", 0, NONE
, NULL
},
141 {Action::ACTION_DOM_ACCESS
, "HTMLModElement.cite", 0, NONE
, NULL
},
142 {Action::ACTION_DOM_ACCESS
, "HTMLObjectElement.data", 0, NONE
, NULL
},
143 {Action::ACTION_DOM_ACCESS
, "HTMLQuoteElement.cite", 0, NONE
, NULL
},
144 {Action::ACTION_DOM_ACCESS
, "HTMLScriptElement.src", 0, NONE
, NULL
},
145 {Action::ACTION_DOM_ACCESS
, "HTMLSourceElement.src", 0, NONE
, NULL
},
146 {Action::ACTION_DOM_ACCESS
, "HTMLTrackElement.src", 0, NONE
, NULL
},
147 {Action::ACTION_DOM_ACCESS
, "HTMLVideoElement.poster", 0, NONE
, NULL
},
148 {Action::ACTION_DOM_ACCESS
, "Location.assign", 0, NONE
, NULL
},
149 {Action::ACTION_DOM_ACCESS
, "Location.replace", 0, NONE
, NULL
},
150 {Action::ACTION_DOM_ACCESS
, "Window.location", 0, NONE
, NULL
},
151 {Action::ACTION_DOM_ACCESS
, "XMLHttpRequest.open", 1, NONE
, NULL
}};
153 // A singleton class which provides lookups into the kApiInfoTable data
154 // structure. It inserts all data into a map on first lookup.
155 class ApiInfoDatabase
{
157 static ApiInfoDatabase
* GetInstance() {
158 return Singleton
<ApiInfoDatabase
>::get();
161 // Retrieves an ApiInfo record for the given Action type. Returns either a
162 // pointer to the record, or NULL if no such record was found.
163 const ApiInfo
* Lookup(Action::ActionType action_type
,
164 const std::string
& api_name
) const {
165 std::map
<std::string
, const ApiInfo
*>::const_iterator i
=
166 api_database_
.find(api_name
);
167 if (i
== api_database_
.end())
169 if (i
->second
->action_type
!= action_type
)
176 for (size_t i
= 0; i
< arraysize(kApiInfoTable
); i
++) {
177 const ApiInfo
* info
= &kApiInfoTable
[i
];
178 api_database_
[info
->api_name
] = info
;
181 virtual ~ApiInfoDatabase() {}
183 // The map is keyed by API name only, since API names aren't be repeated
184 // across multiple action types in kApiInfoTable. However, the action type
185 // should still be checked before returning a positive match.
186 std::map
<std::string
, const ApiInfo
*> api_database_
;
188 friend struct DefaultSingletonTraits
<ApiInfoDatabase
>;
189 DISALLOW_COPY_AND_ASSIGN(ApiInfoDatabase
);
192 // Gets the URL for a given tab ID. Helper method for ExtractUrls. Returns
193 // true if able to perform the lookup. The URL is stored to *url, and
194 // *is_incognito is set to indicate whether the URL is for an incognito tab.
195 bool GetUrlForTabId(int tab_id
,
198 bool* is_incognito
) {
199 content::WebContents
* contents
= NULL
;
200 Browser
* browser
= NULL
;
201 bool found
= ExtensionTabUtil::GetTabById(
204 true, // Search incognito tabs, too.
211 *url
= contents
->GetURL();
212 *is_incognito
= browser
->profile()->IsOffTheRecord();
219 // Resolves an argument URL relative to a base page URL. If the page URL is
220 // not valid, then only absolute argument URLs are supported.
221 bool ResolveUrl(const GURL
& base
, const std::string
& arg
, GURL
* arg_out
) {
223 *arg_out
= base
.Resolve(arg
);
225 *arg_out
= GURL(arg
);
227 return arg_out
->is_valid();
230 // Performs processing of the Action object to extract URLs from the argument
231 // list and translate tab IDs to URLs, according to the API call metadata in
232 // kApiInfoTable. Mutates the Action object in place. There is a small chance
233 // that the tab id->URL translation could be wrong, if the tab has already been
234 // navigated by the time of invocation.
236 // Any extracted URL is stored into the arg_url field of the action, and the
237 // URL in the argument list is replaced with the marker value "<arg_url>". For
238 // APIs that take a list of tab IDs, extracts the first valid URL into arg_url
239 // and overwrites the other tab IDs in the argument list with the translated
241 void ExtractUrls(scoped_refptr
<Action
> action
, Profile
* profile
) {
242 const ApiInfo
* api_info
= ApiInfoDatabase::GetInstance()->Lookup(
243 action
->action_type(), action
->api_name());
244 if (api_info
== NULL
)
247 int url_index
= api_info
->arg_url_index
;
249 if (!action
->args() || url_index
< 0 ||
250 static_cast<size_t>(url_index
) >= action
->args()->GetSize())
253 // Do not overwrite an existing arg_url value in the Action, so that callers
254 // have the option of doing custom arg_url extraction.
255 if (action
->arg_url().is_valid())
259 bool arg_incognito
= action
->page_incognito();
261 switch (api_info
->arg_url_transform
) {
263 // No translation needed; just extract the URL directly from a raw string
264 // or from a dictionary. Succeeds if we can find a string in the
265 // argument list and that the string resolves to a valid URL.
266 std::string url_string
;
267 if (action
->args()->GetString(url_index
, &url_string
) &&
268 ResolveUrl(action
->page_url(), url_string
, &arg_url
)) {
269 action
->mutable_args()->Set(url_index
,
270 new base::StringValue(kArgUrlPlaceholder
));
276 CHECK(api_info
->arg_url_dict_path
);
277 // Look up the URL from a dictionary at the specified location. Succeeds
278 // if we can find a dictionary in the argument list, the dictionary
279 // contains the specified key, and the corresponding value resolves to a
281 base::DictionaryValue
* dict
= NULL
;
282 std::string url_string
;
283 if (action
->mutable_args()->GetDictionary(url_index
, &dict
) &&
284 dict
->GetString(api_info
->arg_url_dict_path
, &url_string
) &&
285 ResolveUrl(action
->page_url(), url_string
, &arg_url
)) {
286 dict
->SetString(api_info
->arg_url_dict_path
, kArgUrlPlaceholder
);
291 case LOOKUP_TAB_ID
: {
292 // Translation of tab IDs to URLs has been requested. There are two
293 // cases to consider: either a single integer or a list of integers (when
294 // multiple tabs are manipulated).
296 base::ListValue
* tab_list
= NULL
;
297 if (action
->args()->GetInteger(url_index
, &tab_id
)) {
298 // Single tab ID to translate.
299 GetUrlForTabId(tab_id
, profile
, &arg_url
, &arg_incognito
);
300 if (arg_url
.is_valid()) {
301 action
->mutable_args()->Set(
302 url_index
, new base::StringValue(kArgUrlPlaceholder
));
304 } else if (action
->mutable_args()->GetList(url_index
, &tab_list
)) {
305 // A list of possible IDs to translate. Work through in reverse order
306 // so the last one translated is left in arg_url.
307 int extracted_index
= -1; // Which list item is copied to arg_url?
308 for (int i
= tab_list
->GetSize() - 1; i
>= 0; --i
) {
309 if (tab_list
->GetInteger(i
, &tab_id
) &&
310 GetUrlForTabId(tab_id
, profile
, &arg_url
, &arg_incognito
)) {
312 tab_list
->Set(i
, new base::StringValue(arg_url
.spec()));
316 if (extracted_index
>= 0) {
318 extracted_index
, new base::StringValue(kArgUrlPlaceholder
));
328 if (arg_url
.is_valid()) {
329 action
->set_arg_incognito(arg_incognito
);
330 action
->set_arg_url(arg_url
);
336 // SET THINGS UP. --------------------------------------------------------------
338 static base::LazyInstance
<BrowserContextKeyedAPIFactory
<ActivityLog
> >
339 g_factory
= LAZY_INSTANCE_INITIALIZER
;
341 BrowserContextKeyedAPIFactory
<ActivityLog
>* ActivityLog::GetFactoryInstance() {
342 return g_factory
.Pointer();
346 ActivityLog
* ActivityLog::GetInstance(content::BrowserContext
* context
) {
347 return ActivityLog::GetFactoryInstance()->Get(
348 Profile::FromBrowserContext(context
));
351 // Use GetInstance instead of directly creating an ActivityLog.
352 ActivityLog::ActivityLog(content::BrowserContext
* context
)
353 : database_policy_(NULL
),
354 database_policy_type_(ActivityLogPolicy::POLICY_INVALID
),
356 profile_(Profile::FromBrowserContext(context
)),
358 testing_mode_(false),
360 extension_registry_observer_(this),
361 watchdog_apps_active_(0) {
362 // This controls whether logging statements are printed & which policy is set.
363 testing_mode_
= base::CommandLine::ForCurrentProcess()->HasSwitch(
364 switches::kEnableExtensionActivityLogTesting
);
366 // Check if the watchdog extension is previously installed and active.
367 watchdog_apps_active_
=
368 profile_
->GetPrefs()->GetInteger(prefs::kWatchdogExtensionActive
);
370 observers_
= new base::ObserverListThreadSafe
<Observer
>;
372 // Check that the right threads exist for logging to the database.
373 // If not, we shouldn't try to do things that require them.
374 if (!BrowserThread::IsMessageLoopValid(BrowserThread::DB
) ||
375 !BrowserThread::IsMessageLoopValid(BrowserThread::FILE) ||
376 !BrowserThread::IsMessageLoopValid(BrowserThread::IO
)) {
377 has_threads_
= false;
381 has_threads_
&& (base::CommandLine::ForCurrentProcess()->HasSwitch(
382 switches::kEnableExtensionActivityLogging
) ||
383 watchdog_apps_active_
);
385 extension_registry_observer_
.Add(ExtensionRegistry::Get(profile_
));
387 if (!profile_
->IsOffTheRecord())
388 uma_policy_
= new UmaPolicy(profile_
);
390 ChooseDatabasePolicy();
393 void ActivityLog::SetDatabasePolicy(
394 ActivityLogPolicy::PolicyType policy_type
) {
395 if (database_policy_type_
== policy_type
)
397 if (!IsDatabaseEnabled() && !IsWatchdogAppActive())
400 // Deleting the old policy takes place asynchronously, on the database
401 // thread. Initializing a new policy below similarly happens
402 // asynchronously. Since the two operations are both queued for the
403 // database, the queue ordering should ensure that the deletion completes
404 // before database initialization occurs.
406 // However, changing policies at runtime is still not recommended, and
407 // likely only should be done for unit tests.
408 if (database_policy_
)
409 database_policy_
->Close();
411 switch (policy_type
) {
412 case ActivityLogPolicy::POLICY_FULLSTREAM
:
413 database_policy_
= new FullStreamUIPolicy(profile_
);
415 case ActivityLogPolicy::POLICY_COUNTS
:
416 database_policy_
= new CountingPolicy(profile_
);
421 database_policy_
->Init();
422 database_policy_type_
= policy_type
;
425 ActivityLog::~ActivityLog() {
427 uma_policy_
->Close();
428 if (database_policy_
)
429 database_policy_
->Close();
432 // MAINTAIN STATUS. ------------------------------------------------------------
434 void ActivityLog::ChooseDatabasePolicy() {
435 if (!(IsDatabaseEnabled() || IsWatchdogAppActive()))
438 SetDatabasePolicy(ActivityLogPolicy::POLICY_FULLSTREAM
);
440 SetDatabasePolicy(ActivityLogPolicy::POLICY_COUNTS
);
443 bool ActivityLog::IsDatabaseEnabled() {
444 // Make sure we are not enabled when there are no threads.
445 DCHECK(has_threads_
|| !db_enabled_
);
449 bool ActivityLog::IsWatchdogAppActive() {
450 return (watchdog_apps_active_
> 0);
453 void ActivityLog::SetWatchdogAppActiveForTesting(bool active
) {
454 watchdog_apps_active_
= active
? 1 : 0;
457 void ActivityLog::OnExtensionLoaded(content::BrowserContext
* browser_context
,
458 const Extension
* extension
) {
459 if (!ActivityLogAPI::IsExtensionWhitelisted(extension
->id())) return;
462 watchdog_apps_active_
++;
463 profile_
->GetPrefs()->SetInteger(prefs::kWatchdogExtensionActive
,
464 watchdog_apps_active_
);
465 if (watchdog_apps_active_
== 1)
466 ChooseDatabasePolicy();
469 void ActivityLog::OnExtensionUnloaded(content::BrowserContext
* browser_context
,
470 const Extension
* extension
,
471 UnloadedExtensionInfo::Reason reason
) {
472 if (!ActivityLogAPI::IsExtensionWhitelisted(extension
->id())) return;
473 watchdog_apps_active_
--;
474 profile_
->GetPrefs()->SetInteger(prefs::kWatchdogExtensionActive
,
475 watchdog_apps_active_
);
476 if (watchdog_apps_active_
== 0 &&
477 !base::CommandLine::ForCurrentProcess()->HasSwitch(
478 switches::kEnableExtensionActivityLogging
)) {
483 // OnExtensionUnloaded will also be called right before this.
484 void ActivityLog::OnExtensionUninstalled(
485 content::BrowserContext
* browser_context
,
486 const Extension
* extension
,
487 extensions::UninstallReason reason
) {
488 if (ActivityLogAPI::IsExtensionWhitelisted(extension
->id()) &&
489 !base::CommandLine::ForCurrentProcess()->HasSwitch(
490 switches::kEnableExtensionActivityLogging
) &&
491 watchdog_apps_active_
== 0) {
493 } else if (database_policy_
) {
494 database_policy_
->RemoveExtensionData(extension
->id());
498 void ActivityLog::AddObserver(ActivityLog::Observer
* observer
) {
499 observers_
->AddObserver(observer
);
502 void ActivityLog::RemoveObserver(ActivityLog::Observer
* observer
) {
503 observers_
->RemoveObserver(observer
);
507 void ActivityLog::RegisterProfilePrefs(
508 user_prefs::PrefRegistrySyncable
* registry
) {
509 registry
->RegisterIntegerPref(prefs::kWatchdogExtensionActive
, false);
512 // LOG ACTIONS. ----------------------------------------------------------------
514 void ActivityLog::LogAction(scoped_refptr
<Action
> action
) {
515 if (ActivityLogAPI::IsExtensionWhitelisted(action
->extension_id()))
518 // Perform some preprocessing of the Action data: convert tab IDs to URLs and
519 // mask out incognito URLs if appropriate.
520 ExtractUrls(action
, profile_
);
522 // Mark DOM XHR requests as such, for easier processing later.
523 if (action
->action_type() == Action::ACTION_DOM_ACCESS
&&
524 base::StartsWith(action
->api_name(), kDomXhrPrefix
,
525 base::CompareCase::SENSITIVE
) &&
527 base::DictionaryValue
* other
= action
->mutable_other();
529 if (other
->GetInteger(constants::kActionDomVerb
, &dom_verb
) &&
530 dom_verb
== DomActionType::METHOD
) {
531 other
->SetInteger(constants::kActionDomVerb
, DomActionType::XHR
);
536 uma_policy_
->ProcessAction(action
);
537 if (IsDatabaseEnabled() && database_policy_
)
538 database_policy_
->ProcessAction(action
);
539 if (IsWatchdogAppActive())
540 observers_
->Notify(FROM_HERE
, &Observer::OnExtensionActivity
, action
);
542 VLOG(1) << action
->PrintForDebug();
545 void ActivityLog::OnScriptsExecuted(
546 const content::WebContents
* web_contents
,
547 const ExecutingScriptsMap
& extension_ids
,
548 const GURL
& on_url
) {
550 Profile::FromBrowserContext(web_contents
->GetBrowserContext());
551 ExtensionRegistry
* registry
= ExtensionRegistry::Get(profile
);
552 for (ExecutingScriptsMap::const_iterator it
= extension_ids
.begin();
553 it
!= extension_ids
.end(); ++it
) {
554 const Extension
* extension
=
555 registry
->GetExtensionById(it
->first
, ExtensionRegistry::ENABLED
);
556 if (!extension
|| ActivityLogAPI::IsExtensionWhitelisted(extension
->id()))
559 // If OnScriptsExecuted is fired because of tabs.executeScript, the list
560 // of content scripts will be empty. We don't want to log it because
561 // the call to tabs.executeScript will have already been logged anyway.
562 if (!it
->second
.empty()) {
563 scoped_refptr
<Action
> action
;
564 action
= new Action(extension
->id(),
566 Action::ACTION_CONTENT_SCRIPT
,
567 ""); // no API call here
568 action
->set_page_url(on_url
);
569 action
->set_page_title(base::UTF16ToUTF8(web_contents
->GetTitle()));
570 action
->set_page_incognito(
571 web_contents
->GetBrowserContext()->IsOffTheRecord());
573 const prerender::PrerenderManager
* prerender_manager
=
574 prerender::PrerenderManagerFactory::GetForProfile(profile
);
575 if (prerender_manager
&&
576 prerender_manager
->IsWebContentsPrerendering(web_contents
, NULL
))
577 action
->mutable_other()->SetBoolean(constants::kActionPrerender
, true);
578 for (std::set
<std::string
>::const_iterator it2
= it
->second
.begin();
579 it2
!= it
->second
.end();
581 action
->mutable_args()->AppendString(*it2
);
588 void ActivityLog::OnApiEventDispatched(const std::string
& extension_id
,
589 const std::string
& event_name
,
590 scoped_ptr
<base::ListValue
> event_args
) {
591 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
592 scoped_refptr
<Action
> action
= new Action(extension_id
,
594 Action::ACTION_API_EVENT
,
596 action
->set_args(event_args
.Pass());
600 void ActivityLog::OnApiFunctionCalled(const std::string
& extension_id
,
601 const std::string
& api_name
,
602 scoped_ptr
<base::ListValue
> args
) {
603 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
604 scoped_refptr
<Action
> action
= new Action(extension_id
,
606 Action::ACTION_API_CALL
,
608 action
->set_args(args
.Pass());
612 // LOOKUP ACTIONS. -------------------------------------------------------------
614 void ActivityLog::GetFilteredActions(
615 const std::string
& extension_id
,
616 const Action::ActionType type
,
617 const std::string
& api_name
,
618 const std::string
& page_url
,
619 const std::string
& arg_url
,
622 <void(scoped_ptr
<std::vector
<scoped_refptr
<Action
> > >)>& callback
) {
623 if (database_policy_
) {
624 database_policy_
->ReadFilteredData(
625 extension_id
, type
, api_name
, page_url
, arg_url
, daysAgo
, callback
);
629 // DELETE ACTIONS. -------------------------------------------------------------
631 void ActivityLog::RemoveActions(const std::vector
<int64
>& action_ids
) {
632 if (!database_policy_
)
634 database_policy_
->RemoveActions(action_ids
);
637 void ActivityLog::RemoveURLs(const std::vector
<GURL
>& restrict_urls
) {
638 if (!database_policy_
)
640 database_policy_
->RemoveURLs(restrict_urls
);
643 void ActivityLog::RemoveURLs(const std::set
<GURL
>& restrict_urls
) {
644 if (!database_policy_
)
647 std::vector
<GURL
> urls
;
648 for (std::set
<GURL
>::const_iterator it
= restrict_urls
.begin();
649 it
!= restrict_urls
.end(); ++it
) {
652 database_policy_
->RemoveURLs(urls
);
655 void ActivityLog::RemoveURL(const GURL
& url
) {
658 std::vector
<GURL
> urls
;
663 void ActivityLog::DeleteDatabase() {
664 if (!database_policy_
)
666 database_policy_
->DeleteDatabase();
670 void BrowserContextKeyedAPIFactory
<ActivityLog
>::DeclareFactoryDependencies() {
671 DependsOn(ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
672 DependsOn(ExtensionRegistryFactory::GetInstance());
675 } // namespace extensions