1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_EXTENSIONS_ACTIVITY_LOG_ACTIVITY_LOG_H_
#define CHROME_BROWSER_EXTENSIONS_ACTIVITY_LOG_ACTIVITY_LOG_H_
#include <stdint.h>
#include <map>
#include <string>
#include <vector>
#include "base/functional/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/observer_list_threadsafe.h"
#include "base/scoped_observation.h"
#include "base/threading/thread.h"
#include "chrome/browser/extensions/activity_log/activity_actions.h"
#include "chrome/browser/extensions/activity_log/activity_log_policy.h"
#include "extensions/browser/browser_context_keyed_api_factory.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_registry_observer.h"
#include "extensions/browser/script_executor.h"
#include "extensions/buildflags/buildflags.h"
#include "extensions/common/dom_action_types.h"
static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
class Profile;
namespace content {
class BrowserContext;
}
namespace user_prefs {
class PrefRegistrySyncable;
}
namespace extensions {
class Extension;
class ExtensionSystem;
// A utility for tracing interesting activity for each extension.
// It writes to an ActivityDatabase on a separate thread to record the activity.
// Each profile has different extensions, so we keep a different database for
// each profile.
//
class ActivityLog : public BrowserContextKeyedAPI,
public ExtensionRegistryObserver {
public:
// Observers can listen for activity events. There is probably only one
// observer: the activityLogPrivate API.
class Observer {
public:
virtual void OnExtensionActivity(scoped_refptr<Action> activity) = 0;
};
explicit ActivityLog(content::BrowserContext* context);
~ActivityLog() override;
ActivityLog(const ActivityLog&) = delete;
ActivityLog& operator=(const ActivityLog&) = delete;
static BrowserContextKeyedAPIFactory<ActivityLog>* GetFactoryInstance();
// ActivityLog is a KeyedService, so don't instantiate it with
// the constructor; use GetInstance instead.
static ActivityLog* GetInstance(content::BrowserContext* context);
// Invoked when a ContentScript is executed.
void OnScriptsExecuted(content::WebContents* web_contents,
const ExecutingScriptsMap& extension_ids,
const GURL& on_url);
// Observe tabs.executeScript on the given `executor`.
void ObserveScripts(ScriptExecutor* executor);
// Add/remove observer: the activityLogPrivate API only listens when the
// ActivityLog extension is registered for an event.
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
// Logs an extension action: passes it to any installed policy to be logged
// to the database, to any observers, and logs to the console if in testing
// mode.
void LogAction(scoped_refptr<Action> action);
// Returns true if an event for the given extension should be logged.
bool ShouldLog(const std::string& extension_id) const;
// Gets all actions that match the specified fields. URLs are treated like
// prefixes; other fields are exact matches. Empty strings are not matched to
// anything. For daysAgo, today is 0, yesterday is 1, etc.; a negative number
// of days is treated as a missing parameter.
void GetFilteredActions(
const std::string& extension_id,
const Action::ActionType type,
const std::string& api_name,
const std::string& page_url,
const std::string& arg_url,
const int days_ago,
base::OnceCallback<
void(std::unique_ptr<std::vector<scoped_refptr<Action>>>)> callback);
// ExtensionRegistryObserver.
// We keep track of whether the allowlisted extension is installed; if it is,
// we want to recompute whether to have logging enabled.
void OnExtensionLoaded(content::BrowserContext* browser_context,
const Extension* extension) override;
void OnExtensionUnloaded(content::BrowserContext* browser_context,
const Extension* extension,
UnloadedExtensionReason reason) override;
void OnExtensionUninstalled(content::BrowserContext* browser_context,
const Extension* extension,
extensions::UninstallReason reason) override;
static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
// Remove actions from the activity log database which IDs specified in the
// action_ids array.
void RemoveActions(const std::vector<int64_t>& action_ids);
// Remove all actions from the activity log database with the specified
// extension_id.
void RemoveExtensionData(const std::string& extension_id);
// Clean up URLs from the activity log database.
// If restrict_urls is empty then all URLs in the activity log database are
// removed, otherwise only those in restrict_urls are removed.
void RemoveURLs(const std::vector<GURL>& restrict_urls);
void RemoveURLs(const std::set<GURL>& restrict_urls);
void RemoveURL(const GURL& url);
// Deletes the database associated with the policy that's currently in use.
void DeleteDatabase();
bool is_active() const { return is_active_; }
// If we're in a browser test, we need to pretend that the watchdog app is
// active.
void SetWatchdogAppActiveForTesting(bool active);
bool has_listeners() const { return has_listeners_; }
void SetHasListeners(bool has_listeners);
private:
friend class ActivityLogTest;
friend class BrowserContextKeyedAPIFactory<ActivityLog>;
// Specifies if the Watchdog app is active (installed & enabled).
// If so, we need to log to the database and stream to the API.
// TODO(kelvinjiang): eliminate this check if possible to simplify logic and
// for the deprecation of the Chrome Apps & Extensions Developer Tool.
bool IsWatchdogAppActive();
// Specifies if we need to record actions to the db. If so, we need to log to
// the database. This is true if the Watchdog app is active *or* the
// --enable-extension-activity-logging flag is set.
bool IsDatabaseEnabled();
// Updates cached_consumer_count_ to be active_consumers_ and stores the value
// in prefs.
void UpdateCachedConsumerCount();
// At the moment, ActivityLog will use only one policy for summarization.
// These methods are used to choose and set the most appropriate policy.
// Changing policies at runtime is not recommended, and likely only should be
// done for unit tests.
void ChooseDatabasePolicy();
void SetDatabasePolicy(ActivityLogPolicy::PolicyType policy_type);
// Checks the current `is_active_` state and modifies it if appropriate.
// If `use_cached` is true, then this checks the cached_consumer_count_ for
// whether or not a consumer is active. Otherwise, checks active_consumers_.
void CheckActive(bool use_cached);
// Called once the ExtensionSystem is ready.
void OnExtensionSystemReady();
// BrowserContextKeyedAPI implementation.
static const char* service_name() { return "ActivityLog"; }
static const bool kServiceRedirectedInIncognito = true;
static const bool kServiceIsCreatedWithBrowserContext = false;
typedef base::ObserverListThreadSafe<Observer> ObserverList;
scoped_refptr<ObserverList> observers_;
// Policy objects are owned by the ActivityLog, but cannot be scoped_ptrs
// since they may need to do some cleanup work on the database thread.
// Calling policy->Close() will free the object; see the comments on the
// ActivityDatabase class for full details.
// The database policy object takes care of recording & looking up data:
// data summarization, compression, and logging. There should only be a
// database_policy_ if the Watchdog app is installed or flag is set.
raw_ptr<ActivityLogDatabasePolicy, DanglingUntriaged> database_policy_;
ActivityLogPolicy::PolicyType database_policy_type_;
raw_ptr<Profile> profile_;
raw_ptr<ExtensionSystem> extension_system_;
bool db_enabled_; // Whether logging to disk is currently enabled.
// testing_mode_ controls which policy is selected.
// * By default, we choose a policy that doesn't log most arguments to avoid
// saving too much data. We also elide some arguments for privacy reasons.
// * In testing mode, we choose a policy that logs all arguments.
// testing_mode_ also causes us to print to the console.
bool testing_mode_;
// Used to track whether the allowlisted extension is installed. If it's
// added or removed, enabled_ may change.
base::ScopedObservation<extensions::ExtensionRegistry,
extensions::ExtensionRegistryObserver>
extension_registry_observation_{this};
// The number of active consumers of the activity log.
// TODO(kelvinjiang): eliminate this flag if possible and use has_listeners_
// instead to simplify logic.
int active_consumers_;
// The cached number of consumers of the activity log. Maintained by the
// kWatchdogExtensionActive pref variable, and updated on startup. We cache
// the result so that we can record extension actions that happen before
// all extensions have finished loading.
int cached_consumer_count_;
// Whether there are listeners on the browser side for the onExtensionActivity
// event.
bool has_listeners_;
// True if the activity log is currently active, meaning that the user has
// either added the commandline switch or has loaded a compatible extension.
// While inactive, the activity log will not store any actions for performance
// reasons.
bool is_active_;
base::WeakPtrFactory<ActivityLog> weak_factory_{this};
FRIEND_TEST_ALL_PREFIXES(ActivityLogApiTest, TriggerEvent);
FRIEND_TEST_ALL_PREFIXES(ActivityLogEnabledTest, AppAndCommandLine);
FRIEND_TEST_ALL_PREFIXES(ActivityLogEnabledTest, CommandLineSwitch);
FRIEND_TEST_ALL_PREFIXES(ActivityLogEnabledTest, NoSwitch);
FRIEND_TEST_ALL_PREFIXES(ActivityLogEnabledTest, PrefSwitch);
FRIEND_TEST_ALL_PREFIXES(ActivityLogEnabledTest, WatchdogSwitch);
};
template <>
void BrowserContextKeyedAPIFactory<ActivityLog>::DeclareFactoryDependencies();
} // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_ACTIVITY_LOG_ACTIVITY_LOG_H_
|