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