Home | History | Annotate | Download | only in activity_log
      1 // Copyright (c) 2013 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_actions.h"
      6 
      7 #include <string>
      8 
      9 #include "base/command_line.h"
     10 #include "base/json/json_string_value_serializer.h"
     11 #include "base/logging.h"
     12 #include "base/memory/singleton.h"
     13 #include "base/strings/string_number_conversions.h"
     14 #include "base/strings/string_util.h"
     15 #include "base/strings/stringprintf.h"
     16 #include "chrome/browser/extensions/activity_log/activity_action_constants.h"
     17 #include "chrome/browser/extensions/activity_log/fullstream_ui_policy.h"
     18 #include "chrome/browser/ui/browser.h"
     19 #include "chrome/common/chrome_switches.h"
     20 #include "chrome/common/extensions/dom_action_types.h"
     21 #include "content/public/browser/browser_thread.h"
     22 #include "content/public/browser/web_contents.h"
     23 #include "sql/statement.h"
     24 
     25 namespace constants = activity_log_constants;
     26 
     27 namespace {
     28 
     29 std::string Serialize(const base::Value* value) {
     30   std::string value_as_text;
     31   if (!value) {
     32     value_as_text = "null";
     33   } else {
     34     JSONStringValueSerializer serializer(&value_as_text);
     35     serializer.SerializeAndOmitBinaryValues(*value);
     36   }
     37   return value_as_text;
     38 }
     39 
     40 }  // namespace
     41 
     42 namespace extensions {
     43 
     44 using api::activity_log_private::ExtensionActivity;
     45 
     46 Action::Action(const std::string& extension_id,
     47                const base::Time& time,
     48                const ActionType action_type,
     49                const std::string& api_name)
     50     : extension_id_(extension_id),
     51       time_(time),
     52       action_type_(action_type),
     53       api_name_(api_name),
     54       page_incognito_(false),
     55       arg_incognito_(false),
     56       count_(0) {}
     57 
     58 Action::~Action() {}
     59 
     60 // TODO(mvrable): As an optimization, we might return this directly if the
     61 // refcount is one.  However, there are likely to be other stray references in
     62 // many cases that will prevent this optimization.
     63 scoped_refptr<Action> Action::Clone() const {
     64   scoped_refptr<Action> clone(
     65       new Action(extension_id(), time(), action_type(), api_name()));
     66   if (args())
     67     clone->set_args(make_scoped_ptr(args()->DeepCopy()));
     68   clone->set_page_url(page_url());
     69   clone->set_page_title(page_title());
     70   clone->set_page_incognito(page_incognito());
     71   clone->set_arg_url(arg_url());
     72   clone->set_arg_incognito(arg_incognito());
     73   if (other())
     74     clone->set_other(make_scoped_ptr(other()->DeepCopy()));
     75   return clone;
     76 }
     77 
     78 void Action::set_args(scoped_ptr<ListValue> args) {
     79   args_.reset(args.release());
     80 }
     81 
     82 ListValue* Action::mutable_args() {
     83   if (!args_.get()) {
     84     args_.reset(new ListValue());
     85   }
     86   return args_.get();
     87 }
     88 
     89 void Action::set_page_url(const GURL& page_url) {
     90   page_url_ = page_url;
     91 }
     92 
     93 void Action::set_arg_url(const GURL& arg_url) {
     94   arg_url_ = arg_url;
     95 }
     96 
     97 void Action::set_other(scoped_ptr<DictionaryValue> other) {
     98   other_.reset(other.release());
     99 }
    100 
    101 DictionaryValue* Action::mutable_other() {
    102   if (!other_.get()) {
    103     other_.reset(new DictionaryValue());
    104   }
    105   return other_.get();
    106 }
    107 
    108 std::string Action::SerializePageUrl() const {
    109   return (page_incognito() ? constants::kIncognitoUrl : "") + page_url().spec();
    110 }
    111 
    112 void Action::ParsePageUrl(const std::string& url) {
    113   set_page_incognito(StartsWithASCII(url, constants::kIncognitoUrl, true));
    114   if (page_incognito())
    115     set_page_url(GURL(url.substr(strlen(constants::kIncognitoUrl))));
    116   else
    117     set_page_url(GURL(url));
    118 }
    119 
    120 std::string Action::SerializeArgUrl() const {
    121   return (arg_incognito() ? constants::kIncognitoUrl : "") + arg_url().spec();
    122 }
    123 
    124 void Action::ParseArgUrl(const std::string& url) {
    125   set_arg_incognito(StartsWithASCII(url, constants::kIncognitoUrl, true));
    126   if (arg_incognito())
    127     set_arg_url(GURL(url.substr(strlen(constants::kIncognitoUrl))));
    128   else
    129     set_arg_url(GURL(url));
    130 }
    131 
    132 scoped_ptr<ExtensionActivity> Action::ConvertToExtensionActivity() {
    133   scoped_ptr<ExtensionActivity> result(new ExtensionActivity);
    134 
    135   // We do this translation instead of using the same enum because the database
    136   // values need to be stable; this allows us to change the extension API
    137   // without affecting the database.
    138   switch (action_type()) {
    139     case ACTION_API_CALL:
    140       result->activity_type = ExtensionActivity::ACTIVITY_TYPE_API_CALL;
    141       break;
    142     case ACTION_API_EVENT:
    143       result->activity_type = ExtensionActivity::ACTIVITY_TYPE_API_EVENT;
    144       break;
    145     case ACTION_CONTENT_SCRIPT:
    146       result->activity_type = ExtensionActivity::ACTIVITY_TYPE_CONTENT_SCRIPT;
    147       break;
    148     case ACTION_DOM_ACCESS:
    149       result->activity_type = ExtensionActivity::ACTIVITY_TYPE_DOM_ACCESS;
    150       break;
    151     case ACTION_DOM_EVENT:
    152       result->activity_type = ExtensionActivity::ACTIVITY_TYPE_DOM_EVENT;
    153       break;
    154     case ACTION_WEB_REQUEST:
    155       result->activity_type = ExtensionActivity::ACTIVITY_TYPE_WEB_REQUEST;
    156       break;
    157     case UNUSED_ACTION_API_BLOCKED:
    158     case ACTION_ANY:
    159     default:
    160       // This shouldn't be reached, but some people might have old or otherwise
    161       // weird db entries. Treat it like an API call if that happens.
    162       result->activity_type = ExtensionActivity::ACTIVITY_TYPE_API_CALL;
    163       break;
    164   }
    165 
    166   result->extension_id.reset(new std::string(extension_id()));
    167   result->time.reset(new double(time().ToJsTime()));
    168   result->count.reset(new double(count()));
    169   result->api_call.reset(new std::string(api_name()));
    170   result->args.reset(new std::string(Serialize(args())));
    171   if (page_url().is_valid()) {
    172     if (!page_title().empty())
    173       result->page_title.reset(new std::string(page_title()));
    174     result->page_url.reset(new std::string(SerializePageUrl()));
    175   }
    176   if (arg_url().is_valid())
    177     result->arg_url.reset(new std::string(SerializeArgUrl()));
    178 
    179   if (other()) {
    180     scoped_ptr<ExtensionActivity::Other> other_field(
    181         new ExtensionActivity::Other);
    182     bool prerender;
    183     if (other()->GetBooleanWithoutPathExpansion(constants::kActionPrerender,
    184                                                 &prerender)) {
    185       other_field->prerender.reset(new bool(prerender));
    186     }
    187     const DictionaryValue* web_request;
    188     if (other()->GetDictionaryWithoutPathExpansion(constants::kActionWebRequest,
    189                                                    &web_request)) {
    190       other_field->web_request.reset(new std::string(
    191           ActivityLogPolicy::Util::Serialize(web_request)));
    192     }
    193     std::string extra;
    194     if (other()->GetStringWithoutPathExpansion(constants::kActionExtra, &extra))
    195       other_field->extra.reset(new std::string(extra));
    196     int dom_verb;
    197     if (other()->GetIntegerWithoutPathExpansion(constants::kActionDomVerb,
    198                                                 &dom_verb)) {
    199       switch (static_cast<DomActionType::Type>(dom_verb)) {
    200         case DomActionType::GETTER:
    201           other_field->dom_verb = ExtensionActivity::Other::DOM_VERB_GETTER;
    202           break;
    203         case DomActionType::SETTER:
    204           other_field->dom_verb = ExtensionActivity::Other::DOM_VERB_SETTER;
    205           break;
    206         case DomActionType::METHOD:
    207           other_field->dom_verb = ExtensionActivity::Other::DOM_VERB_METHOD;
    208           break;
    209         case DomActionType::INSERTED:
    210           other_field->dom_verb = ExtensionActivity::Other::DOM_VERB_INSERTED;
    211           break;
    212         case DomActionType::XHR:
    213           other_field->dom_verb = ExtensionActivity::Other::DOM_VERB_XHR;
    214           break;
    215         case DomActionType::WEBREQUEST:
    216           other_field->dom_verb = ExtensionActivity::Other::DOM_VERB_WEBREQUEST;
    217           break;
    218         case DomActionType::MODIFIED:
    219           other_field->dom_verb = ExtensionActivity::Other::DOM_VERB_MODIFIED;
    220           break;
    221         default:
    222           other_field->dom_verb = ExtensionActivity::Other::DOM_VERB_NONE;
    223       }
    224     } else {
    225       other_field->dom_verb = ExtensionActivity::Other::DOM_VERB_NONE;
    226     }
    227     result->other.reset(other_field.release());
    228   }
    229 
    230   return result.Pass();
    231 }
    232 
    233 std::string Action::PrintForDebug() const {
    234   std::string result = "ID=" + extension_id() + " CATEGORY=";
    235   switch (action_type_) {
    236     case ACTION_API_CALL:
    237       result += "api_call";
    238       break;
    239     case ACTION_API_EVENT:
    240       result += "api_event_callback";
    241       break;
    242     case ACTION_WEB_REQUEST:
    243       result += "webrequest";
    244       break;
    245     case ACTION_CONTENT_SCRIPT:
    246       result += "content_script";
    247       break;
    248     case UNUSED_ACTION_API_BLOCKED:
    249       // This is deprecated.
    250       result += "api_blocked";
    251       break;
    252     case ACTION_DOM_EVENT:
    253       result += "dom_event";
    254       break;
    255     case ACTION_DOM_ACCESS:
    256       result += "dom_access";
    257       break;
    258     default:
    259       result += base::StringPrintf("type%d", static_cast<int>(action_type_));
    260   }
    261 
    262   result += " API=" + api_name_;
    263   if (args_.get()) {
    264     result += " ARGS=" + Serialize(args_.get());
    265   }
    266   if (page_url_.is_valid()) {
    267     if (page_incognito_)
    268       result += " PAGE_URL=(incognito)" + page_url_.spec();
    269     else
    270       result += " PAGE_URL=" + page_url_.spec();
    271   }
    272   if (!page_title_.empty()) {
    273     StringValue title(page_title_);
    274     result += " PAGE_TITLE=" + Serialize(&title);
    275   }
    276   if (arg_url_.is_valid()) {
    277     if (arg_incognito_)
    278       result += " ARG_URL=(incognito)" + arg_url_.spec();
    279     else
    280       result += " ARG_URL=" + arg_url_.spec();
    281   }
    282   if (other_.get()) {
    283     result += " OTHER=" + Serialize(other_.get());
    284   }
    285 
    286   result += base::StringPrintf(" COUNT=%d", count_);
    287   return result;
    288 }
    289 
    290 bool ActionComparator::operator()(
    291     const scoped_refptr<Action>& lhs,
    292     const scoped_refptr<Action>& rhs) const {
    293   if (lhs->time() != rhs->time())
    294     return lhs->time() < rhs->time();
    295   else
    296     return ActionComparatorExcludingTime()(lhs, rhs);
    297 }
    298 
    299 bool ActionComparatorExcludingTime::operator()(
    300     const scoped_refptr<Action>& lhs,
    301     const scoped_refptr<Action>& rhs) const {
    302   if (lhs->extension_id() != rhs->extension_id())
    303     return lhs->extension_id() < rhs->extension_id();
    304   if (lhs->action_type() != rhs->action_type())
    305     return lhs->action_type() < rhs->action_type();
    306   if (lhs->api_name() != rhs->api_name())
    307     return lhs->api_name() < rhs->api_name();
    308 
    309   // args might be null; treat a null value as less than all non-null values,
    310   // including the empty string.
    311   if (!lhs->args() && rhs->args())
    312     return true;
    313   if (lhs->args() && !rhs->args())
    314     return false;
    315   if (lhs->args() && rhs->args()) {
    316     std::string lhs_args = ActivityLogPolicy::Util::Serialize(lhs->args());
    317     std::string rhs_args = ActivityLogPolicy::Util::Serialize(rhs->args());
    318     if (lhs_args != rhs_args)
    319       return lhs_args < rhs_args;
    320   }
    321 
    322   // Compare URLs as strings, and treat the incognito flag as a separate field.
    323   if (lhs->page_url().spec() != rhs->page_url().spec())
    324     return lhs->page_url().spec() < rhs->page_url().spec();
    325   if (lhs->page_incognito() != rhs->page_incognito())
    326     return lhs->page_incognito() < rhs->page_incognito();
    327 
    328   if (lhs->page_title() != rhs->page_title())
    329     return lhs->page_title() < rhs->page_title();
    330 
    331   if (lhs->arg_url().spec() != rhs->arg_url().spec())
    332     return lhs->arg_url().spec() < rhs->arg_url().spec();
    333   if (lhs->arg_incognito() != rhs->arg_incognito())
    334     return lhs->arg_incognito() < rhs->arg_incognito();
    335 
    336   // other is treated much like the args field.
    337   if (!lhs->other() && rhs->other())
    338     return true;
    339   if (lhs->other() && !rhs->other())
    340     return false;
    341   if (lhs->other() && rhs->other()) {
    342     std::string lhs_other = ActivityLogPolicy::Util::Serialize(lhs->other());
    343     std::string rhs_other = ActivityLogPolicy::Util::Serialize(rhs->other());
    344     if (lhs_other != rhs_other)
    345       return lhs_other < rhs_other;
    346   }
    347 
    348   // All fields compare as equal if this point is reached.
    349   return false;
    350 }
    351 
    352 }  // namespace extensions
    353