This implements half of the activityLogPrivate API. The API is only available to a single whitelisted extension. It includes the schema and events. It is missing the implementation of its function, which I'll add as a separate CL.
BUG=241672
Review URL: https://chromiumcodereview.appspot.com/16061002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@204796 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/extensions/activity_log/activity_actions.cc b/chrome/browser/extensions/activity_log/activity_actions.cc
index b6195af..7cff66d 100644
--- a/chrome/browser/extensions/activity_log/activity_actions.cc
+++ b/chrome/browser/extensions/activity_log/activity_actions.cc
@@ -5,18 +5,22 @@
#include <string>
#include "base/logging.h"
#include "base/stringprintf.h"
-#include "chrome/browser/extensions/activity_log/api_actions.h"
+#include "chrome/browser/extensions/activity_log/activity_actions.h"
namespace extensions {
+using api::activity_log_private::ExtensionActivity;
+
const char* Action::kTableBasicFields =
"extension_id LONGVARCHAR NOT NULL, "
"time INTEGER NOT NULL";
Action::Action(const std::string& extension_id,
- const base::Time& time)
+ const base::Time& time,
+ ExtensionActivity::ActivityType activity_type)
: extension_id_(extension_id),
- time_(time) {}
+ time_(time),
+ activity_type_(activity_type) {}
// static
bool Action::InitializeTableInternal(sql::Connection* db,
diff --git a/chrome/browser/extensions/activity_log/activity_actions.h b/chrome/browser/extensions/activity_log/activity_actions.h
index da7c4999..547d2d8 100644
--- a/chrome/browser/extensions/activity_log/activity_actions.h
+++ b/chrome/browser/extensions/activity_log/activity_actions.h
@@ -9,6 +9,7 @@
#include "base/memory/ref_counted_memory.h"
#include "base/time.h"
#include "base/values.h"
+#include "chrome/common/extensions/api/activity_log_private.h"
#include "sql/connection.h"
#include "sql/statement.h"
#include "sql/transaction.h"
@@ -27,14 +28,22 @@
// Record the action in the database.
virtual void Record(sql::Connection* db) = 0;
+ // Flatten the activity's type-specific fields into an ExtensionActivity.
+ virtual scoped_ptr<api::activity_log_private::ExtensionActivity>
+ ConvertToExtensionActivity() = 0;
+
// Print an action as a regular string for debugging purposes.
virtual std::string PrintForDebug() = 0;
const std::string& extension_id() const { return extension_id_; }
const base::Time& time() const { return time_; }
+ api::activity_log_private::ExtensionActivity::ActivityType activity_type()
+ const { return activity_type_; }
protected:
- Action(const std::string& extension_id, const base::Time& time);
+ Action(const std::string& extension_id,
+ const base::Time& time,
+ api::activity_log_private::ExtensionActivity::ActivityType type);
virtual ~Action() {}
// Initialize the table for a given action type.
@@ -53,6 +62,7 @@
std::string extension_id_;
base::Time time_;
+ api::activity_log_private::ExtensionActivity::ActivityType activity_type_;
DISALLOW_COPY_AND_ASSIGN(Action);
};
diff --git a/chrome/browser/extensions/activity_log/activity_database_unittest.cc b/chrome/browser/extensions/activity_log/activity_database_unittest.cc
index d53e785c..e101b1a 100644
--- a/chrome/browser/extensions/activity_log/activity_database_unittest.cc
+++ b/chrome/browser/extensions/activity_log/activity_database_unittest.cc
@@ -45,11 +45,6 @@
test_user_manager_.reset(new chromeos::ScopedTestUserManager());
#endif
CommandLine command_line(CommandLine::NO_PROGRAM);
- profile_ =
- Profile::FromBrowserContext(web_contents()->GetBrowserContext());
- extension_service_ = static_cast<TestExtensionSystem*>(
- ExtensionSystem::Get(profile_))->CreateExtensionService(
- &command_line, base::FilePath(), false);
CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kEnableExtensionActivityLogTesting);
}
@@ -61,10 +56,6 @@
ChromeRenderViewHostTestHarness::TearDown();
}
- protected:
- ExtensionService* extension_service_;
- Profile* profile_;
-
private:
#if defined OS_CHROMEOS
chromeos::ScopedStubCrosEnabler stub_cros_enabler_;
@@ -217,9 +208,9 @@
activity_db->RecordAction(extra_dom_action);
// Read them back
- std::string api_print = "ID: punky, CATEGORY: CALL, "
+ std::string api_print = "ID: punky, CATEGORY: call, "
"API: brewster, ARGS: woof";
- std::string dom_print = "DOM API CALL: lets, ARGS: vamoose, VERB: MODIFIED";
+ std::string dom_print = "DOM API CALL: lets, ARGS: vamoose, VERB: modified";
scoped_ptr<std::vector<scoped_refptr<Action> > > actions =
activity_db->GetActions("punky", 0);
ASSERT_EQ(2, static_cast<int>(actions->size()));
@@ -288,9 +279,9 @@
activity_db->RecordAction(tooold_dom_action);
// Read them back
- std::string api_print = "ID: punky, CATEGORY: CALL, "
+ std::string api_print = "ID: punky, CATEGORY: call, "
"API: brewster, ARGS: woof";
- std::string dom_print = "DOM API CALL: lets, ARGS: vamoose, VERB: MODIFIED";
+ std::string dom_print = "DOM API CALL: lets, ARGS: vamoose, VERB: modified";
scoped_ptr<std::vector<scoped_refptr<Action> > > actions =
activity_db->GetActions("punky", 3);
ASSERT_EQ(2, static_cast<int>(actions->size()));
diff --git a/chrome/browser/extensions/activity_log/activity_log.cc b/chrome/browser/extensions/activity_log/activity_log.cc
index c02d93da..5910840 100644
--- a/chrome/browser/extensions/activity_log/activity_log.cc
+++ b/chrome/browser/extensions/activity_log/activity_log.cc
@@ -12,6 +12,7 @@
#include "chrome/browser/extensions/activity_log/activity_log.h"
#include "chrome/browser/extensions/activity_log/api_actions.h"
#include "chrome/browser/extensions/activity_log/blocked_actions.h"
+#include "chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_system.h"
#include "chrome/browser/profiles/incognito_helpers.h"
@@ -41,14 +42,6 @@
return call_signature;
}
-// Concatenate an API call with its arguments.
-std::string MakeCallSignature(const std::string& name, const ListValue* args) {
- std::string call_signature = name + "(";
- call_signature += MakeArgList(args);
- call_signature += ")";
- return call_signature;
-}
-
// Computes whether the activity log is enabled in this browser (controlled by
// command-line flags) and caches the value (which is assumed never to change).
class LogIsEnabled {
@@ -106,12 +99,9 @@
// Use GetInstance instead of directly creating an ActivityLog.
ActivityLog::ActivityLog(Profile* profile) : profile_(profile) {
- // enable-extension-activity-logging and enable-extension-activity-ui
- log_activity_to_stdout_ = CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kEnableExtensionActivityLogging);
-
// enable-extension-activity-log-testing
// This controls whether arguments are collected.
+ // It also controls whether logging statements are printed.
testing_mode_ = CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableExtensionActivityLogTesting);
if (!testing_mode_) {
@@ -130,6 +120,8 @@
dispatch_thread_ = BrowserThread::UI;
}
+ observers_ = new ObserverListThreadSafe<Observer>;
+
// If the database cannot be initialized for some reason, we keep
// chugging along but nothing will get recorded. If the UI is
// available, things will still get sent to the UI even if nothing
@@ -158,12 +150,11 @@
}
void ActivityLog::AddObserver(ActivityLog::Observer* observer) {
- if (!IsLogEnabled()) return;
- // TODO(felt) Re-implement Observer notification HERE for the API.
+ observers_->AddObserver(observer);
}
void ActivityLog::RemoveObserver(ActivityLog::Observer* observer) {
- // TODO(felt) Re-implement Observer notification HERE for the API.
+ observers_->RemoveObserver(observer);
}
void ActivityLog::LogAPIActionInternal(const std::string& extension_id,
@@ -185,9 +176,8 @@
MakeArgList(args),
extra);
ScheduleAndForget(&ActivityDatabase::RecordAction, action);
- // TODO(felt) Re-implement Observer notification HERE for the API.
- if (log_activity_to_stdout_)
- LOG(INFO) << action->PrintForDebug();
+ observers_->Notify(&Observer::OnExtensionActivity, action);
+ if (testing_mode_) LOG(INFO) << action->PrintForDebug();
} else {
LOG(ERROR) << "Unknown API call! " << api_call;
}
@@ -198,7 +188,8 @@
const std::string& api_call,
ListValue* args,
const std::string& extra) {
- if (!IsLogEnabled()) return;
+ if (!IsLogEnabled() ||
+ ActivityLogAPI::IsExtensionWhitelisted(extension_id)) return;
if (!testing_mode_ &&
arg_whitelist_api_.find(api_call) == arg_whitelist_api_.end())
args->Clear();
@@ -217,7 +208,8 @@
const std::string& api_call,
ListValue* args,
const std::string& extra) {
- if (!IsLogEnabled()) return;
+ if (!IsLogEnabled() ||
+ ActivityLogAPI::IsExtensionWhitelisted(extension_id)) return;
if (!testing_mode_ &&
arg_whitelist_api_.find(api_call) == arg_whitelist_api_.end())
args->Clear();
@@ -233,7 +225,8 @@
ListValue* args,
BlockedAction::Reason reason,
const std::string& extra) {
- if (!IsLogEnabled()) return;
+ if (!IsLogEnabled() ||
+ ActivityLogAPI::IsExtensionWhitelisted(extension_id)) return;
if (!testing_mode_ &&
arg_whitelist_api_.find(blocked_call) == arg_whitelist_api_.end())
args->Clear();
@@ -244,9 +237,8 @@
reason,
extra);
ScheduleAndForget(&ActivityDatabase::RecordAction, action);
- // TODO(felt) Re-implement Observer notification HERE for the API.
- if (log_activity_to_stdout_)
- LOG(INFO) << action->PrintForDebug();
+ observers_->Notify(&Observer::OnExtensionActivity, action);
+ if (testing_mode_) LOG(INFO) << action->PrintForDebug();
}
void ActivityLog::LogDOMAction(const std::string& extension_id,
@@ -256,7 +248,8 @@
const ListValue* args,
DomActionType::Type call_type,
const std::string& extra) {
- if (!IsLogEnabled()) return;
+ if (!IsLogEnabled() ||
+ ActivityLogAPI::IsExtensionWhitelisted(extension_id)) return;
if (call_type == DomActionType::METHOD && api_call == "XMLHttpRequest.open")
call_type = DomActionType::XHR;
scoped_refptr<DOMAction> action = new DOMAction(
@@ -269,9 +262,8 @@
MakeArgList(args),
extra);
ScheduleAndForget(&ActivityDatabase::RecordAction, action);
- // TODO(felt) Re-implement Observer notification HERE for the API.
- if (log_activity_to_stdout_)
- LOG(INFO) << action->PrintForDebug();
+ observers_->Notify(&Observer::OnExtensionActivity, action);
+ if (testing_mode_) LOG(INFO) << action->PrintForDebug();
}
void ActivityLog::LogWebRequestAction(const std::string& extension_id,
@@ -280,7 +272,8 @@
scoped_ptr<DictionaryValue> details,
const std::string& extra) {
string16 null_title;
- if (!IsLogEnabled()) return;
+ if (!IsLogEnabled() ||
+ ActivityLogAPI::IsExtensionWhitelisted(extension_id)) return;
// Strip details of the web request modifications (for privacy reasons),
// unless testing is enabled.
@@ -305,9 +298,8 @@
details_string,
extra);
ScheduleAndForget(&ActivityDatabase::RecordAction, action);
- // TODO(felt) Re-implement Observer notification HERE for the API.
- if (log_activity_to_stdout_)
- LOG(INFO) << action->PrintForDebug();
+ observers_->Notify(&Observer::OnExtensionActivity, action);
+ if (testing_mode_) LOG(INFO) << action->PrintForDebug();
}
void ActivityLog::GetActions(
@@ -340,7 +332,7 @@
for (ExecutingScriptsMap::const_iterator it = extension_ids.begin();
it != extension_ids.end(); ++it) {
const Extension* extension = extensions->GetByID(it->first);
- if (!extension)
+ if (!extension || ActivityLogAPI::IsExtensionWhitelisted(extension->id()))
continue;
// If OnScriptsExecuted is fired because of tabs.executeScript, the list
diff --git a/chrome/browser/extensions/activity_log/activity_log.h b/chrome/browser/extensions/activity_log/activity_log.h
index 2314f0e..763f0d2 100644
--- a/chrome/browser/extensions/activity_log/activity_log.h
+++ b/chrome/browser/extensions/activity_log/activity_log.h
@@ -38,7 +38,8 @@
class ActivityLog : public BrowserContextKeyedService,
public TabHelper::ScriptExecutionObserver {
public:
- // Observers can listen for activity events.
+ // 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;
@@ -57,7 +58,8 @@
// really intended for use by unit tests.
static void RecomputeLoggingIsEnabled();
- // Add/remove observer.
+ // 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);
@@ -170,6 +172,7 @@
}
typedef ObserverListThreadSafe<Observer> ObserverList;
+ scoped_refptr<ObserverList> observers_;
// The database wrapper that does the actual database I/O.
// We initialize this on the same thread as the ActivityLog, but then
@@ -182,14 +185,11 @@
// we dispatch to the UI thread.
BrowserThread::ID dispatch_thread_;
- // Whether to log activity to stdout or the UI. These are set by switches.
- bool log_activity_to_stdout_;
- bool log_activity_to_ui_;
-
// testing_mode_ controls whether to log API call arguments. By default, we
// don't log most arguments to avoid saving too much data. In testing mode,
// argument collection is enabled. We also whitelist some arguments for
// collection regardless of whether this bool is true.
+ // When testing_mode_ is enabled, we also print to the console.
bool testing_mode_;
base::hash_set<std::string> arg_whitelist_api_;
diff --git a/chrome/browser/extensions/activity_log/activity_log_browsertest.cc b/chrome/browser/extensions/activity_log/activity_log_browsertest.cc
index f0cfeb3..196ed311 100644
--- a/chrome/browser/extensions/activity_log/activity_log_browsertest.cc
+++ b/chrome/browser/extensions/activity_log/activity_log_browsertest.cc
@@ -26,6 +26,7 @@
virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
ExtensionBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitch(switches::kEnableExtensionActivityLogging);
+ command_line->AppendSwitch(switches::kEnableExtensionActivityLogTesting);
}
};
diff --git a/chrome/browser/extensions/activity_log/activity_log_unittest.cc b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
index 15f00a1..e8a3492 100644
--- a/chrome/browser/extensions/activity_log/activity_log_unittest.cc
+++ b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
@@ -76,7 +76,7 @@
scoped_refptr<Action> last = i->front();
std::string id(kExtensionId);
std::string noargs = "ID: " + id + ", CATEGORY: "
- "CALL, API: tabs.testMethod, ARGS: ";
+ "call, API: tabs.testMethod, ARGS: ";
ASSERT_EQ(noargs, last->PrintForDebug());
}
@@ -85,7 +85,7 @@
scoped_refptr<Action> last = i->front();
std::string id(kExtensionId);
std::string args = "ID: " + id + ", CATEGORY: "
- "CALL, API: extension.connect, ARGS: \"hello\", \"world\"";
+ "call, API: extension.connect, ARGS: \"hello\", \"world\"";
ASSERT_EQ(args, last->PrintForDebug());
}
diff --git a/chrome/browser/extensions/activity_log/api_actions.cc b/chrome/browser/extensions/activity_log/api_actions.cc
index e982b30..6a7cb654 100644
--- a/chrome/browser/extensions/activity_log/api_actions.cc
+++ b/chrome/browser/extensions/activity_log/api_actions.cc
@@ -96,6 +96,11 @@
namespace extensions {
+using api::activity_log_private::ExtensionActivity;
+using api::activity_log_private::DomActivityDetail;
+using api::activity_log_private::ChromeActivityDetail;
+using api::activity_log_private::BlockedChromeActivityDetail;
+
const char* APIAction::kTableName = "activitylog_apis";
const char* APIAction::kTableContentFields[] =
{"api_type", "api_call", "args", "extra"};
@@ -115,7 +120,7 @@
const std::string& api_call,
const std::string& args,
const std::string& extra)
- : Action(extension_id, time),
+ : Action(extension_id, time, ExtensionActivity::ACTIVITY_TYPE_CHROME),
type_(type),
api_call_(api_call),
args_(args),
@@ -123,7 +128,8 @@
APIAction::APIAction(const sql::Statement& s)
: Action(s.ColumnString(0),
- base::Time::FromInternalValue(s.ColumnInt64(1))),
+ base::Time::FromInternalValue(s.ColumnInt64(1)),
+ ExtensionActivity::ACTIVITY_TYPE_CHROME),
type_(static_cast<Type>(s.ColumnInt(2))),
api_call_(APINameMap::GetInstance()->ShortnameToApi(s.ColumnString(3))),
args_(s.ColumnString(4)),
@@ -132,6 +138,23 @@
APIAction::~APIAction() {
}
+scoped_ptr<ExtensionActivity> APIAction::ConvertToExtensionActivity() {
+ scoped_ptr<ExtensionActivity> formatted_activity;
+ formatted_activity.reset(new ExtensionActivity);
+ formatted_activity->extension_id.reset(
+ new std::string(extension_id()));
+ formatted_activity->activity_type = activity_type();
+ formatted_activity->time.reset(new double(time().ToJsTime()));
+ ChromeActivityDetail* details = new ChromeActivityDetail;
+ details->api_activity_type = ChromeActivityDetail::ParseApiActivityType(
+ TypeAsString());
+ details->api_call.reset(new std::string(api_call_));
+ details->args.reset(new std::string(args_));
+ details->extra.reset(new std::string(extra_));
+ formatted_activity->chrome_activity_detail.reset(details);
+ return formatted_activity.Pass();
+}
+
// static
bool APIAction::InitializeTable(sql::Connection* db) {
// The original table schema was different than the existing one.
@@ -229,11 +252,11 @@
std::string APIAction::TypeAsString() const {
switch (type_) {
case CALL:
- return "CALL";
+ return "call";
case EVENT_CALLBACK:
- return "EVENT_CALLBACK";
+ return "event_callback";
default:
- return "UNKNOWN_TYPE";
+ return "unknown_type";
}
}
diff --git a/chrome/browser/extensions/activity_log/api_actions.h b/chrome/browser/extensions/activity_log/api_actions.h
index 3744f52..1269444 100644
--- a/chrome/browser/extensions/activity_log/api_actions.h
+++ b/chrome/browser/extensions/activity_log/api_actions.h
@@ -47,6 +47,9 @@
// Record the action in the database.
virtual void Record(sql::Connection* db) OVERRIDE;
+ virtual scoped_ptr<api::activity_log_private::ExtensionActivity>
+ ConvertToExtensionActivity() OVERRIDE;
+
// Used to associate tab IDs with URLs. It will swap out the int in args with
// a URL as a string. If the tab is in incognito mode, we leave it alone as
// the original int. There is a small chance that the URL translation could
diff --git a/chrome/browser/extensions/activity_log/blocked_actions.cc b/chrome/browser/extensions/activity_log/blocked_actions.cc
index b773299..aa35f70 100644
--- a/chrome/browser/extensions/activity_log/blocked_actions.cc
+++ b/chrome/browser/extensions/activity_log/blocked_actions.cc
@@ -11,6 +11,11 @@
namespace extensions {
+using api::activity_log_private::ExtensionActivity;
+using api::activity_log_private::DomActivityDetail;
+using api::activity_log_private::ChromeActivityDetail;
+using api::activity_log_private::BlockedChromeActivityDetail;
+
const char* BlockedAction::kTableName = "activitylog_blocked";
const char* BlockedAction::kTableContentFields[] =
{"api_call", "args", "reason", "extra"};
@@ -23,7 +28,9 @@
const std::string& args,
const BlockedAction::Reason reason,
const std::string& extra)
- : Action(extension_id, time),
+ : Action(extension_id,
+ time,
+ ExtensionActivity::ACTIVITY_TYPE_BLOCKED_CHROME),
api_call_(api_call),
args_(args),
reason_(reason),
@@ -31,7 +38,8 @@
BlockedAction::BlockedAction(const sql::Statement& s)
: Action(s.ColumnString(0),
- base::Time::FromInternalValue(s.ColumnInt64(1))),
+ base::Time::FromInternalValue(s.ColumnInt64(1)),
+ ExtensionActivity::ACTIVITY_TYPE_BLOCKED_CHROME),
api_call_(s.ColumnString(2)),
args_(s.ColumnString(3)),
reason_(static_cast<Reason>(s.ColumnInt(4))),
@@ -40,6 +48,23 @@
BlockedAction::~BlockedAction() {
}
+scoped_ptr<ExtensionActivity> BlockedAction::ConvertToExtensionActivity() {
+ scoped_ptr<ExtensionActivity> formatted_activity;
+ formatted_activity.reset(new ExtensionActivity);
+ formatted_activity->extension_id.reset(
+ new std::string(extension_id()));
+ formatted_activity->activity_type = activity_type();
+ formatted_activity->time.reset(new double(time().ToJsTime()));
+ BlockedChromeActivityDetail* details = new BlockedChromeActivityDetail;
+ details->api_call.reset(new std::string(api_call_));
+ details->args.reset(new std::string(args_));
+ details->reason = BlockedChromeActivityDetail::ParseReason(
+ ReasonAsString());
+ details->extra.reset(new std::string(extra_));
+ formatted_activity->blocked_chrome_activity_detail.reset(details);
+ return formatted_activity.Pass();
+}
+
// static
bool BlockedAction::InitializeTable(sql::Connection* db) {
// The original table schema was different than the existing one.
@@ -93,11 +118,11 @@
std::string BlockedAction::ReasonAsString() const {
if (reason_ == ACCESS_DENIED)
- return std::string("access denied");
+ return std::string("access_denied");
else if (reason_ == QUOTA_EXCEEDED)
- return std::string("quota exceeded");
+ return std::string("quota_exceeded");
else
- return std::string("unknown");
+ return std::string("unknown_reason_type"); // To avoid Win header name.
}
} // namespace extensions
diff --git a/chrome/browser/extensions/activity_log/blocked_actions.h b/chrome/browser/extensions/activity_log/blocked_actions.h
index 930b70a5a..76abe95 100644
--- a/chrome/browser/extensions/activity_log/blocked_actions.h
+++ b/chrome/browser/extensions/activity_log/blocked_actions.h
@@ -43,6 +43,9 @@
// Record the action in the database.
virtual void Record(sql::Connection* db) OVERRIDE;
+ virtual scoped_ptr<api::activity_log_private::ExtensionActivity>
+ ConvertToExtensionActivity() OVERRIDE;
+
// Print a BlockedAction as a string for debugging purposes.
virtual std::string PrintForDebug() OVERRIDE;
diff --git a/chrome/browser/extensions/activity_log/dom_actions.cc b/chrome/browser/extensions/activity_log/dom_actions.cc
index d431270..ea2486c 100644
--- a/chrome/browser/extensions/activity_log/dom_actions.cc
+++ b/chrome/browser/extensions/activity_log/dom_actions.cc
@@ -13,6 +13,11 @@
namespace extensions {
+using api::activity_log_private::ExtensionActivity;
+using api::activity_log_private::DomActivityDetail;
+using api::activity_log_private::ChromeActivityDetail;
+using api::activity_log_private::BlockedChromeActivityDetail;
+
const char* DOMAction::kTableName = "activitylog_urls";
const char* DOMAction::kTableContentFields[] =
{"url_action_type", "url", "url_title", "api_call", "args", "extra"};
@@ -28,7 +33,7 @@
const std::string& api_call,
const std::string& args,
const std::string& extra)
- : Action(extension_id, time),
+ : Action(extension_id, time, ExtensionActivity::ACTIVITY_TYPE_DOM),
verb_(verb),
url_(url),
url_title_(url_title),
@@ -38,7 +43,8 @@
DOMAction::DOMAction(const sql::Statement& s)
: Action(s.ColumnString(0),
- base::Time::FromInternalValue(s.ColumnInt64(1))),
+ base::Time::FromInternalValue(s.ColumnInt64(1)),
+ ExtensionActivity::ACTIVITY_TYPE_DOM),
verb_(static_cast<DomActionType::Type>(s.ColumnInt(2))),
url_(GURL(s.ColumnString(3))),
url_title_(s.ColumnString16(4)),
@@ -49,6 +55,25 @@
DOMAction::~DOMAction() {
}
+scoped_ptr<ExtensionActivity> DOMAction::ConvertToExtensionActivity() {
+ scoped_ptr<ExtensionActivity> formatted_activity;
+ formatted_activity.reset(new ExtensionActivity);
+ formatted_activity->extension_id.reset(
+ new std::string(extension_id()));
+ formatted_activity->activity_type = activity_type();
+ formatted_activity->time.reset(new double(time().ToJsTime()));
+ DomActivityDetail* details = new DomActivityDetail;
+ details->dom_activity_type = DomActivityDetail::ParseDomActivityType(
+ VerbAsString());
+ details->url.reset(new std::string(url_.spec()));
+ details->url_title.reset(new std::string(base::UTF16ToUTF8(url_title_)));
+ details->api_call.reset(new std::string(api_call_));
+ details->args.reset(new std::string(args_));
+ details->extra.reset(new std::string(extra_));
+ formatted_activity->dom_activity_detail.reset(details);
+ return formatted_activity.Pass();
+}
+
// static
bool DOMAction::InitializeTable(sql::Connection* db) {
// The original table schema was different than the existing one.
@@ -111,19 +136,19 @@
std::string DOMAction::VerbAsString() const {
switch (verb_) {
case DomActionType::GETTER:
- return "GETTER";
+ return "getter";
case DomActionType::SETTER:
- return "SETTER";
+ return "setter";
case DomActionType::METHOD:
- return "METHOD";
+ return "method";
case DomActionType::INSERTED:
- return "INSERTED";
+ return "inserted";
case DomActionType::XHR:
- return "XHR";
+ return "xhr";
case DomActionType::WEBREQUEST:
- return "WEBREQUEST";
+ return "webrequest";
case DomActionType::MODIFIED: // legacy
- return "MODIFIED";
+ return "modified";
default:
NOTREACHED();
return NULL;
diff --git a/chrome/browser/extensions/activity_log/dom_actions.h b/chrome/browser/extensions/activity_log/dom_actions.h
index c430803..4b4825d 100644
--- a/chrome/browser/extensions/activity_log/dom_actions.h
+++ b/chrome/browser/extensions/activity_log/dom_actions.h
@@ -41,6 +41,9 @@
// Create a new DOMAction from a database row.
explicit DOMAction(const sql::Statement& s);
+ virtual scoped_ptr<api::activity_log_private::ExtensionActivity>
+ ConvertToExtensionActivity() OVERRIDE;
+
// Record the action in the database.
virtual void Record(sql::Connection* db) OVERRIDE;
diff --git a/chrome/browser/extensions/api/activity_log_private/activity_log_private_api.cc b/chrome/browser/extensions/api/activity_log_private/activity_log_private_api.cc
new file mode 100644
index 0000000..7035417
--- /dev/null
+++ b/chrome/browser/extensions/api/activity_log_private/activity_log_private_api.cc
@@ -0,0 +1,96 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h"
+
+#include "base/lazy_instance.h"
+#include "base/prefs/pref_service.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/extensions/event_router_forwarder.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_system.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/extensions/api/activity_log_private.h"
+#include "chrome/common/pref_names.h"
+
+namespace extensions {
+
+using api::activity_log_private::ExtensionActivity;
+
+const char kActivityLogExtensionId[] = "acldcpdepobcjbdanifkmfndkjoilgba";
+const char kActivityLogTestExtensionId[] = "ajabfgledjhbabeoojlabelaifmakodf";
+const char kNewActivityEventName[] = "activityLogPrivate.onExtensionActivity";
+
+static base::LazyInstance<ProfileKeyedAPIFactory<ActivityLogAPI> >
+ g_factory = LAZY_INSTANCE_INITIALIZER;
+
+// static
+ProfileKeyedAPIFactory<ActivityLogAPI>* ActivityLogAPI::GetFactoryInstance() {
+ return &g_factory.Get();
+}
+
+template<>
+void ProfileKeyedAPIFactory<ActivityLogAPI>::DeclareFactoryDependencies() {
+ DependsOn(ExtensionSystemFactory::GetInstance());
+ DependsOn(ActivityLogFactory::GetInstance());
+}
+
+ActivityLogAPI::ActivityLogAPI(Profile* profile)
+ : profile_(profile),
+ initialized_(false) {
+ if (!ExtensionSystem::Get(profile_)->event_router()) { // Check for testing.
+ LOG(ERROR) << "ExtensionSystem event_router does not exist.";
+ return;
+ }
+ activity_log_ = extensions::ActivityLog::GetInstance(profile_);
+ DCHECK(activity_log_);
+ ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
+ this, kNewActivityEventName);
+ activity_log_->AddObserver(this);
+ initialized_ = true;
+}
+
+ActivityLogAPI::~ActivityLogAPI() {
+}
+
+void ActivityLogAPI::Shutdown() {
+ if (!initialized_) { // Check for testing.
+ LOG(ERROR) << "ExtensionSystem event_router does not exist.";
+ return;
+ }
+ ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this);
+ activity_log_->RemoveObserver(this);
+}
+
+// static
+bool ActivityLogAPI::IsExtensionWhitelisted(const std::string& extension_id) {
+ return (extension_id == kActivityLogExtensionId ||
+ extension_id == kActivityLogTestExtensionId);
+}
+
+void ActivityLogAPI::OnListenerAdded(const EventListenerInfo& details) {
+ // TODO(felt): Only observe activity_log_ events when we have a customer.
+}
+
+void ActivityLogAPI::OnListenerRemoved(const EventListenerInfo& details) {
+ // TODO(felt): Only observe activity_log_ events when we have a customer.
+}
+
+void ActivityLogAPI::OnExtensionActivity(scoped_refptr<Action> activity) {
+ scoped_ptr<base::ListValue> value(new base::ListValue());
+ scoped_ptr<ExtensionActivity> activity_arg =
+ activity->ConvertToExtensionActivity();
+ value->Append(activity_arg->ToValue().release());
+ scoped_ptr<Event> event(new Event(kNewActivityEventName, value.Pass()));
+ event->restrict_to_profile = profile_;
+ ExtensionSystem::Get(profile_)->event_router()->BroadcastEvent(event.Pass());
+}
+
+bool ActivityLogPrivateGetExtensionActivitiesFunction::RunImpl() {
+ return true;
+}
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h b/chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h
new file mode 100644
index 0000000..ea74c1fd
--- /dev/null
+++ b/chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h
@@ -0,0 +1,84 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This extension API provides access to the Activity Log, which is a
+// monitoring framework for extension behavior. Only specific Google-produced
+// extensions should have access to it.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_ACTIVITY_LOG_PRIVATE_ACTIVITY_LOG_PRIVATE_API_H_
+#define CHROME_BROWSER_EXTENSIONS_API_ACTIVITY_LOG_PRIVATE_ACTIVITY_LOG_PRIVATE_API_H_
+
+#include "base/synchronization/lock.h"
+#include "chrome/browser/extensions/activity_log/activity_actions.h"
+#include "chrome/browser/extensions/activity_log/activity_log.h"
+#include "chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h"
+#include "chrome/browser/extensions/api/profile_keyed_api_factory.h"
+#include "chrome/browser/extensions/event_router.h"
+#include "chrome/browser/extensions/extension_function.h"
+
+namespace extensions {
+
+class ActivityLog;
+
+// The ID of the trusted/whitelisted ActivityLog extension.
+extern const char kActivityLogExtensionId[];
+extern const char kActivityLogTestExtensionId[];
+extern const char kNewActivityEventName[];
+
+// Handles interactions between the Activity Log API and implementation.
+class ActivityLogAPI : public ProfileKeyedAPI,
+ public extensions::ActivityLog::Observer,
+ public EventRouter::Observer {
+ public:
+ explicit ActivityLogAPI(Profile* profile);
+ virtual ~ActivityLogAPI();
+
+ // ProfileKeyedAPI implementation.
+ static ProfileKeyedAPIFactory<ActivityLogAPI>* GetFactoryInstance();
+
+ virtual void Shutdown() OVERRIDE;
+
+ // Lookup whether the extension ID is whitelisted.
+ static bool IsExtensionWhitelisted(const std::string& extension_id);
+
+ private:
+ friend class ProfileKeyedAPIFactory<ActivityLogAPI>;
+ static const char* service_name() { return "ActivityLogPrivateAPI"; }
+
+ // ActivityLog::Observer
+ // We pass this along to activityLogPrivate.onExtensionActivity.
+ virtual void OnExtensionActivity(scoped_refptr<Action> activity) OVERRIDE;
+
+ // EventRouter::Observer
+ // We only keep track of OnExtensionActivity if we have any listeners.
+ virtual void OnListenerAdded(const EventListenerInfo& details) OVERRIDE;
+ virtual void OnListenerRemoved(const EventListenerInfo& details) OVERRIDE;
+
+ Profile* profile_;
+ ActivityLog* activity_log_;
+ bool initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(ActivityLogAPI);
+};
+
+template<>
+void ProfileKeyedAPIFactory<ActivityLogAPI>::DeclareFactoryDependencies();
+
+// The implementation of activityLogPrivate.getExtensionActivities
+class ActivityLogPrivateGetExtensionActivitiesFunction
+ : public AsyncExtensionFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("activityLogPrivate.getExtensionActivities",
+ ACTIVITYLOGPRIVATE_GETEXTENSIONACTIVITIES)
+
+ protected:
+ virtual ~ActivityLogPrivateGetExtensionActivitiesFunction() {}
+
+ // ExtensionFunction:
+ virtual bool RunImpl() OVERRIDE;
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_API_ACTIVITY_LOG_PRIVATE_ACTIVITY_LOG_PRIVATE_API_H_
diff --git a/chrome/browser/extensions/api/activity_log_private/activity_log_private_api_unittest.cc b/chrome/browser/extensions/api/activity_log_private/activity_log_private_api_unittest.cc
new file mode 100644
index 0000000..6aef6142
--- /dev/null
+++ b/chrome/browser/extensions/api/activity_log_private/activity_log_private_api_unittest.cc
@@ -0,0 +1,105 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kExtensionId[] = "extensionid";
+const char kApiCall[] = "api.call";
+const char kArgs[] = "1, 2";
+const char kExtra[] = "extra";
+
+} // extensions
+
+namespace extensions {
+
+using api::activity_log_private::BlockedChromeActivityDetail;
+using api::activity_log_private::ChromeActivityDetail;
+using api::activity_log_private::DomActivityDetail;
+using api::activity_log_private::ExtensionActivity;
+
+class ActivityLogApiUnitTest : public testing::Test {
+};
+
+TEST_F(ActivityLogApiUnitTest, ConvertBlockedAction) {
+ scoped_refptr<Action> action(
+ new BlockedAction(kExtensionId,
+ base::Time::Now(),
+ kApiCall,
+ kArgs,
+ BlockedAction::ACCESS_DENIED,
+ kExtra));
+ scoped_ptr<ExtensionActivity> result =
+ action->ConvertToExtensionActivity();
+ ASSERT_EQ(ExtensionActivity::ACTIVITY_TYPE_BLOCKED_CHROME,
+ result->activity_type);
+ ASSERT_EQ(kExtensionId, *(result->extension_id.get()));
+ ASSERT_EQ(kApiCall,
+ *(result->blocked_chrome_activity_detail->api_call.get()));
+ ASSERT_EQ(kArgs,
+ *(result->blocked_chrome_activity_detail->args.get()));
+ ASSERT_EQ(BlockedChromeActivityDetail::REASON_ACCESS_DENIED,
+ result->blocked_chrome_activity_detail->reason);
+ ASSERT_EQ(kExtra,
+ *(result->blocked_chrome_activity_detail->extra.get()));
+}
+
+TEST_F(ActivityLogApiUnitTest, ConvertChromeApiAction) {
+ scoped_refptr<Action> action(
+ new APIAction(kExtensionId,
+ base::Time::Now(),
+ APIAction::CALL,
+ kApiCall,
+ kArgs,
+ kExtra));
+ scoped_ptr<ExtensionActivity> result =
+ action->ConvertToExtensionActivity();
+ ASSERT_EQ(ExtensionActivity::ACTIVITY_TYPE_CHROME,
+ result->activity_type);
+ ASSERT_EQ(kExtensionId, *(result->extension_id.get()));
+ ASSERT_EQ(ChromeActivityDetail::API_ACTIVITY_TYPE_CALL,
+ result->chrome_activity_detail->api_activity_type);
+ ASSERT_EQ(kApiCall,
+ *(result->chrome_activity_detail->api_call.get()));
+ ASSERT_EQ(kArgs,
+ *(result->chrome_activity_detail->args.get()));
+ ASSERT_EQ(kExtra,
+ *(result->chrome_activity_detail->extra.get()));
+}
+
+TEST_F(ActivityLogApiUnitTest, ConvertDomAction) {
+ scoped_refptr<Action> action(
+ new DOMAction(kExtensionId,
+ base::Time::Now(),
+ DomActionType::SETTER,
+ GURL("http://www.google.com"),
+ base::ASCIIToUTF16("Title"),
+ kApiCall,
+ kArgs,
+ kExtra));
+ scoped_ptr<ExtensionActivity> result =
+ action->ConvertToExtensionActivity();
+ ASSERT_EQ(ExtensionActivity::ACTIVITY_TYPE_DOM, result->activity_type);
+ ASSERT_EQ(kExtensionId, *(result->extension_id.get()));
+ ASSERT_EQ(DomActivityDetail::DOM_ACTIVITY_TYPE_SETTER,
+ result->dom_activity_detail->dom_activity_type);
+ ASSERT_EQ("http://www.google.com/",
+ *(result->dom_activity_detail->url.get()));
+ ASSERT_EQ("Title", *(result->dom_activity_detail->url_title.get()));
+ ASSERT_EQ(kApiCall,
+ *(result->dom_activity_detail->api_call.get()));
+ ASSERT_EQ(kArgs,
+ *(result->dom_activity_detail->args.get()));
+ ASSERT_EQ(kExtra,
+ *(result->dom_activity_detail->extra.get()));
+}
+
+} // extensions
+
diff --git a/chrome/browser/extensions/api/activity_log_private/activity_log_private_apitest.cc b/chrome/browser/extensions/api/activity_log_private/activity_log_private_apitest.cc
new file mode 100644
index 0000000..7e5d77e
--- /dev/null
+++ b/chrome/browser/extensions/api/activity_log_private/activity_log_private_apitest.cc
@@ -0,0 +1,35 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "chrome/browser/extensions/activity_log/activity_log.h"
+#include "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/extensions/extension_builder.h"
+
+namespace extensions {
+
+class ActivityLogApiTest : public ExtensionApiTest {
+ public:
+ ActivityLogApiTest() {}
+
+ virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+ ExtensionApiTest::SetUpCommandLine(command_line);
+ command_line->AppendSwitch(switches::kEnableExtensionActivityLogging);
+ command_line->AppendSwitch(switches::kEnableExtensionActivityLogTesting);
+ }
+};
+
+// The test extension sends a message to its 'friend'. The test completes
+// if it successfully sees the 'friend' receive the message.
+IN_PROC_BROWSER_TEST_F(ActivityLogApiTest, TriggerEvent) {
+ const Extension* friend_extension =
+ LoadExtension(test_data_dir_.AppendASCII("activity_log_private/friend"));
+ ASSERT_TRUE(friend_extension);
+ ASSERT_TRUE(RunExtensionTest("activity_log_private/test"));
+}
+
+} // namespace extensions
+
diff --git a/chrome/browser/extensions/extension_function_histogram_value.h b/chrome/browser/extensions/extension_function_histogram_value.h
index 3a67231..3b7687a 100644
--- a/chrome/browser/extensions/extension_function_histogram_value.h
+++ b/chrome/browser/extensions/extension_function_histogram_value.h
@@ -546,6 +546,7 @@
EXPERIMENTAL_SYSTEMINFO_STORAGE_GETALLWATCH,
EXPERIMENTAL_SYSTEMINFO_STORAGE_REMOVEALLWATCH,
SYSTEMINFO_MEMORY_GET,
+ ACTIVITYLOGPRIVATE_GETEXTENSIONACTIVITIES,
ENUM_BOUNDARY // Last entry: Add new entries above.
};
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index 41c5ae86..41c3929 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -17,6 +17,7 @@
#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
#include "chrome/browser/download/download_service_factory.h"
#include "chrome/browser/extensions/activity_log/activity_log.h"
+#include "chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h"
#include "chrome/browser/extensions/api/alarms/alarm_manager.h"
#include "chrome/browser/extensions/api/audio/audio_api.h"
#include "chrome/browser/extensions/api/bluetooth/bluetooth_api_factory.h"
@@ -183,6 +184,7 @@
apps::ShortcutManagerFactory::GetInstance();
autofill::autocheckout::WhitelistManagerFactory::GetInstance();
extensions::ActivityLogFactory::GetInstance();
+ extensions::ActivityLogAPI::GetFactoryInstance();
extensions::AlarmManager::GetFactoryInstance();
extensions::AudioAPI::GetFactoryInstance();
extensions::BookmarksAPI::GetFactoryInstance();