| OLD | NEW |
| (Empty) |
| 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 "base/json/json_string_value_serializer.h" | |
| 6 #include "base/logging.h" | |
| 7 #include "base/memory/singleton.h" | |
| 8 #include "base/strings/string_number_conversions.h" | |
| 9 #include "base/strings/stringprintf.h" | |
| 10 #include "chrome/browser/extensions/activity_log/api_actions.h" | |
| 11 #include "chrome/browser/extensions/activity_log/api_name_constants.h" | |
| 12 #include "chrome/browser/extensions/activity_log/fullstream_ui_policy.h" | |
| 13 #include "chrome/browser/extensions/extension_tab_util.h" | |
| 14 #include "chrome/browser/ui/browser.h" | |
| 15 #include "content/public/browser/browser_thread.h" | |
| 16 #include "content/public/browser/web_contents.h" | |
| 17 #include "url/gurl.h" | |
| 18 | |
| 19 using content::BrowserThread; | |
| 20 | |
| 21 namespace { | |
| 22 | |
| 23 // Gets the URL for a given tab ID. Helper method for APIAction::LookupTabId. | |
| 24 std::string GetURLForTabId(const int tab_id, Profile* profile) { | |
| 25 content::WebContents* contents = NULL; | |
| 26 Browser* browser = NULL; | |
| 27 bool found = ExtensionTabUtil::GetTabById(tab_id, | |
| 28 profile, | |
| 29 true, // search incognito tabs too | |
| 30 &browser, | |
| 31 NULL, | |
| 32 &contents, | |
| 33 NULL); | |
| 34 if (found) { | |
| 35 // Check whether the profile the tab was found in is a normal or incognito | |
| 36 // profile. | |
| 37 if (!browser->profile()->IsOffTheRecord()) { | |
| 38 GURL url = contents->GetURL(); | |
| 39 return std::string(url.spec()); | |
| 40 } else { | |
| 41 return std::string(extensions::APIAction::kIncognitoUrl); | |
| 42 } | |
| 43 } else { | |
| 44 return std::string(); | |
| 45 } | |
| 46 } | |
| 47 | |
| 48 // Sets up the hashmap for mapping extension strings to "ints". The hashmap is | |
| 49 // only set up once because it's quite long; the value is then cached. | |
| 50 class APINameMap { | |
| 51 public: | |
| 52 APINameMap() { | |
| 53 SetupMap(); | |
| 54 } | |
| 55 | |
| 56 // activity_log_api_name_constants.h lists all known API calls as of 5/17. | |
| 57 // This code maps each of those API calls (and events) to short strings | |
| 58 // (integers converted to strings). They're all strings because (1) sqlite | |
| 59 // databases are all strings underneath anyway and (2) the Lookup function | |
| 60 // will simply return the original api_call string if we don't have it in our | |
| 61 // lookup table. | |
| 62 void SetupMap() { | |
| 63 for (size_t i = 0; | |
| 64 i < arraysize(activity_log_api_name_constants::kNames); | |
| 65 ++i) { | |
| 66 std::string name = | |
| 67 std::string(activity_log_api_name_constants::kNames[i]); | |
| 68 std::string num = base::IntToString(i); | |
| 69 names_to_nums_[name] = num; | |
| 70 nums_to_names_[num] = name; | |
| 71 } | |
| 72 } | |
| 73 | |
| 74 static APINameMap* GetInstance() { | |
| 75 return Singleton<APINameMap>::get(); | |
| 76 } | |
| 77 | |
| 78 // This matches an api call to a number, if it's in the lookup table. If not, | |
| 79 // it returns the original api call. | |
| 80 const std::string& ApiToShortname(const std::string& api_call) { | |
| 81 std::map<std::string, std::string>::iterator it = | |
| 82 names_to_nums_.find(api_call); | |
| 83 if (it == names_to_nums_.end()) | |
| 84 return api_call; | |
| 85 else | |
| 86 return it->second; | |
| 87 } | |
| 88 | |
| 89 // This matches a number to an API call -- it's the opposite of | |
| 90 // ApiToShortname. | |
| 91 const std::string& ShortnameToApi(const std::string& shortname) { | |
| 92 std::map<std::string, std::string>::iterator it = | |
| 93 nums_to_names_.find(shortname); | |
| 94 if (it == nums_to_names_.end()) | |
| 95 return shortname; | |
| 96 else | |
| 97 return it->second; | |
| 98 } | |
| 99 | |
| 100 private: | |
| 101 std::map<std::string, std::string> names_to_nums_; // <name, number label> | |
| 102 std::map<std::string, std::string> nums_to_names_; // <number label, name> | |
| 103 }; | |
| 104 | |
| 105 std::string Serialize(const base::Value& value) { | |
| 106 std::string value_as_text; | |
| 107 JSONStringValueSerializer serializer(&value_as_text); | |
| 108 serializer.SerializeAndOmitBinaryValues(value); | |
| 109 return value_as_text; | |
| 110 } | |
| 111 | |
| 112 } // namespace | |
| 113 | |
| 114 namespace extensions { | |
| 115 | |
| 116 using api::activity_log_private::ExtensionActivity; | |
| 117 using api::activity_log_private::DomActivityDetail; | |
| 118 using api::activity_log_private::ChromeActivityDetail; | |
| 119 using api::activity_log_private::BlockedChromeActivityDetail; | |
| 120 | |
| 121 // We should log the arguments to these API calls, even if argument logging is | |
| 122 // disabled by default. | |
| 123 const char* APIAction::kAlwaysLog[] = | |
| 124 {"extension.connect", "extension.sendMessage", | |
| 125 "tabs.executeScript", "tabs.insertCSS" }; | |
| 126 const int APIAction::kSizeAlwaysLog = arraysize(kAlwaysLog); | |
| 127 | |
| 128 // A string used in place of the real URL when the URL is hidden because it is | |
| 129 // in an incognito window. Extension activity logs mentioning kIncognitoUrl | |
| 130 // let the user know that an extension is manipulating incognito tabs without | |
| 131 // recording specific data about the pages. | |
| 132 const char* APIAction::kIncognitoUrl = "http://incognito/"; | |
| 133 | |
| 134 APIAction::APIAction(const std::string& extension_id, | |
| 135 const base::Time& time, | |
| 136 const Type type, | |
| 137 const std::string& api_call, | |
| 138 const std::string& args, | |
| 139 const base::ListValue& args_list, | |
| 140 const std::string& extra) | |
| 141 : Action(extension_id, time, ExtensionActivity::ACTIVITY_TYPE_CHROME), | |
| 142 type_(type), | |
| 143 api_call_(api_call), | |
| 144 args_(args), | |
| 145 args_list_(args_list.DeepCopy()), | |
| 146 extra_(extra) { } | |
| 147 | |
| 148 APIAction::~APIAction() { | |
| 149 } | |
| 150 | |
| 151 scoped_ptr<ExtensionActivity> APIAction::ConvertToExtensionActivity() { | |
| 152 scoped_ptr<ExtensionActivity> formatted_activity; | |
| 153 formatted_activity.reset(new ExtensionActivity); | |
| 154 formatted_activity->extension_id.reset( | |
| 155 new std::string(extension_id())); | |
| 156 formatted_activity->activity_type = activity_type(); | |
| 157 formatted_activity->time.reset(new double(time().ToJsTime())); | |
| 158 ChromeActivityDetail* details = new ChromeActivityDetail; | |
| 159 details->api_activity_type = ChromeActivityDetail::ParseApiActivityType( | |
| 160 TypeAsString()); | |
| 161 details->api_call.reset(new std::string(api_call_)); | |
| 162 details->args.reset(new std::string(args_)); | |
| 163 details->extra.reset(new std::string(extra_)); | |
| 164 formatted_activity->chrome_activity_detail.reset(details); | |
| 165 return formatted_activity.Pass(); | |
| 166 } | |
| 167 | |
| 168 bool APIAction::Record(sql::Connection* db) { | |
| 169 std::string sql_str = | |
| 170 "INSERT INTO " + std::string(FullStreamUIPolicy::kTableName) + | |
| 171 " (extension_id, time, action_type, api_name, args) VALUES" | |
| 172 " (?,?,?,?,?)"; | |
| 173 sql::Statement statement(db->GetCachedStatement( | |
| 174 sql::StatementID(SQL_FROM_HERE), sql_str.c_str())); | |
| 175 statement.BindString(0, extension_id()); | |
| 176 statement.BindInt64(1, time().ToInternalValue()); | |
| 177 switch (type_) { | |
| 178 case CALL: | |
| 179 statement.BindInt(2, static_cast<int>(Action::ACTION_API_CALL)); | |
| 180 break; | |
| 181 case EVENT_CALLBACK: | |
| 182 statement.BindInt(2, static_cast<int>(Action::ACTION_API_EVENT)); | |
| 183 break; | |
| 184 default: | |
| 185 LOG(ERROR) << "Invalid action type: " << type_; | |
| 186 return false; | |
| 187 } | |
| 188 statement.BindString(3, api_call_); | |
| 189 statement.BindString(4, Serialize(*args_list_)); | |
| 190 if (!statement.Run()) { | |
| 191 LOG(ERROR) << "Activity log database I/O failed: " << sql_str; | |
| 192 statement.Clear(); | |
| 193 return false; | |
| 194 } | |
| 195 return true; | |
| 196 } | |
| 197 | |
| 198 // static | |
| 199 void APIAction::LookupTabId(const std::string& api_call, | |
| 200 base::ListValue* args, | |
| 201 Profile* profile) { | |
| 202 if (api_call == "tabs.get" || // api calls, ID as int | |
| 203 api_call == "tabs.connect" || | |
| 204 api_call == "tabs.sendMessage" || | |
| 205 api_call == "tabs.duplicate" || | |
| 206 api_call == "tabs.update" || | |
| 207 api_call == "tabs.reload" || | |
| 208 api_call == "tabs.detectLanguage" || | |
| 209 api_call == "tabs.executeScript" || | |
| 210 api_call == "tabs.insertCSS" || | |
| 211 api_call == "tabs.move" || // api calls, IDs in array | |
| 212 api_call == "tabs.remove" || | |
| 213 api_call == "tabs.onUpdated" || // events, ID as int | |
| 214 api_call == "tabs.onMoved" || | |
| 215 api_call == "tabs.onDetached" || | |
| 216 api_call == "tabs.onAttached" || | |
| 217 api_call == "tabs.onRemoved" || | |
| 218 api_call == "tabs.onReplaced") { | |
| 219 int tab_id; | |
| 220 base::ListValue* id_list; | |
| 221 if (args->GetInteger(0, &tab_id)) { | |
| 222 std::string url = GetURLForTabId(tab_id, profile); | |
| 223 if (url != std::string()) | |
| 224 args->Set(0, new base::StringValue(url)); | |
| 225 } else if ((api_call == "tabs.move" || api_call == "tabs.remove") && | |
| 226 args->GetList(0, &id_list)) { | |
| 227 for (int i = 0; i < static_cast<int>(id_list->GetSize()); ++i) { | |
| 228 if (id_list->GetInteger(i, &tab_id)) { | |
| 229 std::string url = GetURLForTabId(tab_id, profile); | |
| 230 if (url != std::string()) | |
| 231 id_list->Set(i, new base::StringValue(url)); | |
| 232 } else { | |
| 233 LOG(ERROR) << "The tab ID array is malformed at index " << i; | |
| 234 } | |
| 235 } | |
| 236 } | |
| 237 } | |
| 238 } | |
| 239 | |
| 240 std::string APIAction::PrintForDebug() { | |
| 241 return "ID: " + extension_id() + ", CATEGORY: " + TypeAsString() + | |
| 242 ", API: " + api_call_ + ", ARGS: " + args_; | |
| 243 } | |
| 244 | |
| 245 std::string APIAction::TypeAsString() const { | |
| 246 switch (type_) { | |
| 247 case CALL: | |
| 248 return "call"; | |
| 249 case EVENT_CALLBACK: | |
| 250 return "event_callback"; | |
| 251 default: | |
| 252 return "unknown_type"; | |
| 253 } | |
| 254 } | |
| 255 | |
| 256 } // namespace extensions | |
| 257 | |
| OLD | NEW |