Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/extensions/activity_log/activity_log.h" | 5 #include "chrome/browser/extensions/activity_log/activity_log.h" |
| 6 | 6 |
| 7 #include <set> | 7 #include <set> |
| 8 #include <vector> | 8 #include <vector> |
| 9 | 9 |
| 10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 33 #include "chrome/common/pref_names.h" | 33 #include "chrome/common/pref_names.h" |
| 34 #include "components/browser_context_keyed_service/browser_context_dependency_ma nager.h" | 34 #include "components/browser_context_keyed_service/browser_context_dependency_ma nager.h" |
| 35 #include "content/public/browser/web_contents.h" | 35 #include "content/public/browser/web_contents.h" |
| 36 #include "third_party/re2/re2/re2.h" | 36 #include "third_party/re2/re2/re2.h" |
| 37 #include "url/gurl.h" | 37 #include "url/gurl.h" |
| 38 | 38 |
| 39 namespace constants = activity_log_constants; | 39 namespace constants = activity_log_constants; |
| 40 | 40 |
| 41 namespace { | 41 namespace { |
| 42 | 42 |
| 43 // Concatenate arguments. | 43 using extensions::Action; |
| 44 std::string MakeArgList(const base::ListValue* args) { | 44 using constants::kArgUrlPlaceholder; |
| 45 std::string call_signature; | 45 |
| 46 base::ListValue::const_iterator it = args->begin(); | 46 // Information about specific Chrome and DOM APIs, such as which contain |
| 47 for (; it != args->end(); ++it) { | 47 // arguments that should be extracted into the arg_url field of an Action. |
| 48 std::string arg; | 48 struct ApiInfo { |
| 49 JSONStringValueSerializer serializer(&arg); | 49 // The lookup key consists of the action_type and api_name in the Action |
| 50 if (serializer.SerializeAndOmitBinaryValues(**it)) { | 50 // object. |
| 51 if (it != args->begin()) | 51 Action::ActionType action_type; |
| 52 call_signature += ", "; | 52 const char* api_name; |
| 53 call_signature += arg; | 53 |
| 54 // If non-negative, an index into args might contain a URL to be extracted | |
| 55 // into arg_url. | |
| 56 int arg_url_index; | |
| 57 | |
| 58 // An optional transformation to apply to the data found at index | |
| 59 // arg_url_index in the argument list. | |
| 60 // | |
| 61 // If NULL, the data is expected to be a string which is treated as a URL. | |
| 62 // | |
| 63 // If the special value kArgUrlLookupTabId, the data is either an integer | |
| 64 // which is treated as a tab ID and translated (in the context of a provided | |
| 65 // Profile), or a list of tab IDs which are translated. | |
| 66 // | |
| 67 // Otherwise, the data is expected to be a dictionary, and arg_url_lookup is | |
| 68 // a path (list of keys delimited by ".") where a URL string is to be found. | |
| 69 const char* arg_url_lookup; | |
|
not at google - send to devlin
2013/09/06 17:36:15
this is kind of roundabout and looking at the code
mvrable
2013/09/06 21:27:42
That is a little more verbose but I don't mind cha
| |
| 70 }; | |
| 71 | |
| 72 // Note: Comparisons against kArgUrlLookupTabId are done by address only; the | |
| 73 // contents of the string aren't relevant. | |
| 74 const char kArgUrlLookupTabId[] = "<lookupTabId>"; | |
| 75 | |
| 76 static const ApiInfo kApiInfoTable[] = { | |
| 77 // Tabs APIs that require tab ID translation | |
| 78 {Action::ACTION_API_CALL, "tabs.connect", 0, kArgUrlLookupTabId}, | |
| 79 {Action::ACTION_API_CALL, "tabs.detectLanguage", 0, kArgUrlLookupTabId}, | |
| 80 {Action::ACTION_API_CALL, "tabs.duplicate", 0, kArgUrlLookupTabId}, | |
| 81 {Action::ACTION_API_CALL, "tabs.executeScript", 0, kArgUrlLookupTabId}, | |
| 82 {Action::ACTION_API_CALL, "tabs.get", 0, kArgUrlLookupTabId}, | |
| 83 {Action::ACTION_API_CALL, "tabs.insertCSS", 0, kArgUrlLookupTabId}, | |
| 84 {Action::ACTION_API_CALL, "tabs.move", 0, kArgUrlLookupTabId}, | |
| 85 {Action::ACTION_API_CALL, "tabs.reload", 0, kArgUrlLookupTabId}, | |
| 86 {Action::ACTION_API_CALL, "tabs.remove", 0, kArgUrlLookupTabId}, | |
| 87 {Action::ACTION_API_CALL, "tabs.sendMessage", 0, kArgUrlLookupTabId}, | |
| 88 {Action::ACTION_API_CALL, "tabs.update", 0, kArgUrlLookupTabId}, | |
| 89 | |
| 90 {Action::ACTION_API_EVENT, "tabs.onUpdated", 0, kArgUrlLookupTabId}, | |
| 91 {Action::ACTION_API_EVENT, "tabs.onMoved", 0, kArgUrlLookupTabId}, | |
| 92 {Action::ACTION_API_EVENT, "tabs.onDetached", 0, kArgUrlLookupTabId}, | |
| 93 {Action::ACTION_API_EVENT, "tabs.onAttached", 0, kArgUrlLookupTabId}, | |
| 94 {Action::ACTION_API_EVENT, "tabs.onRemoved", 0, kArgUrlLookupTabId}, | |
| 95 {Action::ACTION_API_EVENT, "tabs.onReplaced", 0, kArgUrlLookupTabId}, | |
| 96 | |
| 97 // Other APIs that accept URLs as strings | |
| 98 {Action::ACTION_API_CALL, "windows.create", 0, "url"}, | |
|
not at google - send to devlin
2013/09/06 17:36:15
Generally speaking - and this would be a much larg
mvrable
2013/09/06 21:27:42
Agreed, it would be much more sustainable if we ha
not at google - send to devlin
2013/09/06 22:05:05
Totally fine to have this for now. It would be nic
| |
| 99 | |
| 100 {Action::ACTION_DOM_ACCESS, "Location.assign", 0, NULL}, | |
| 101 {Action::ACTION_DOM_ACCESS, "Location.replace", 0, NULL}, | |
| 102 {Action::ACTION_DOM_ACCESS, "XMLHttpRequest.open", 1, NULL}, | |
| 103 }; | |
| 104 | |
| 105 // A singleton class which provides lookups into the kApiInfoTable data | |
| 106 // structure. It inserts all data into a map on first lookup. | |
| 107 class ApiInfoDatabase { | |
| 108 public: | |
| 109 static ApiInfoDatabase* GetInstance() { | |
| 110 return Singleton<ApiInfoDatabase>::get(); | |
| 111 } | |
| 112 | |
| 113 // Retrieves an ApiInfo record for the given Action type. Returns either a | |
| 114 // pointer to the record, or NULL if no such record was found. | |
| 115 const ApiInfo* Lookup(Action::ActionType action_type, | |
| 116 const std::string& api_name) const { | |
| 117 std::map<std::string, const ApiInfo*>::const_iterator i = | |
| 118 api_database_.find(api_name); | |
| 119 if (i == api_database_.end()) | |
| 120 return NULL; | |
| 121 if (i->second->action_type != action_type) | |
|
not at google - send to devlin
2013/09/06 17:36:15
Can this ever be false?
I think the comment on ap
mvrable
2013/09/06 21:27:42
Yes, this could potentially be false since we coul
| |
| 122 return NULL; | |
| 123 return i->second; | |
| 124 } | |
| 125 | |
| 126 private: | |
| 127 ApiInfoDatabase() { | |
| 128 for (size_t i = 0; i < arraysize(kApiInfoTable); i++) { | |
| 129 const ApiInfo* info = &kApiInfoTable[i]; | |
| 130 api_database_[info->api_name] = info; | |
| 54 } | 131 } |
| 55 } | 132 } |
| 56 return call_signature; | 133 virtual ~ApiInfoDatabase() {} |
| 57 } | |
| 58 | 134 |
| 59 // Gets the URL for a given tab ID. Helper method for LookupTabId. Returns | 135 // The map is keyed by API name only, since API names aren't be repeated |
| 136 // across multiple action types in kApiInfoTable. However, the action type | |
| 137 // should still be checked before returning a positive match. | |
| 138 std::map<std::string, const ApiInfo*> api_database_; | |
| 139 | |
| 140 friend struct DefaultSingletonTraits<ApiInfoDatabase>; | |
| 141 DISALLOW_COPY_AND_ASSIGN(ApiInfoDatabase); | |
| 142 }; | |
| 143 | |
| 144 // Gets the URL for a given tab ID. Helper method for ExtractUrls. Returns | |
| 60 // true if able to perform the lookup. The URL is stored to *url, and | 145 // true if able to perform the lookup. The URL is stored to *url, and |
| 61 // *is_incognito is set to indicate whether the URL is for an incognito tab. | 146 // *is_incognito is set to indicate whether the URL is for an incognito tab. |
| 62 bool GetUrlForTabId(int tab_id, | 147 bool GetUrlForTabId(int tab_id, |
| 63 Profile* profile, | 148 Profile* profile, |
| 64 GURL* url, | 149 GURL* url, |
| 65 bool* is_incognito) { | 150 bool* is_incognito) { |
| 66 content::WebContents* contents = NULL; | 151 content::WebContents* contents = NULL; |
| 67 Browser* browser = NULL; | 152 Browser* browser = NULL; |
| 68 bool found = ExtensionTabUtil::GetTabById(tab_id, | 153 bool found = ExtensionTabUtil::GetTabById(tab_id, |
| 69 profile, | 154 profile, |
| 70 true, // search incognito tabs too | 155 true, // search incognito tabs too |
| 71 &browser, | 156 &browser, |
| 72 NULL, | 157 NULL, |
| 73 &contents, | 158 &contents, |
| 74 NULL); | 159 NULL); |
| 75 if (found) { | 160 if (found) { |
| 76 *url = contents->GetURL(); | 161 *url = contents->GetURL(); |
| 77 *is_incognito = browser->profile()->IsOffTheRecord(); | 162 *is_incognito = browser->profile()->IsOffTheRecord(); |
| 78 return true; | 163 return true; |
| 79 } else { | 164 } else { |
| 80 return false; | 165 return false; |
| 81 } | 166 } |
| 82 } | 167 } |
| 83 | 168 |
| 84 // Translate tab IDs to URLs in tabs API calls. Mutates the Action object in | 169 // Performs processing of the Action object to extract URLs from the argument |
| 85 // place. There is a small chance that the URL translation could be wrong, if | 170 // list and translate tab IDs to URLs, according to the API call metadata in |
| 86 // the tab has already been navigated by the time of invocation. | 171 // kApiInfoTable. Mutates the Action object in place. There is a small chance |
| 172 // that the tab id->URL translation could be wrong, if the tab has already been | |
| 173 // navigated by the time of invocation. | |
| 87 // | 174 // |
| 88 // If a single tab ID is translated to a URL, the URL is stored into arg_url | 175 // Any extracted URL is stored into the arg_url field of the action, and the |
| 89 // where it can more easily be searched for in the database. For APIs that | 176 // URL in the argument list is replaced with the marker value "<arg_url>". For |
| 90 // take a list of tab IDs, replace each tab ID with the URL in the argument | 177 // APIs that take a list of tab IDs, extracts the first valid URL into arg_url |
| 91 // list; we can only extract a single URL into arg_url so arbitrarily pull out | 178 // and overwrites the other tab IDs in the argument list with the translated |
| 92 // the first one. | 179 // URL. |
| 93 void LookupTabIds(scoped_refptr<extensions::Action> action, Profile* profile) { | 180 void ExtractUrls(scoped_refptr<Action> action, Profile* profile) { |
| 94 const std::string& api_call = action->api_name(); | 181 const ApiInfo* api_info = ApiInfoDatabase::GetInstance()->Lookup( |
| 95 if (api_call == "tabs.get" || // api calls, ID as int | 182 action->action_type(), action->api_name()); |
| 96 api_call == "tabs.connect" || | 183 if (api_info == NULL) |
| 97 api_call == "tabs.sendMessage" || | 184 return; |
| 98 api_call == "tabs.duplicate" || | 185 |
| 99 api_call == "tabs.update" || | 186 int url_index = api_info->arg_url_index; |
| 100 api_call == "tabs.reload" || | 187 |
| 101 api_call == "tabs.detectLanguage" || | 188 if (!action->args() || url_index < 0 || |
| 102 api_call == "tabs.executeScript" || | 189 static_cast<size_t>(url_index) >= action->args()->GetSize()) |
| 103 api_call == "tabs.insertCSS" || | 190 return; |
| 104 api_call == "tabs.move" || // api calls, IDs in array | 191 |
| 105 api_call == "tabs.remove" || | 192 // Do not overwrite an existing arg_url value in the Action, so that callers |
| 106 api_call == "tabs.onUpdated" || // events, ID as int | 193 // have the option of doing custom arg_url extraction. |
| 107 api_call == "tabs.onMoved" || | 194 if (action->arg_url().is_valid()) |
| 108 api_call == "tabs.onDetached" || | 195 return; |
| 109 api_call == "tabs.onAttached" || | 196 |
| 110 api_call == "tabs.onRemoved" || | 197 GURL arg_url; |
| 111 api_call == "tabs.onReplaced") { | 198 bool arg_incognito = action->page_incognito(); |
| 199 | |
| 200 if (api_info->arg_url_lookup == kArgUrlLookupTabId) { | |
| 201 // Translation of tab IDs to URLs has been requested. There are two cases | |
| 202 // to consider: either a single integer or a list of integers (when | |
| 203 // multiple tabs are manipulated). | |
| 112 int tab_id; | 204 int tab_id; |
| 113 base::ListValue* id_list; | 205 base::ListValue* tab_list; |
|
not at google - send to devlin
2013/09/06 17:36:15
= NULL
mvrable
2013/09/06 21:27:42
Done.
| |
| 114 base::ListValue* args = action->mutable_args(); | 206 if (action->args()->GetInteger(url_index, &tab_id)) { |
| 115 if (args->GetInteger(0, &tab_id)) { | |
| 116 // Single tab ID to translate. | 207 // Single tab ID to translate. |
| 117 GURL url; | 208 GetUrlForTabId(tab_id, profile, &arg_url, &arg_incognito); |
| 118 bool is_incognito; | 209 if (arg_url.is_valid()) { |
| 119 if (GetUrlForTabId(tab_id, profile, &url, &is_incognito)) { | 210 action->mutable_args()->Set(url_index, |
| 120 action->set_arg_url(url); | 211 new StringValue(kArgUrlPlaceholder)); |
| 121 action->set_arg_incognito(is_incognito); | |
| 122 } | 212 } |
| 123 } else if ((api_call == "tabs.move" || api_call == "tabs.remove") && | 213 } else if (action->mutable_args()->GetList(url_index, &tab_list)) { |
| 124 args->GetList(0, &id_list)) { | 214 // A list of possible IDs to translate. Work through in reverse order |
| 125 // Array of tab IDs to translate. | 215 // so the last one translated is left in arg_url. |
| 126 for (int i = 0; i < static_cast<int>(id_list->GetSize()); ++i) { | 216 int extracted_index = -1; // Which item in the list is stored to arg_url? |
| 127 if (id_list->GetInteger(i, &tab_id)) { | 217 for (int i = tab_list->GetSize(); i >= 0; i--) { |
|
not at google - send to devlin
2013/09/06 17:36:15
--i not i--.
and shouldn't this be tab_list->GetS
mvrable
2013/09/06 21:27:42
Done.
| |
| 128 GURL url; | 218 if (tab_list->GetInteger(i, &tab_id) && |
| 129 bool is_incognito; | 219 GetUrlForTabId(tab_id, profile, &arg_url, &arg_incognito)) { |
| 130 if (GetUrlForTabId(tab_id, profile, &url, &is_incognito) && | 220 if (!arg_incognito) |
| 131 !is_incognito) { | 221 tab_list->Set(i, new base::StringValue(arg_url.spec())); |
| 132 id_list->Set(i, new base::StringValue(url.spec())); | 222 extracted_index = i; |
| 133 if (i == 0) { | |
| 134 action->set_arg_url(url); | |
| 135 action->set_arg_incognito(is_incognito); | |
| 136 } | |
| 137 } | |
| 138 } else { | |
| 139 LOG(ERROR) << "The tab ID array is malformed at index " << i; | |
| 140 } | 223 } |
| 141 } | 224 } |
| 225 if (extracted_index >= 0) | |
| 226 tab_list->Set(extracted_index, new StringValue(kArgUrlPlaceholder)); | |
| 142 } | 227 } |
| 228 } else { | |
| 229 // No translation needed; just extract the URL directly from a raw string | |
| 230 // or from a dictionary. | |
| 231 DictionaryValue* dict; | |
|
not at google - send to devlin
2013/09/06 17:36:15
= NULL
mvrable
2013/09/06 21:27:42
Done.
| |
| 232 std::string url_string; | |
| 233 | |
| 234 if (api_info->arg_url_lookup) { | |
| 235 if (action->mutable_args()->GetDictionary(url_index, &dict) && | |
| 236 dict->GetString(api_info->arg_url_lookup, &url_string)) { | |
| 237 arg_url = GURL(url_string); | |
| 238 if (arg_url.is_valid()) | |
| 239 dict->SetString(api_info->arg_url_lookup, kArgUrlPlaceholder); | |
| 240 } | |
| 241 } else if (action->args()->GetString(url_index, &url_string)) { | |
| 242 arg_url = GURL(url_string); | |
| 243 if (arg_url.is_valid()) { | |
| 244 action->mutable_args()->Set(url_index, | |
| 245 new StringValue(kArgUrlPlaceholder)); | |
| 246 } | |
| 247 } | |
| 248 } | |
| 249 | |
| 250 if (arg_url.is_valid()) { | |
| 251 action->set_arg_incognito(arg_incognito); | |
| 252 action->set_arg_url(arg_url); | |
| 143 } | 253 } |
| 144 } | 254 } |
| 145 | 255 |
| 146 } // namespace | 256 } // namespace |
| 147 | 257 |
| 148 namespace extensions { | 258 namespace extensions { |
| 149 | 259 |
| 150 // ActivityLogFactory | 260 // ActivityLogFactory |
| 151 | 261 |
| 152 ActivityLogFactory* ActivityLogFactory::GetInstance() { | 262 ActivityLogFactory* ActivityLogFactory::GetInstance() { |
| (...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 335 } | 445 } |
| 336 | 446 |
| 337 // LOG ACTIONS. ---------------------------------------------------------------- | 447 // LOG ACTIONS. ---------------------------------------------------------------- |
| 338 | 448 |
| 339 void ActivityLog::LogAction(scoped_refptr<Action> action) { | 449 void ActivityLog::LogAction(scoped_refptr<Action> action) { |
| 340 if (ActivityLogAPI::IsExtensionWhitelisted(action->extension_id())) | 450 if (ActivityLogAPI::IsExtensionWhitelisted(action->extension_id())) |
| 341 return; | 451 return; |
| 342 | 452 |
| 343 // Perform some preprocessing of the Action data: convert tab IDs to URLs and | 453 // Perform some preprocessing of the Action data: convert tab IDs to URLs and |
| 344 // mask out incognito URLs if appropriate. | 454 // mask out incognito URLs if appropriate. |
| 345 if ((action->action_type() == Action::ACTION_API_CALL || | 455 ExtractUrls(action, profile_); |
| 346 action->action_type() == Action::ACTION_API_EVENT) && | |
| 347 StartsWithASCII(action->api_name(), "tabs.", true)) { | |
| 348 LookupTabIds(action, profile_); | |
| 349 } | |
| 350 | 456 |
| 351 if (IsDatabaseEnabled() && policy_) | 457 if (IsDatabaseEnabled() && policy_) |
| 352 policy_->ProcessAction(action); | 458 policy_->ProcessAction(action); |
| 353 if (IsWatchdogAppActive()) | 459 if (IsWatchdogAppActive()) |
| 354 observers_->Notify(&Observer::OnExtensionActivity, action); | 460 observers_->Notify(&Observer::OnExtensionActivity, action); |
| 355 if (testing_mode_) | 461 if (testing_mode_) |
| 356 LOG(INFO) << action->PrintForDebug(); | 462 LOG(INFO) << action->PrintForDebug(); |
| 357 } | 463 } |
| 358 | 464 |
| 359 void ActivityLog::OnScriptsExecuted( | 465 void ActivityLog::OnScriptsExecuted( |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 450 | 556 |
| 451 void ActivityLog::RemoveURL(const GURL& url) { | 557 void ActivityLog::RemoveURL(const GURL& url) { |
| 452 if (url.is_empty()) | 558 if (url.is_empty()) |
| 453 return; | 559 return; |
| 454 std::vector<GURL> urls; | 560 std::vector<GURL> urls; |
| 455 urls.push_back(url); | 561 urls.push_back(url); |
| 456 RemoveURLs(urls); | 562 RemoveURLs(urls); |
| 457 } | 563 } |
| 458 | 564 |
| 459 } // namespace extensions | 565 } // namespace extensions |
| OLD | NEW |