| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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/api/webrequest/webrequest_api.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/json/json_writer.h" | |
| 11 #include "base/metrics/histogram.h" | |
| 12 #include "base/string_number_conversions.h" | |
| 13 #include "base/string_util.h" | |
| 14 #include "base/time.h" | |
| 15 #include "base/utf_string_conversions.h" | |
| 16 #include "base/values.h" | |
| 17 #include "chrome/browser/browser_process.h" | |
| 18 #include "chrome/browser/chrome_content_browser_client.h" | |
| 19 #include "chrome/browser/extensions/api/webrequest/webrequest_api_constants.h" | |
| 20 #include "chrome/browser/extensions/api/webrequest/webrequest_api_helpers.h" | |
| 21 #include "chrome/browser/extensions/api/webrequest/webrequest_time_tracker.h" | |
| 22 #include "chrome/browser/extensions/extension_event_router.h" | |
| 23 #include "chrome/browser/extensions/extension_info_map.h" | |
| 24 #include "chrome/browser/extensions/extension_prefs.h" | |
| 25 #include "chrome/browser/extensions/extension_service.h" | |
| 26 #include "chrome/browser/extensions/extension_tab_id_map.h" | |
| 27 #include "chrome/browser/profiles/profile.h" | |
| 28 #include "chrome/browser/profiles/profile_manager.h" | |
| 29 #include "chrome/browser/renderer_host/chrome_render_message_filter.h" | |
| 30 #include "chrome/browser/renderer_host/web_cache_manager.h" | |
| 31 #include "chrome/common/extensions/extension.h" | |
| 32 #include "chrome/common/extensions/extension_constants.h" | |
| 33 #include "chrome/common/extensions/extension_error_utils.h" | |
| 34 #include "chrome/common/extensions/extension_messages.h" | |
| 35 #include "chrome/common/extensions/url_pattern.h" | |
| 36 #include "chrome/common/url_constants.h" | |
| 37 #include "content/public/browser/browser_message_filter.h" | |
| 38 #include "content/public/browser/browser_thread.h" | |
| 39 #include "content/public/browser/render_process_host.h" | |
| 40 #include "content/public/browser/resource_request_info.h" | |
| 41 #include "googleurl/src/gurl.h" | |
| 42 #include "grit/generated_resources.h" | |
| 43 #include "net/base/auth.h" | |
| 44 #include "net/base/net_errors.h" | |
| 45 #include "net/base/net_log.h" | |
| 46 #include "net/http/http_response_headers.h" | |
| 47 #include "net/url_request/url_request.h" | |
| 48 #include "ui/base/l10n/l10n_util.h" | |
| 49 | |
| 50 using content::BrowserMessageFilter; | |
| 51 using content::BrowserThread; | |
| 52 using content::ResourceRequestInfo; | |
| 53 | |
| 54 namespace helpers = extension_webrequest_api_helpers; | |
| 55 namespace keys = extension_webrequest_api_constants; | |
| 56 | |
| 57 namespace { | |
| 58 | |
| 59 // List of all the webRequest events. | |
| 60 static const char* const kWebRequestEvents[] = { | |
| 61 keys::kOnBeforeRedirect, | |
| 62 keys::kOnBeforeRequest, | |
| 63 keys::kOnBeforeSendHeaders, | |
| 64 keys::kOnCompleted, | |
| 65 keys::kOnErrorOccurred, | |
| 66 keys::kOnSendHeaders, | |
| 67 keys::kOnAuthRequired, | |
| 68 keys::kOnResponseStarted, | |
| 69 keys::kOnHeadersReceived, | |
| 70 }; | |
| 71 | |
| 72 static const char* kResourceTypeStrings[] = { | |
| 73 "main_frame", | |
| 74 "sub_frame", | |
| 75 "stylesheet", | |
| 76 "script", | |
| 77 "image", | |
| 78 "object", | |
| 79 "xmlhttprequest", | |
| 80 "other", | |
| 81 "other", | |
| 82 }; | |
| 83 | |
| 84 static ResourceType::Type kResourceTypeValues[] = { | |
| 85 ResourceType::MAIN_FRAME, | |
| 86 ResourceType::SUB_FRAME, | |
| 87 ResourceType::STYLESHEET, | |
| 88 ResourceType::SCRIPT, | |
| 89 ResourceType::IMAGE, | |
| 90 ResourceType::OBJECT, | |
| 91 ResourceType::XHR, | |
| 92 ResourceType::LAST_TYPE, // represents "other" | |
| 93 // TODO(jochen): We duplicate the last entry, so the array's size is not a | |
| 94 // power of two. If it is, this triggers a bug in gcc 4.4 in Release builds | |
| 95 // (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43949). Once we use a version | |
| 96 // of gcc with this bug fixed, or the array is changed so this duplicate | |
| 97 // entry is no longer required, this should be removed. | |
| 98 ResourceType::LAST_TYPE, | |
| 99 }; | |
| 100 | |
| 101 COMPILE_ASSERT( | |
| 102 arraysize(kResourceTypeStrings) == arraysize(kResourceTypeValues), | |
| 103 keep_resource_types_in_sync); | |
| 104 | |
| 105 #define ARRAYEND(array) (array + arraysize(array)) | |
| 106 | |
| 107 // Returns the frame ID as it will be passed to the extension: | |
| 108 // 0 if the navigation happens in the main frame, or the frame ID | |
| 109 // modulo 32 bits otherwise. | |
| 110 // Keep this in sync with the GetFrameId() function in | |
| 111 // extension_webnavigation_api.cc. | |
| 112 int GetFrameId(bool is_main_frame, int64 frame_id) { | |
| 113 return is_main_frame ? 0 : static_cast<int>(frame_id); | |
| 114 } | |
| 115 | |
| 116 bool IsWebRequestEvent(const std::string& event_name) { | |
| 117 return std::find(kWebRequestEvents, ARRAYEND(kWebRequestEvents), | |
| 118 event_name) != ARRAYEND(kWebRequestEvents); | |
| 119 } | |
| 120 | |
| 121 // Returns whether |request| has been triggered by an extension in | |
| 122 // |extension_info_map|. | |
| 123 bool IsRequestFromExtension(const net::URLRequest* request, | |
| 124 const ExtensionInfoMap* extension_info_map) { | |
| 125 // |extension_info_map| is NULL for system-level requests. | |
| 126 if (!extension_info_map) | |
| 127 return false; | |
| 128 | |
| 129 const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request); | |
| 130 | |
| 131 // If this request was not created by the ResourceDispatcher, |info| is NULL. | |
| 132 // All requests from extensions are created by the ResourceDispatcher. | |
| 133 if (!info) | |
| 134 return false; | |
| 135 | |
| 136 return extension_info_map->process_map().Contains(info->GetChildID()); | |
| 137 } | |
| 138 | |
| 139 // Returns true if the URL is sensitive and requests to this URL must not be | |
| 140 // modified/canceled by extensions, e.g. because it is targeted to the webstore | |
| 141 // to check for updates, extension blacklisting, etc. | |
| 142 bool IsSensitiveURL(const GURL& url) { | |
| 143 bool is_webstore_gallery_url = | |
| 144 StartsWithASCII(url.spec(), extension_urls::kGalleryBrowsePrefix, true); | |
| 145 bool is_google_com_chrome_url = | |
| 146 EndsWith(url.host(), "google.com", true) && | |
| 147 StartsWithASCII(url.path(), "/chrome", true); | |
| 148 GURL::Replacements replacements; | |
| 149 replacements.ClearQuery(); | |
| 150 replacements.ClearRef(); | |
| 151 GURL url_without_query = url.ReplaceComponents(replacements); | |
| 152 return is_webstore_gallery_url || is_google_com_chrome_url || | |
| 153 extension_urls::IsWebstoreUpdateUrl(url_without_query) || | |
| 154 extension_urls::IsBlacklistUpdateUrl(url); | |
| 155 } | |
| 156 | |
| 157 // Returns true if the scheme is one we want to allow extensions to have access | |
| 158 // to. Extensions still need specific permissions for a given URL, which is | |
| 159 // covered by CanExtensionAccessURL. | |
| 160 bool HasWebRequestScheme(const GURL& url) { | |
| 161 return (url.SchemeIs(chrome::kAboutScheme) || | |
| 162 url.SchemeIs(chrome::kFileScheme) || | |
| 163 url.SchemeIs(chrome::kFtpScheme) || | |
| 164 url.SchemeIs(chrome::kHttpScheme) || | |
| 165 url.SchemeIs(chrome::kHttpsScheme) || | |
| 166 url.SchemeIs(chrome::kExtensionScheme)); | |
| 167 } | |
| 168 | |
| 169 // Returns true if requests for |url| shall not be reported to extensions. | |
| 170 bool HideRequestForURL(const GURL& url) { | |
| 171 return IsSensitiveURL(url) || !HasWebRequestScheme(url); | |
| 172 } | |
| 173 | |
| 174 bool CanExtensionAccessURL(const Extension* extension, const GURL& url) { | |
| 175 // about: URLs are not covered in host permissions, but are allowed anyway. | |
| 176 return (url.SchemeIs(chrome::kAboutScheme) || | |
| 177 extension->HasHostPermission(url) || | |
| 178 url.GetOrigin() == extension->url()); | |
| 179 } | |
| 180 | |
| 181 const char* ResourceTypeToString(ResourceType::Type type) { | |
| 182 ResourceType::Type* iter = | |
| 183 std::find(kResourceTypeValues, ARRAYEND(kResourceTypeValues), type); | |
| 184 if (iter == ARRAYEND(kResourceTypeValues)) | |
| 185 return "other"; | |
| 186 | |
| 187 return kResourceTypeStrings[iter - kResourceTypeValues]; | |
| 188 } | |
| 189 | |
| 190 bool ParseResourceType(const std::string& type_str, | |
| 191 ResourceType::Type* type) { | |
| 192 const char** iter = | |
| 193 std::find(kResourceTypeStrings, ARRAYEND(kResourceTypeStrings), type_str); | |
| 194 if (iter == ARRAYEND(kResourceTypeStrings)) | |
| 195 return false; | |
| 196 *type = kResourceTypeValues[iter - kResourceTypeStrings]; | |
| 197 return true; | |
| 198 } | |
| 199 | |
| 200 void ExtractRequestInfoDetails(net::URLRequest* request, | |
| 201 bool* is_main_frame, | |
| 202 int64* frame_id, | |
| 203 bool* parent_is_main_frame, | |
| 204 int64* parent_frame_id, | |
| 205 int* tab_id, | |
| 206 int* window_id, | |
| 207 ResourceType::Type* resource_type) { | |
| 208 if (!request->GetUserData(NULL)) | |
| 209 return; | |
| 210 | |
| 211 const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request); | |
| 212 ExtensionTabIdMap::GetInstance()->GetTabAndWindowId( | |
| 213 info->GetChildID(), info->GetRouteID(), tab_id, window_id); | |
| 214 *frame_id = info->GetFrameID(); | |
| 215 *is_main_frame = info->IsMainFrame(); | |
| 216 *parent_frame_id = info->GetParentFrameID(); | |
| 217 *parent_is_main_frame = info->ParentIsMainFrame(); | |
| 218 | |
| 219 // Restrict the resource type to the values we care about. | |
| 220 ResourceType::Type* iter = | |
| 221 std::find(kResourceTypeValues, ARRAYEND(kResourceTypeValues), | |
| 222 info->GetResourceType()); | |
| 223 *resource_type = (iter != ARRAYEND(kResourceTypeValues)) ? | |
| 224 *iter : ResourceType::LAST_TYPE; | |
| 225 } | |
| 226 | |
| 227 // Extracts from |request| information for the keys requestId, url, method, | |
| 228 // frameId, tabId, type, and timeStamp and writes these into |out| to be passed | |
| 229 // on to extensions. | |
| 230 void ExtractRequestInfo(net::URLRequest* request, DictionaryValue* out) { | |
| 231 bool is_main_frame = false; | |
| 232 int64 frame_id = -1; | |
| 233 bool parent_is_main_frame = false; | |
| 234 int64 parent_frame_id = -1; | |
| 235 int frame_id_for_extension = -1; | |
| 236 int parent_frame_id_for_extension = -1; | |
| 237 int tab_id = -1; | |
| 238 int window_id = -1; | |
| 239 ResourceType::Type resource_type = ResourceType::LAST_TYPE; | |
| 240 ExtractRequestInfoDetails(request, &is_main_frame, &frame_id, | |
| 241 &parent_is_main_frame, &parent_frame_id, &tab_id, | |
| 242 &window_id, &resource_type); | |
| 243 frame_id_for_extension = GetFrameId(is_main_frame, frame_id); | |
| 244 parent_frame_id_for_extension = GetFrameId(parent_is_main_frame, | |
| 245 parent_frame_id); | |
| 246 | |
| 247 out->SetString(keys::kRequestIdKey, | |
| 248 base::Uint64ToString(request->identifier())); | |
| 249 out->SetString(keys::kUrlKey, request->url().spec()); | |
| 250 out->SetString(keys::kMethodKey, request->method()); | |
| 251 out->SetInteger(keys::kFrameIdKey, frame_id_for_extension); | |
| 252 out->SetInteger(keys::kParentFrameIdKey, parent_frame_id_for_extension); | |
| 253 out->SetInteger(keys::kTabIdKey, tab_id); | |
| 254 out->SetString(keys::kTypeKey, ResourceTypeToString(resource_type)); | |
| 255 out->SetDouble(keys::kTimeStampKey, base::Time::Now().ToDoubleT() * 1000); | |
| 256 } | |
| 257 | |
| 258 // Converts a HttpHeaders dictionary to a |name|, |value| pair. Returns | |
| 259 // true if successful. | |
| 260 bool FromHeaderDictionary(const DictionaryValue* header_value, | |
| 261 std::string* name, | |
| 262 std::string* value) { | |
| 263 if (!header_value->GetString(keys::kHeaderNameKey, name)) | |
| 264 return false; | |
| 265 | |
| 266 // We require either a "value" or a "binaryValue" entry. | |
| 267 if (!(header_value->HasKey(keys::kHeaderValueKey) ^ | |
| 268 header_value->HasKey(keys::kHeaderBinaryValueKey))) | |
| 269 return false; | |
| 270 | |
| 271 if (header_value->HasKey(keys::kHeaderValueKey)) { | |
| 272 if (!header_value->GetString(keys::kHeaderValueKey, value)) { | |
| 273 return false; | |
| 274 } | |
| 275 } else if (header_value->HasKey(keys::kHeaderBinaryValueKey)) { | |
| 276 ListValue* list = NULL; | |
| 277 if (!header_value->GetList(keys::kHeaderBinaryValueKey, &list) || | |
| 278 !helpers::CharListToString(list, value)) { | |
| 279 return false; | |
| 280 } | |
| 281 } | |
| 282 return true; | |
| 283 } | |
| 284 | |
| 285 // Converts the |name|, |value| pair of a http header to a HttpHeaders | |
| 286 // dictionary. Ownership is passed to the caller. | |
| 287 DictionaryValue* ToHeaderDictionary(const std::string& name, | |
| 288 const std::string& value) { | |
| 289 DictionaryValue* header = new DictionaryValue(); | |
| 290 header->SetString(keys::kHeaderNameKey, name); | |
| 291 if (IsStringUTF8(value)) { | |
| 292 header->SetString(keys::kHeaderValueKey, value); | |
| 293 } else { | |
| 294 header->Set(keys::kHeaderBinaryValueKey, | |
| 295 helpers::StringToCharList(value)); | |
| 296 } | |
| 297 return header; | |
| 298 } | |
| 299 | |
| 300 // Creates a list of HttpHeaders (see the extension API JSON). If |headers| is | |
| 301 // NULL, the list is empty. Ownership is passed to the caller. | |
| 302 ListValue* GetResponseHeadersList(const net::HttpResponseHeaders* headers) { | |
| 303 ListValue* headers_value = new ListValue(); | |
| 304 if (headers) { | |
| 305 void* iter = NULL; | |
| 306 std::string name; | |
| 307 std::string value; | |
| 308 while (headers->EnumerateHeaderLines(&iter, &name, &value)) | |
| 309 headers_value->Append(ToHeaderDictionary(name, value)); | |
| 310 } | |
| 311 return headers_value; | |
| 312 } | |
| 313 | |
| 314 ListValue* GetRequestHeadersList(const net::HttpRequestHeaders& headers) { | |
| 315 ListValue* headers_value = new ListValue(); | |
| 316 for (net::HttpRequestHeaders::Iterator it(headers); it.GetNext(); ) | |
| 317 headers_value->Append(ToHeaderDictionary(it.name(), it.value())); | |
| 318 return headers_value; | |
| 319 } | |
| 320 | |
| 321 // Creates a StringValue with the status line of |headers|. If |headers| is | |
| 322 // NULL, an empty string is returned. Ownership is passed to the caller. | |
| 323 StringValue* GetStatusLine(net::HttpResponseHeaders* headers) { | |
| 324 return new StringValue(headers ? headers->GetStatusLine() : ""); | |
| 325 } | |
| 326 | |
| 327 void NotifyWebRequestAPIUsed(void* profile_id, const Extension* extension) { | |
| 328 Profile* profile = reinterpret_cast<Profile*>(profile_id); | |
| 329 if (!g_browser_process->profile_manager()->IsValidProfile(profile)) | |
| 330 return; | |
| 331 | |
| 332 if (profile->GetExtensionService()->HasUsedWebRequest(extension)) | |
| 333 return; | |
| 334 profile->GetExtensionService()->SetHasUsedWebRequest(extension, true); | |
| 335 | |
| 336 content::BrowserContext* browser_context = profile; | |
| 337 for (content::RenderProcessHost::iterator it = | |
| 338 content::RenderProcessHost::AllHostsIterator(); | |
| 339 !it.IsAtEnd(); it.Advance()) { | |
| 340 content::RenderProcessHost* host = it.GetCurrentValue(); | |
| 341 if (host->GetBrowserContext() == browser_context) | |
| 342 SendExtensionWebRequestStatusToHost(host); | |
| 343 } | |
| 344 } | |
| 345 | |
| 346 void ClearCacheOnNavigationOnUI() { | |
| 347 WebCacheManager::GetInstance()->ClearCacheOnNavigation(); | |
| 348 } | |
| 349 | |
| 350 } // namespace | |
| 351 | |
| 352 // Represents a single unique listener to an event, along with whatever filter | |
| 353 // parameters and extra_info_spec were specified at the time the listener was | |
| 354 // added. | |
| 355 struct ExtensionWebRequestEventRouter::EventListener { | |
| 356 std::string extension_id; | |
| 357 std::string extension_name; | |
| 358 std::string sub_event_name; | |
| 359 RequestFilter filter; | |
| 360 int extra_info_spec; | |
| 361 base::WeakPtr<IPC::Message::Sender> ipc_sender; | |
| 362 mutable std::set<uint64> blocked_requests; | |
| 363 | |
| 364 // Comparator to work with std::set. | |
| 365 bool operator<(const EventListener& that) const { | |
| 366 if (extension_id < that.extension_id) | |
| 367 return true; | |
| 368 if (extension_id == that.extension_id && | |
| 369 sub_event_name < that.sub_event_name) | |
| 370 return true; | |
| 371 return false; | |
| 372 } | |
| 373 | |
| 374 EventListener() : extra_info_spec(0) {} | |
| 375 }; | |
| 376 | |
| 377 // Contains info about requests that are blocked waiting for a response from | |
| 378 // an extension. | |
| 379 struct ExtensionWebRequestEventRouter::BlockedRequest { | |
| 380 // The request that is being blocked. | |
| 381 net::URLRequest* request; | |
| 382 | |
| 383 // The event that we're currently blocked on. | |
| 384 EventTypes event; | |
| 385 | |
| 386 // The number of event handlers that we are awaiting a response from. | |
| 387 int num_handlers_blocking; | |
| 388 | |
| 389 // Pointer to NetLog to report significant changes to the request for | |
| 390 // debugging. | |
| 391 const net::BoundNetLog* net_log; | |
| 392 | |
| 393 // The callback to call when we get a response from all event handlers. | |
| 394 net::CompletionCallback callback; | |
| 395 | |
| 396 // If non-empty, this contains the new URL that the request will redirect to. | |
| 397 // Only valid for OnBeforeRequest. | |
| 398 GURL* new_url; | |
| 399 | |
| 400 // The request headers that will be issued along with this request. Only valid | |
| 401 // for OnBeforeSendHeaders. | |
| 402 net::HttpRequestHeaders* request_headers; | |
| 403 | |
| 404 // The response headers that were received from the server. Only valid for | |
| 405 // OnHeadersReceived. | |
| 406 scoped_refptr<net::HttpResponseHeaders> original_response_headers; | |
| 407 | |
| 408 // Location where to override response headers. Only valid for | |
| 409 // OnHeadersReceived. | |
| 410 scoped_refptr<net::HttpResponseHeaders>* override_response_headers; | |
| 411 | |
| 412 // If non-empty, this contains the auth credentials that may be filled in. | |
| 413 // Only valid for OnAuthRequired. | |
| 414 net::AuthCredentials* auth_credentials; | |
| 415 | |
| 416 // The callback to invoke for auth. If |auth_callback.is_null()| is false, | |
| 417 // |callback| must be NULL. | |
| 418 // Only valid for OnAuthRequired. | |
| 419 net::NetworkDelegate::AuthCallback auth_callback; | |
| 420 | |
| 421 // Time the request was paused. Used for logging purposes. | |
| 422 base::Time blocking_time; | |
| 423 | |
| 424 // Changes requested by extensions. | |
| 425 helpers::EventResponseDeltas response_deltas; | |
| 426 | |
| 427 BlockedRequest() | |
| 428 : request(NULL), | |
| 429 event(kInvalidEvent), | |
| 430 num_handlers_blocking(0), | |
| 431 net_log(NULL), | |
| 432 new_url(NULL), | |
| 433 request_headers(NULL), | |
| 434 override_response_headers(NULL), | |
| 435 auth_credentials(NULL) {} | |
| 436 }; | |
| 437 | |
| 438 bool ExtensionWebRequestEventRouter::RequestFilter::InitFromValue( | |
| 439 const DictionaryValue& value, std::string* error) { | |
| 440 if (!value.HasKey("urls")) | |
| 441 return false; | |
| 442 | |
| 443 for (DictionaryValue::key_iterator key = value.begin_keys(); | |
| 444 key != value.end_keys(); ++key) { | |
| 445 if (*key == "urls") { | |
| 446 ListValue* urls_value = NULL; | |
| 447 if (!value.GetList("urls", &urls_value)) | |
| 448 return false; | |
| 449 for (size_t i = 0; i < urls_value->GetSize(); ++i) { | |
| 450 std::string url; | |
| 451 URLPattern pattern( | |
| 452 URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS | | |
| 453 URLPattern::SCHEME_FTP | URLPattern::SCHEME_FILE | | |
| 454 URLPattern::SCHEME_EXTENSION); | |
| 455 if (!urls_value->GetString(i, &url) || | |
| 456 pattern.Parse(url) != URLPattern::PARSE_SUCCESS) { | |
| 457 *error = ExtensionErrorUtils::FormatErrorMessage( | |
| 458 keys::kInvalidRequestFilterUrl, url); | |
| 459 return false; | |
| 460 } | |
| 461 urls.AddPattern(pattern); | |
| 462 } | |
| 463 } else if (*key == "types") { | |
| 464 ListValue* types_value = NULL; | |
| 465 if (!value.GetList("types", &types_value)) | |
| 466 return false; | |
| 467 for (size_t i = 0; i < types_value->GetSize(); ++i) { | |
| 468 std::string type_str; | |
| 469 ResourceType::Type type; | |
| 470 if (!types_value->GetString(i, &type_str) || | |
| 471 !ParseResourceType(type_str, &type)) | |
| 472 return false; | |
| 473 types.push_back(type); | |
| 474 } | |
| 475 } else if (*key == "tabId") { | |
| 476 if (!value.GetInteger("tabId", &tab_id)) | |
| 477 return false; | |
| 478 } else if (*key == "windowId") { | |
| 479 if (!value.GetInteger("windowId", &window_id)) | |
| 480 return false; | |
| 481 } else { | |
| 482 return false; | |
| 483 } | |
| 484 } | |
| 485 return true; | |
| 486 } | |
| 487 | |
| 488 // static | |
| 489 bool ExtensionWebRequestEventRouter::ExtraInfoSpec::InitFromValue( | |
| 490 const ListValue& value, int* extra_info_spec) { | |
| 491 *extra_info_spec = 0; | |
| 492 for (size_t i = 0; i < value.GetSize(); ++i) { | |
| 493 std::string str; | |
| 494 if (!value.GetString(i, &str)) | |
| 495 return false; | |
| 496 | |
| 497 if (str == "requestHeaders") | |
| 498 *extra_info_spec |= REQUEST_HEADERS; | |
| 499 else if (str == "responseHeaders") | |
| 500 *extra_info_spec |= RESPONSE_HEADERS; | |
| 501 else if (str == "blocking") | |
| 502 *extra_info_spec |= BLOCKING; | |
| 503 else if (str == "asyncBlocking") | |
| 504 *extra_info_spec |= ASYNC_BLOCKING; | |
| 505 else | |
| 506 return false; | |
| 507 | |
| 508 // BLOCKING and ASYNC_BLOCKING are mutually exclusive. | |
| 509 if ((*extra_info_spec & BLOCKING) && (*extra_info_spec & ASYNC_BLOCKING)) | |
| 510 return false; | |
| 511 } | |
| 512 return true; | |
| 513 } | |
| 514 | |
| 515 | |
| 516 ExtensionWebRequestEventRouter::EventResponse::EventResponse( | |
| 517 const std::string& extension_id, const base::Time& extension_install_time) | |
| 518 : extension_id(extension_id), | |
| 519 extension_install_time(extension_install_time), | |
| 520 cancel(false) { | |
| 521 } | |
| 522 | |
| 523 ExtensionWebRequestEventRouter::EventResponse::~EventResponse() { | |
| 524 } | |
| 525 | |
| 526 | |
| 527 ExtensionWebRequestEventRouter::RequestFilter::RequestFilter() | |
| 528 : tab_id(-1), window_id(-1) { | |
| 529 } | |
| 530 | |
| 531 ExtensionWebRequestEventRouter::RequestFilter::~RequestFilter() { | |
| 532 } | |
| 533 | |
| 534 // | |
| 535 // ExtensionWebRequestEventRouter | |
| 536 // | |
| 537 | |
| 538 // static | |
| 539 ExtensionWebRequestEventRouter* ExtensionWebRequestEventRouter::GetInstance() { | |
| 540 return Singleton<ExtensionWebRequestEventRouter>::get(); | |
| 541 } | |
| 542 | |
| 543 ExtensionWebRequestEventRouter::ExtensionWebRequestEventRouter() | |
| 544 : request_time_tracker_(new ExtensionWebRequestTimeTracker) { | |
| 545 } | |
| 546 | |
| 547 ExtensionWebRequestEventRouter::~ExtensionWebRequestEventRouter() { | |
| 548 } | |
| 549 | |
| 550 int ExtensionWebRequestEventRouter::OnBeforeRequest( | |
| 551 void* profile, | |
| 552 ExtensionInfoMap* extension_info_map, | |
| 553 net::URLRequest* request, | |
| 554 const net::CompletionCallback& callback, | |
| 555 GURL* new_url) { | |
| 556 // We hide events from the system context as well as sensitive requests. | |
| 557 if (!profile || HideRequestForURL(request->url())) | |
| 558 return net::OK; | |
| 559 | |
| 560 if (IsPageLoad(request)) | |
| 561 NotifyPageLoad(); | |
| 562 | |
| 563 request_time_tracker_->LogRequestStartTime(request->identifier(), | |
| 564 base::Time::Now(), | |
| 565 request->url(), | |
| 566 profile); | |
| 567 | |
| 568 int extra_info_spec = 0; | |
| 569 std::vector<const EventListener*> listeners = | |
| 570 GetMatchingListeners(profile, extension_info_map, keys::kOnBeforeRequest, | |
| 571 request, &extra_info_spec); | |
| 572 if (listeners.empty()) | |
| 573 return net::OK; | |
| 574 | |
| 575 if (GetAndSetSignaled(request->identifier(), kOnBeforeRequest)) | |
| 576 return net::OK; | |
| 577 | |
| 578 ListValue args; | |
| 579 DictionaryValue* dict = new DictionaryValue(); | |
| 580 ExtractRequestInfo(request, dict); | |
| 581 args.Append(dict); | |
| 582 | |
| 583 if (DispatchEvent(profile, request, listeners, args)) { | |
| 584 blocked_requests_[request->identifier()].event = kOnBeforeRequest; | |
| 585 blocked_requests_[request->identifier()].callback = callback; | |
| 586 blocked_requests_[request->identifier()].new_url = new_url; | |
| 587 blocked_requests_[request->identifier()].net_log = &request->net_log(); | |
| 588 return net::ERR_IO_PENDING; | |
| 589 } | |
| 590 return net::OK; | |
| 591 } | |
| 592 | |
| 593 int ExtensionWebRequestEventRouter::OnBeforeSendHeaders( | |
| 594 void* profile, | |
| 595 ExtensionInfoMap* extension_info_map, | |
| 596 net::URLRequest* request, | |
| 597 const net::CompletionCallback& callback, | |
| 598 net::HttpRequestHeaders* headers) { | |
| 599 // We hide events from the system context as well as sensitive requests. | |
| 600 if (!profile || HideRequestForURL(request->url())) | |
| 601 return net::OK; | |
| 602 | |
| 603 if (GetAndSetSignaled(request->identifier(), kOnBeforeSendHeaders)) | |
| 604 return net::OK; | |
| 605 | |
| 606 int extra_info_spec = 0; | |
| 607 std::vector<const EventListener*> listeners = | |
| 608 GetMatchingListeners(profile, extension_info_map, | |
| 609 keys::kOnBeforeSendHeaders, request, | |
| 610 &extra_info_spec); | |
| 611 if (listeners.empty()) | |
| 612 return net::OK; | |
| 613 | |
| 614 ListValue args; | |
| 615 DictionaryValue* dict = new DictionaryValue(); | |
| 616 ExtractRequestInfo(request, dict); | |
| 617 if (extra_info_spec & ExtraInfoSpec::REQUEST_HEADERS) | |
| 618 dict->Set(keys::kRequestHeadersKey, GetRequestHeadersList(*headers)); | |
| 619 args.Append(dict); | |
| 620 | |
| 621 if (DispatchEvent(profile, request, listeners, args)) { | |
| 622 blocked_requests_[request->identifier()].event = kOnBeforeSendHeaders; | |
| 623 blocked_requests_[request->identifier()].callback = callback; | |
| 624 blocked_requests_[request->identifier()].request_headers = headers; | |
| 625 blocked_requests_[request->identifier()].net_log = &request->net_log(); | |
| 626 return net::ERR_IO_PENDING; | |
| 627 } | |
| 628 return net::OK; | |
| 629 } | |
| 630 | |
| 631 void ExtensionWebRequestEventRouter::OnSendHeaders( | |
| 632 void* profile, | |
| 633 ExtensionInfoMap* extension_info_map, | |
| 634 net::URLRequest* request, | |
| 635 const net::HttpRequestHeaders& headers) { | |
| 636 // We hide events from the system context as well as sensitive requests. | |
| 637 if (!profile || HideRequestForURL(request->url())) | |
| 638 return; | |
| 639 | |
| 640 if (GetAndSetSignaled(request->identifier(), kOnSendHeaders)) | |
| 641 return; | |
| 642 | |
| 643 ClearSignaled(request->identifier(), kOnBeforeRedirect); | |
| 644 | |
| 645 int extra_info_spec = 0; | |
| 646 std::vector<const EventListener*> listeners = | |
| 647 GetMatchingListeners(profile, extension_info_map, | |
| 648 keys::kOnSendHeaders, request, &extra_info_spec); | |
| 649 if (listeners.empty()) | |
| 650 return; | |
| 651 | |
| 652 ListValue args; | |
| 653 DictionaryValue* dict = new DictionaryValue(); | |
| 654 ExtractRequestInfo(request, dict); | |
| 655 if (extra_info_spec & ExtraInfoSpec::REQUEST_HEADERS) | |
| 656 dict->Set(keys::kRequestHeadersKey, GetRequestHeadersList(headers)); | |
| 657 args.Append(dict); | |
| 658 | |
| 659 DispatchEvent(profile, request, listeners, args); | |
| 660 } | |
| 661 | |
| 662 int ExtensionWebRequestEventRouter::OnHeadersReceived( | |
| 663 void* profile, | |
| 664 ExtensionInfoMap* extension_info_map, | |
| 665 net::URLRequest* request, | |
| 666 const net::CompletionCallback& callback, | |
| 667 net::HttpResponseHeaders* original_response_headers, | |
| 668 scoped_refptr<net::HttpResponseHeaders>* override_response_headers) { | |
| 669 // We hide events from the system context as well as sensitive requests. | |
| 670 if (!profile || HideRequestForURL(request->url())) | |
| 671 return net::OK; | |
| 672 | |
| 673 int extra_info_spec = 0; | |
| 674 std::vector<const EventListener*> listeners = | |
| 675 GetMatchingListeners(profile, extension_info_map, | |
| 676 keys::kOnHeadersReceived, request, | |
| 677 &extra_info_spec); | |
| 678 | |
| 679 if (listeners.empty()) | |
| 680 return net::OK; | |
| 681 | |
| 682 if (GetAndSetSignaled(request->identifier(), kOnHeadersReceived)) | |
| 683 return net::OK; | |
| 684 | |
| 685 ListValue args; | |
| 686 DictionaryValue* dict = new DictionaryValue(); | |
| 687 ExtractRequestInfo(request, dict); | |
| 688 dict->SetString(keys::kStatusLineKey, | |
| 689 original_response_headers->GetStatusLine()); | |
| 690 if (extra_info_spec & ExtraInfoSpec::RESPONSE_HEADERS) { | |
| 691 dict->Set(keys::kResponseHeadersKey, | |
| 692 GetResponseHeadersList(original_response_headers)); | |
| 693 } | |
| 694 args.Append(dict); | |
| 695 | |
| 696 if (DispatchEvent(profile, request, listeners, args)) { | |
| 697 blocked_requests_[request->identifier()].event = kOnHeadersReceived; | |
| 698 blocked_requests_[request->identifier()].callback = callback; | |
| 699 blocked_requests_[request->identifier()].net_log = &request->net_log(); | |
| 700 blocked_requests_[request->identifier()].override_response_headers = | |
| 701 override_response_headers; | |
| 702 blocked_requests_[request->identifier()].original_response_headers = | |
| 703 original_response_headers; | |
| 704 return net::ERR_IO_PENDING; | |
| 705 } | |
| 706 return net::OK; | |
| 707 } | |
| 708 | |
| 709 net::NetworkDelegate::AuthRequiredResponse | |
| 710 ExtensionWebRequestEventRouter::OnAuthRequired( | |
| 711 void* profile, | |
| 712 ExtensionInfoMap* extension_info_map, | |
| 713 net::URLRequest* request, | |
| 714 const net::AuthChallengeInfo& auth_info, | |
| 715 const net::NetworkDelegate::AuthCallback& callback, | |
| 716 net::AuthCredentials* credentials) { | |
| 717 // No profile means that this is for authentication challenges in the | |
| 718 // system context. Skip in that case. Also skip sensitive requests. | |
| 719 if (!profile || HideRequestForURL(request->url())) | |
| 720 return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION; | |
| 721 | |
| 722 int extra_info_spec = 0; | |
| 723 std::vector<const EventListener*> listeners = | |
| 724 GetMatchingListeners(profile, extension_info_map, | |
| 725 keys::kOnAuthRequired, request, &extra_info_spec); | |
| 726 if (listeners.empty()) | |
| 727 return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION; | |
| 728 | |
| 729 ListValue args; | |
| 730 DictionaryValue* dict = new DictionaryValue(); | |
| 731 ExtractRequestInfo(request, dict); | |
| 732 dict->SetBoolean(keys::kIsProxyKey, auth_info.is_proxy); | |
| 733 if (!auth_info.scheme.empty()) | |
| 734 dict->SetString(keys::kSchemeKey, auth_info.scheme); | |
| 735 if (!auth_info.realm.empty()) | |
| 736 dict->SetString(keys::kRealmKey, auth_info.realm); | |
| 737 DictionaryValue* challenger = new DictionaryValue(); | |
| 738 challenger->SetString(keys::kHostKey, auth_info.challenger.host()); | |
| 739 challenger->SetInteger(keys::kPortKey, auth_info.challenger.port()); | |
| 740 dict->Set(keys::kChallengerKey, challenger); | |
| 741 dict->Set(keys::kStatusLineKey, GetStatusLine(request->response_headers())); | |
| 742 if (extra_info_spec & ExtraInfoSpec::RESPONSE_HEADERS) { | |
| 743 dict->Set(keys::kResponseHeadersKey, | |
| 744 GetResponseHeadersList(request->response_headers())); | |
| 745 } | |
| 746 args.Append(dict); | |
| 747 | |
| 748 if (DispatchEvent(profile, request, listeners, args)) { | |
| 749 blocked_requests_[request->identifier()].event = kOnAuthRequired; | |
| 750 blocked_requests_[request->identifier()].auth_callback = callback; | |
| 751 blocked_requests_[request->identifier()].auth_credentials = credentials; | |
| 752 blocked_requests_[request->identifier()].net_log = &request->net_log(); | |
| 753 return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_IO_PENDING; | |
| 754 } | |
| 755 return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION; | |
| 756 } | |
| 757 | |
| 758 void ExtensionWebRequestEventRouter::OnBeforeRedirect( | |
| 759 void* profile, | |
| 760 ExtensionInfoMap* extension_info_map, | |
| 761 net::URLRequest* request, | |
| 762 const GURL& new_location) { | |
| 763 // We hide events from the system context as well as sensitive requests. | |
| 764 if (!profile || HideRequestForURL(request->url())) | |
| 765 return; | |
| 766 | |
| 767 if (GetAndSetSignaled(request->identifier(), kOnBeforeRedirect)) | |
| 768 return; | |
| 769 | |
| 770 ClearSignaled(request->identifier(), kOnBeforeRequest); | |
| 771 ClearSignaled(request->identifier(), kOnBeforeSendHeaders); | |
| 772 ClearSignaled(request->identifier(), kOnSendHeaders); | |
| 773 ClearSignaled(request->identifier(), kOnHeadersReceived); | |
| 774 | |
| 775 int extra_info_spec = 0; | |
| 776 std::vector<const EventListener*> listeners = | |
| 777 GetMatchingListeners(profile, extension_info_map, | |
| 778 keys::kOnBeforeRedirect, request, &extra_info_spec); | |
| 779 if (listeners.empty()) | |
| 780 return; | |
| 781 | |
| 782 int http_status_code = request->GetResponseCode(); | |
| 783 | |
| 784 std::string response_ip = request->GetSocketAddress().host(); | |
| 785 | |
| 786 ListValue args; | |
| 787 DictionaryValue* dict = new DictionaryValue(); | |
| 788 ExtractRequestInfo(request, dict); | |
| 789 dict->SetString(keys::kRedirectUrlKey, new_location.spec()); | |
| 790 dict->SetInteger(keys::kStatusCodeKey, http_status_code); | |
| 791 if (!response_ip.empty()) | |
| 792 dict->SetString(keys::kIpKey, response_ip); | |
| 793 dict->SetBoolean(keys::kFromCache, request->was_cached()); | |
| 794 dict->Set(keys::kStatusLineKey, GetStatusLine(request->response_headers())); | |
| 795 if (extra_info_spec & ExtraInfoSpec::RESPONSE_HEADERS) { | |
| 796 dict->Set(keys::kResponseHeadersKey, | |
| 797 GetResponseHeadersList(request->response_headers())); | |
| 798 } | |
| 799 args.Append(dict); | |
| 800 | |
| 801 DispatchEvent(profile, request, listeners, args); | |
| 802 } | |
| 803 | |
| 804 void ExtensionWebRequestEventRouter::OnResponseStarted( | |
| 805 void* profile, | |
| 806 ExtensionInfoMap* extension_info_map, | |
| 807 net::URLRequest* request) { | |
| 808 // We hide events from the system context as well as sensitive requests. | |
| 809 if (!profile || HideRequestForURL(request->url())) | |
| 810 return; | |
| 811 | |
| 812 // OnResponseStarted is even triggered, when the request was cancelled. | |
| 813 if (request->status().status() != net::URLRequestStatus::SUCCESS) | |
| 814 return; | |
| 815 | |
| 816 int extra_info_spec = 0; | |
| 817 std::vector<const EventListener*> listeners = | |
| 818 GetMatchingListeners(profile, extension_info_map, | |
| 819 keys::kOnResponseStarted, request, &extra_info_spec); | |
| 820 if (listeners.empty()) | |
| 821 return; | |
| 822 | |
| 823 // UrlRequestFileJobs do not send headers, so we simulate their behavior. | |
| 824 int response_code = 200; | |
| 825 if (request->response_headers()) | |
| 826 response_code = request->response_headers()->response_code(); | |
| 827 | |
| 828 std::string response_ip = request->GetSocketAddress().host(); | |
| 829 | |
| 830 ListValue args; | |
| 831 DictionaryValue* dict = new DictionaryValue(); | |
| 832 ExtractRequestInfo(request, dict); | |
| 833 if (!response_ip.empty()) | |
| 834 dict->SetString(keys::kIpKey, response_ip); | |
| 835 dict->SetBoolean(keys::kFromCache, request->was_cached()); | |
| 836 dict->SetInteger(keys::kStatusCodeKey, response_code); | |
| 837 dict->Set(keys::kStatusLineKey, GetStatusLine(request->response_headers())); | |
| 838 if (extra_info_spec & ExtraInfoSpec::RESPONSE_HEADERS) { | |
| 839 dict->Set(keys::kResponseHeadersKey, | |
| 840 GetResponseHeadersList(request->response_headers())); | |
| 841 } | |
| 842 args.Append(dict); | |
| 843 | |
| 844 DispatchEvent(profile, request, listeners, args); | |
| 845 } | |
| 846 | |
| 847 void ExtensionWebRequestEventRouter::OnCompleted( | |
| 848 void* profile, | |
| 849 ExtensionInfoMap* extension_info_map, | |
| 850 net::URLRequest* request) { | |
| 851 // We hide events from the system context as well as sensitive requests. | |
| 852 if (!profile || HideRequestForURL(request->url())) | |
| 853 return; | |
| 854 | |
| 855 request_time_tracker_->LogRequestEndTime(request->identifier(), | |
| 856 base::Time::Now()); | |
| 857 | |
| 858 DCHECK(request->status().status() == net::URLRequestStatus::SUCCESS); | |
| 859 | |
| 860 DCHECK(!GetAndSetSignaled(request->identifier(), kOnCompleted)); | |
| 861 | |
| 862 ClearPendingCallbacks(request); | |
| 863 | |
| 864 int extra_info_spec = 0; | |
| 865 std::vector<const EventListener*> listeners = | |
| 866 GetMatchingListeners(profile, extension_info_map, | |
| 867 keys::kOnCompleted, request, &extra_info_spec); | |
| 868 if (listeners.empty()) | |
| 869 return; | |
| 870 | |
| 871 // UrlRequestFileJobs do not send headers, so we simulate their behavior. | |
| 872 int response_code = 200; | |
| 873 if (request->response_headers()) | |
| 874 response_code = request->response_headers()->response_code(); | |
| 875 | |
| 876 std::string response_ip = request->GetSocketAddress().host(); | |
| 877 | |
| 878 ListValue args; | |
| 879 DictionaryValue* dict = new DictionaryValue(); | |
| 880 ExtractRequestInfo(request, dict); | |
| 881 dict->SetInteger(keys::kStatusCodeKey, response_code); | |
| 882 if (!response_ip.empty()) | |
| 883 dict->SetString(keys::kIpKey, response_ip); | |
| 884 dict->SetBoolean(keys::kFromCache, request->was_cached()); | |
| 885 dict->Set(keys::kStatusLineKey, GetStatusLine(request->response_headers())); | |
| 886 if (extra_info_spec & ExtraInfoSpec::RESPONSE_HEADERS) { | |
| 887 dict->Set(keys::kResponseHeadersKey, | |
| 888 GetResponseHeadersList(request->response_headers())); | |
| 889 } | |
| 890 args.Append(dict); | |
| 891 | |
| 892 DispatchEvent(profile, request, listeners, args); | |
| 893 } | |
| 894 | |
| 895 void ExtensionWebRequestEventRouter::OnErrorOccurred( | |
| 896 void* profile, | |
| 897 ExtensionInfoMap* extension_info_map, | |
| 898 net::URLRequest* request, | |
| 899 bool started) { | |
| 900 // We hide events from the system context as well as sensitive requests. | |
| 901 if (!profile || HideRequestForURL(request->url())) | |
| 902 return; | |
| 903 | |
| 904 request_time_tracker_->LogRequestEndTime(request->identifier(), | |
| 905 base::Time::Now()); | |
| 906 | |
| 907 DCHECK(request->status().status() == net::URLRequestStatus::FAILED || | |
| 908 request->status().status() == net::URLRequestStatus::CANCELED); | |
| 909 | |
| 910 DCHECK(!GetAndSetSignaled(request->identifier(), kOnErrorOccurred)); | |
| 911 | |
| 912 ClearPendingCallbacks(request); | |
| 913 | |
| 914 int extra_info_spec = 0; | |
| 915 std::vector<const EventListener*> listeners = | |
| 916 GetMatchingListeners(profile, extension_info_map, | |
| 917 keys::kOnErrorOccurred, request, &extra_info_spec); | |
| 918 if (listeners.empty()) | |
| 919 return; | |
| 920 | |
| 921 ListValue args; | |
| 922 DictionaryValue* dict = new DictionaryValue(); | |
| 923 ExtractRequestInfo(request, dict); | |
| 924 if (started) { | |
| 925 std::string response_ip = request->GetSocketAddress().host(); | |
| 926 if (!response_ip.empty()) | |
| 927 dict->SetString(keys::kIpKey, response_ip); | |
| 928 } | |
| 929 dict->SetBoolean(keys::kFromCache, request->was_cached()); | |
| 930 dict->SetString(keys::kErrorKey, | |
| 931 net::ErrorToString(request->status().error())); | |
| 932 args.Append(dict); | |
| 933 | |
| 934 DispatchEvent(profile, request, listeners, args); | |
| 935 } | |
| 936 | |
| 937 void ExtensionWebRequestEventRouter::OnURLRequestDestroyed( | |
| 938 void* profile, net::URLRequest* request) { | |
| 939 ClearPendingCallbacks(request); | |
| 940 | |
| 941 signaled_requests_.erase(request->identifier()); | |
| 942 | |
| 943 request_time_tracker_->LogRequestEndTime(request->identifier(), | |
| 944 base::Time::Now()); | |
| 945 } | |
| 946 | |
| 947 void ExtensionWebRequestEventRouter::ClearPendingCallbacks( | |
| 948 net::URLRequest* request) { | |
| 949 blocked_requests_.erase(request->identifier()); | |
| 950 } | |
| 951 | |
| 952 bool ExtensionWebRequestEventRouter::DispatchEvent( | |
| 953 void* profile, | |
| 954 net::URLRequest* request, | |
| 955 const std::vector<const EventListener*>& listeners, | |
| 956 const ListValue& args) { | |
| 957 std::string json_args; | |
| 958 | |
| 959 // TODO(mpcomplete): Consider consolidating common (extension_id,json_args) | |
| 960 // pairs into a single message sent to a list of sub_event_names. | |
| 961 int num_handlers_blocking = 0; | |
| 962 for (std::vector<const EventListener*>::const_iterator it = listeners.begin(); | |
| 963 it != listeners.end(); ++it) { | |
| 964 // Filter out the optional keys that this listener didn't request. | |
| 965 scoped_ptr<ListValue> args_filtered(args.DeepCopy()); | |
| 966 DictionaryValue* dict = NULL; | |
| 967 CHECK(args_filtered->GetDictionary(0, &dict) && dict); | |
| 968 if (!((*it)->extra_info_spec & ExtraInfoSpec::REQUEST_HEADERS)) | |
| 969 dict->Remove(keys::kRequestHeadersKey, NULL); | |
| 970 if (!((*it)->extra_info_spec & ExtraInfoSpec::RESPONSE_HEADERS)) | |
| 971 dict->Remove(keys::kResponseHeadersKey, NULL); | |
| 972 | |
| 973 base::JSONWriter::Write(args_filtered.get(), &json_args); | |
| 974 | |
| 975 ExtensionEventRouter::DispatchEvent( | |
| 976 (*it)->ipc_sender.get(), (*it)->extension_id, (*it)->sub_event_name, | |
| 977 json_args, GURL(), ExtensionEventRouter::USER_GESTURE_UNKNOWN); | |
| 978 if ((*it)->extra_info_spec & | |
| 979 (ExtraInfoSpec::BLOCKING | ExtraInfoSpec::ASYNC_BLOCKING)) { | |
| 980 (*it)->blocked_requests.insert(request->identifier()); | |
| 981 ++num_handlers_blocking; | |
| 982 | |
| 983 request->SetLoadStateParam( | |
| 984 l10n_util::GetStringFUTF16(IDS_LOAD_STATE_PARAMETER_EXTENSION, | |
| 985 UTF8ToUTF16((*it)->extension_name))); | |
| 986 } | |
| 987 } | |
| 988 | |
| 989 if (num_handlers_blocking > 0) { | |
| 990 CHECK(blocked_requests_.find(request->identifier()) == | |
| 991 blocked_requests_.end()); | |
| 992 blocked_requests_[request->identifier()].request = request; | |
| 993 blocked_requests_[request->identifier()].num_handlers_blocking = | |
| 994 num_handlers_blocking; | |
| 995 blocked_requests_[request->identifier()].blocking_time = base::Time::Now(); | |
| 996 | |
| 997 return true; | |
| 998 } | |
| 999 | |
| 1000 return false; | |
| 1001 } | |
| 1002 | |
| 1003 void ExtensionWebRequestEventRouter::OnEventHandled( | |
| 1004 void* profile, | |
| 1005 const std::string& extension_id, | |
| 1006 const std::string& event_name, | |
| 1007 const std::string& sub_event_name, | |
| 1008 uint64 request_id, | |
| 1009 EventResponse* response) { | |
| 1010 EventListener listener; | |
| 1011 listener.extension_id = extension_id; | |
| 1012 listener.sub_event_name = sub_event_name; | |
| 1013 | |
| 1014 // The listener may have been removed (e.g. due to the process going away) | |
| 1015 // before we got here. | |
| 1016 std::set<EventListener>::iterator found = | |
| 1017 listeners_[profile][event_name].find(listener); | |
| 1018 if (found != listeners_[profile][event_name].end()) | |
| 1019 found->blocked_requests.erase(request_id); | |
| 1020 | |
| 1021 DecrementBlockCount(profile, extension_id, event_name, request_id, response); | |
| 1022 } | |
| 1023 | |
| 1024 void ExtensionWebRequestEventRouter::AddEventListener( | |
| 1025 void* profile, | |
| 1026 const std::string& extension_id, | |
| 1027 const std::string& extension_name, | |
| 1028 const std::string& event_name, | |
| 1029 const std::string& sub_event_name, | |
| 1030 const RequestFilter& filter, | |
| 1031 int extra_info_spec, | |
| 1032 base::WeakPtr<IPC::Message::Sender> ipc_sender) { | |
| 1033 if (!IsWebRequestEvent(event_name)) | |
| 1034 return; | |
| 1035 | |
| 1036 EventListener listener; | |
| 1037 listener.extension_id = extension_id; | |
| 1038 listener.extension_name = extension_name; | |
| 1039 listener.sub_event_name = sub_event_name; | |
| 1040 listener.filter = filter; | |
| 1041 listener.extra_info_spec = extra_info_spec; | |
| 1042 listener.ipc_sender = ipc_sender; | |
| 1043 | |
| 1044 CHECK_EQ(listeners_[profile][event_name].count(listener), 0u) << | |
| 1045 "extension=" << extension_id << " event=" << event_name; | |
| 1046 listeners_[profile][event_name].insert(listener); | |
| 1047 } | |
| 1048 | |
| 1049 void ExtensionWebRequestEventRouter::RemoveEventListener( | |
| 1050 void* profile, | |
| 1051 const std::string& extension_id, | |
| 1052 const std::string& sub_event_name) { | |
| 1053 size_t slash_sep = sub_event_name.find('/'); | |
| 1054 std::string event_name = sub_event_name.substr(0, slash_sep); | |
| 1055 | |
| 1056 if (!IsWebRequestEvent(event_name)) | |
| 1057 return; | |
| 1058 | |
| 1059 EventListener listener; | |
| 1060 listener.extension_id = extension_id; | |
| 1061 listener.sub_event_name = sub_event_name; | |
| 1062 | |
| 1063 // It's possible for AddEventListener to fail asynchronously. In that case, | |
| 1064 // the renderer believes the listener exists, while the browser does not. | |
| 1065 // Ignore a RemoveEventListener in that case. | |
| 1066 std::set<EventListener>::iterator found = | |
| 1067 listeners_[profile][event_name].find(listener); | |
| 1068 if (found == listeners_[profile][event_name].end()) | |
| 1069 return; | |
| 1070 | |
| 1071 CHECK_EQ(listeners_[profile][event_name].count(listener), 1u) << | |
| 1072 "extension=" << extension_id << " event=" << event_name; | |
| 1073 | |
| 1074 // Unblock any request that this event listener may have been blocking. | |
| 1075 for (std::set<uint64>::iterator it = found->blocked_requests.begin(); | |
| 1076 it != found->blocked_requests.end(); ++it) { | |
| 1077 DecrementBlockCount(profile, extension_id, event_name, *it, NULL); | |
| 1078 } | |
| 1079 | |
| 1080 listeners_[profile][event_name].erase(listener); | |
| 1081 | |
| 1082 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 1083 base::Bind(&ClearCacheOnNavigationOnUI)); | |
| 1084 } | |
| 1085 | |
| 1086 void ExtensionWebRequestEventRouter::OnOTRProfileCreated( | |
| 1087 void* original_profile, void* otr_profile) { | |
| 1088 cross_profile_map_[original_profile] = otr_profile; | |
| 1089 cross_profile_map_[otr_profile] = original_profile; | |
| 1090 } | |
| 1091 | |
| 1092 void ExtensionWebRequestEventRouter::OnOTRProfileDestroyed( | |
| 1093 void* original_profile, void* otr_profile) { | |
| 1094 cross_profile_map_.erase(otr_profile); | |
| 1095 cross_profile_map_.erase(original_profile); | |
| 1096 } | |
| 1097 | |
| 1098 void ExtensionWebRequestEventRouter::AddCallbackForPageLoad( | |
| 1099 const base::Closure& callback) { | |
| 1100 callbacks_for_page_load_.push_back(callback); | |
| 1101 } | |
| 1102 | |
| 1103 bool ExtensionWebRequestEventRouter::IsPageLoad( | |
| 1104 net::URLRequest* request) const { | |
| 1105 bool is_main_frame = false; | |
| 1106 int64 frame_id = -1; | |
| 1107 bool parent_is_main_frame = false; | |
| 1108 int64 parent_frame_id = -1; | |
| 1109 int tab_id = -1; | |
| 1110 int window_id = -1; | |
| 1111 ResourceType::Type resource_type = ResourceType::LAST_TYPE; | |
| 1112 | |
| 1113 ExtractRequestInfoDetails(request, &is_main_frame, &frame_id, | |
| 1114 &parent_is_main_frame, &parent_frame_id, | |
| 1115 &tab_id, &window_id, &resource_type); | |
| 1116 | |
| 1117 return resource_type == ResourceType::MAIN_FRAME; | |
| 1118 } | |
| 1119 | |
| 1120 void ExtensionWebRequestEventRouter::NotifyPageLoad() { | |
| 1121 for (CallbacksForPageLoad::const_iterator i = | |
| 1122 callbacks_for_page_load_.begin(); | |
| 1123 i != callbacks_for_page_load_.end(); ++i) { | |
| 1124 i->Run(); | |
| 1125 } | |
| 1126 callbacks_for_page_load_.clear(); | |
| 1127 } | |
| 1128 | |
| 1129 void ExtensionWebRequestEventRouter::GetMatchingListenersImpl( | |
| 1130 void* profile, | |
| 1131 ExtensionInfoMap* extension_info_map, | |
| 1132 bool crosses_incognito, | |
| 1133 const std::string& event_name, | |
| 1134 const GURL& url, | |
| 1135 int tab_id, | |
| 1136 int window_id, | |
| 1137 ResourceType::Type resource_type, | |
| 1138 bool is_request_from_extension, | |
| 1139 int* extra_info_spec, | |
| 1140 std::vector<const ExtensionWebRequestEventRouter::EventListener*>* | |
| 1141 matching_listeners) { | |
| 1142 std::set<EventListener>& listeners = listeners_[profile][event_name]; | |
| 1143 for (std::set<EventListener>::iterator it = listeners.begin(); | |
| 1144 it != listeners.end(); ++it) { | |
| 1145 if (!it->ipc_sender.get()) { | |
| 1146 // The IPC sender has been deleted. This listener will be removed soon | |
| 1147 // via a call to RemoveEventListener. For now, just skip it. | |
| 1148 continue; | |
| 1149 } | |
| 1150 | |
| 1151 if (!it->filter.urls.is_empty() && !it->filter.urls.MatchesURL(url)) | |
| 1152 continue; | |
| 1153 if (it->filter.tab_id != -1 && tab_id != it->filter.tab_id) | |
| 1154 continue; | |
| 1155 if (it->filter.window_id != -1 && window_id != it->filter.window_id) | |
| 1156 continue; | |
| 1157 if (!it->filter.types.empty() && | |
| 1158 std::find(it->filter.types.begin(), it->filter.types.end(), | |
| 1159 resource_type) == it->filter.types.end()) | |
| 1160 continue; | |
| 1161 | |
| 1162 // extension_info_map can be NULL if this is a system-level request. | |
| 1163 if (extension_info_map) { | |
| 1164 const Extension* extension = | |
| 1165 extension_info_map->extensions().GetByID(it->extension_id); | |
| 1166 | |
| 1167 // Check if this event crosses incognito boundaries when it shouldn't. | |
| 1168 if (!extension || | |
| 1169 (crosses_incognito && | |
| 1170 !extension_info_map->CanCrossIncognito(extension))) | |
| 1171 continue; | |
| 1172 | |
| 1173 bool blocking_listener = | |
| 1174 (it->extra_info_spec & | |
| 1175 (ExtraInfoSpec::BLOCKING | ExtraInfoSpec::ASYNC_BLOCKING)) != 0; | |
| 1176 | |
| 1177 // We do not want to notify extensions about XHR requests that are | |
| 1178 // triggered by themselves. This is a workaround to prevent deadlocks | |
| 1179 // in case of synchronous XHR requests that block the extension renderer | |
| 1180 // and therefore prevent the extension from processing the request | |
| 1181 // handler. This is only a problem for blocking listeners. | |
| 1182 // http://crbug.com/105656 | |
| 1183 bool possibly_synchronous_xhr_from_extension = | |
| 1184 is_request_from_extension && resource_type == ResourceType::XHR; | |
| 1185 | |
| 1186 // Only send webRequest events for URLs the extension has access to. | |
| 1187 if (!CanExtensionAccessURL(extension, url) || | |
| 1188 (blocking_listener && possibly_synchronous_xhr_from_extension)) { | |
| 1189 continue; | |
| 1190 } | |
| 1191 } | |
| 1192 | |
| 1193 matching_listeners->push_back(&(*it)); | |
| 1194 *extra_info_spec |= it->extra_info_spec; | |
| 1195 } | |
| 1196 } | |
| 1197 | |
| 1198 std::vector<const ExtensionWebRequestEventRouter::EventListener*> | |
| 1199 ExtensionWebRequestEventRouter::GetMatchingListeners( | |
| 1200 void* profile, | |
| 1201 ExtensionInfoMap* extension_info_map, | |
| 1202 const std::string& event_name, | |
| 1203 net::URLRequest* request, | |
| 1204 int* extra_info_spec) { | |
| 1205 // TODO(mpcomplete): handle profile == NULL (should collect all listeners). | |
| 1206 *extra_info_spec = 0; | |
| 1207 | |
| 1208 bool is_main_frame = false; | |
| 1209 int64 frame_id = -1; | |
| 1210 bool parent_is_main_frame = false; | |
| 1211 int64 parent_frame_id = -1; | |
| 1212 int tab_id = -1; | |
| 1213 int window_id = -1; | |
| 1214 ResourceType::Type resource_type = ResourceType::LAST_TYPE; | |
| 1215 const GURL& url = request->url(); | |
| 1216 | |
| 1217 ExtractRequestInfoDetails(request, &is_main_frame, &frame_id, | |
| 1218 &parent_is_main_frame, &parent_frame_id, | |
| 1219 &tab_id, &window_id, &resource_type); | |
| 1220 | |
| 1221 std::vector<const ExtensionWebRequestEventRouter::EventListener*> | |
| 1222 matching_listeners; | |
| 1223 | |
| 1224 bool is_request_from_extension = | |
| 1225 IsRequestFromExtension(request, extension_info_map); | |
| 1226 | |
| 1227 GetMatchingListenersImpl( | |
| 1228 profile, extension_info_map, false, event_name, url, | |
| 1229 tab_id, window_id, resource_type, is_request_from_extension, | |
| 1230 extra_info_spec, &matching_listeners); | |
| 1231 CrossProfileMap::const_iterator cross_profile = | |
| 1232 cross_profile_map_.find(profile); | |
| 1233 if (cross_profile != cross_profile_map_.end()) { | |
| 1234 GetMatchingListenersImpl( | |
| 1235 cross_profile->second, extension_info_map, true, event_name, url, | |
| 1236 tab_id, window_id, resource_type, is_request_from_extension, | |
| 1237 extra_info_spec, &matching_listeners); | |
| 1238 } | |
| 1239 | |
| 1240 return matching_listeners; | |
| 1241 } | |
| 1242 | |
| 1243 namespace { | |
| 1244 | |
| 1245 helpers::EventResponseDelta* CalculateDelta( | |
| 1246 ExtensionWebRequestEventRouter::BlockedRequest* blocked_request, | |
| 1247 ExtensionWebRequestEventRouter::EventResponse* response) { | |
| 1248 switch (blocked_request->event) { | |
| 1249 case ExtensionWebRequestEventRouter::kOnBeforeRequest: | |
| 1250 return helpers::CalculateOnBeforeRequestDelta( | |
| 1251 response->extension_id, response->extension_install_time, | |
| 1252 response->cancel, response->new_url); | |
| 1253 case ExtensionWebRequestEventRouter::kOnBeforeSendHeaders: { | |
| 1254 net::HttpRequestHeaders* old_headers = blocked_request->request_headers; | |
| 1255 net::HttpRequestHeaders* new_headers = response->request_headers.get(); | |
| 1256 return helpers::CalculateOnBeforeSendHeadersDelta( | |
| 1257 response->extension_id, response->extension_install_time, | |
| 1258 response->cancel, old_headers, new_headers); | |
| 1259 } | |
| 1260 case ExtensionWebRequestEventRouter::kOnHeadersReceived: { | |
| 1261 net::HttpResponseHeaders* old_headers = | |
| 1262 blocked_request->original_response_headers.get(); | |
| 1263 helpers::ResponseHeaders* new_headers = | |
| 1264 response->response_headers.get(); | |
| 1265 return helpers::CalculateOnHeadersReceivedDelta( | |
| 1266 response->extension_id, response->extension_install_time, | |
| 1267 response->cancel, old_headers, new_headers); | |
| 1268 } | |
| 1269 case ExtensionWebRequestEventRouter::kOnAuthRequired: | |
| 1270 return helpers::CalculateOnAuthRequiredDelta( | |
| 1271 response->extension_id, response->extension_install_time, | |
| 1272 response->cancel, &response->auth_credentials); | |
| 1273 default: | |
| 1274 NOTREACHED(); | |
| 1275 break; | |
| 1276 } | |
| 1277 return NULL; | |
| 1278 } | |
| 1279 | |
| 1280 } // namespace | |
| 1281 | |
| 1282 void ExtensionWebRequestEventRouter::DecrementBlockCount( | |
| 1283 void* profile, | |
| 1284 const std::string& extension_id, | |
| 1285 const std::string& event_name, | |
| 1286 uint64 request_id, | |
| 1287 EventResponse* response) { | |
| 1288 scoped_ptr<EventResponse> response_scoped(response); | |
| 1289 | |
| 1290 // It's possible that this request was deleted, or cancelled by a previous | |
| 1291 // event handler. If so, ignore this response. | |
| 1292 if (blocked_requests_.find(request_id) == blocked_requests_.end()) | |
| 1293 return; | |
| 1294 | |
| 1295 BlockedRequest& blocked_request = blocked_requests_[request_id]; | |
| 1296 int num_handlers_blocking = --blocked_request.num_handlers_blocking; | |
| 1297 CHECK_GE(num_handlers_blocking, 0); | |
| 1298 | |
| 1299 if (response) { | |
| 1300 blocked_request.response_deltas.push_back( | |
| 1301 linked_ptr<helpers::EventResponseDelta>( | |
| 1302 CalculateDelta(&blocked_request, response))); | |
| 1303 } | |
| 1304 | |
| 1305 base::TimeDelta block_time = | |
| 1306 base::Time::Now() - blocked_request.blocking_time; | |
| 1307 request_time_tracker_->IncrementExtensionBlockTime( | |
| 1308 extension_id, request_id, block_time); | |
| 1309 | |
| 1310 if (num_handlers_blocking == 0) { | |
| 1311 request_time_tracker_->IncrementTotalBlockTime(request_id, block_time); | |
| 1312 | |
| 1313 bool credentials_set = false; | |
| 1314 | |
| 1315 helpers::EventResponseDeltas& deltas = blocked_request.response_deltas; | |
| 1316 deltas.sort(&helpers::InDecreasingExtensionInstallationTimeOrder); | |
| 1317 std::set<std::string> conflicting_extensions; | |
| 1318 helpers::EventLogEntries event_log_entries; | |
| 1319 | |
| 1320 bool canceled = false; | |
| 1321 helpers::MergeCancelOfResponses( | |
| 1322 blocked_request.response_deltas, | |
| 1323 &canceled, | |
| 1324 &event_log_entries); | |
| 1325 | |
| 1326 if (blocked_request.event == kOnBeforeRequest) { | |
| 1327 CHECK(!blocked_request.callback.is_null()); | |
| 1328 helpers::MergeOnBeforeRequestResponses( | |
| 1329 blocked_request.response_deltas, | |
| 1330 blocked_request.new_url, | |
| 1331 &conflicting_extensions, | |
| 1332 &event_log_entries); | |
| 1333 } else if (blocked_request.event == kOnBeforeSendHeaders) { | |
| 1334 CHECK(!blocked_request.callback.is_null()); | |
| 1335 helpers::MergeOnBeforeSendHeadersResponses( | |
| 1336 blocked_request.response_deltas, | |
| 1337 blocked_request.request_headers, | |
| 1338 &conflicting_extensions, | |
| 1339 &event_log_entries); | |
| 1340 } else if (blocked_request.event == kOnHeadersReceived) { | |
| 1341 CHECK(!blocked_request.callback.is_null()); | |
| 1342 helpers::MergeOnHeadersReceivedResponses( | |
| 1343 blocked_request.response_deltas, | |
| 1344 blocked_request.original_response_headers.get(), | |
| 1345 blocked_request.override_response_headers, | |
| 1346 &conflicting_extensions, | |
| 1347 &event_log_entries); | |
| 1348 } else if (blocked_request.event == kOnAuthRequired) { | |
| 1349 CHECK(blocked_request.callback.is_null()); | |
| 1350 CHECK(!blocked_request.auth_callback.is_null()); | |
| 1351 credentials_set = helpers::MergeOnAuthRequiredResponses( | |
| 1352 blocked_request.response_deltas, | |
| 1353 blocked_request.auth_credentials, | |
| 1354 &conflicting_extensions, | |
| 1355 &event_log_entries); | |
| 1356 } else { | |
| 1357 NOTREACHED(); | |
| 1358 } | |
| 1359 | |
| 1360 for (helpers::EventLogEntries::const_iterator i = | |
| 1361 event_log_entries.begin(); | |
| 1362 i != event_log_entries.end(); ++i) { | |
| 1363 blocked_request.net_log->AddEvent(i->event_type, i->params); | |
| 1364 } | |
| 1365 if (!conflicting_extensions.empty()) { | |
| 1366 BrowserThread::PostTask( | |
| 1367 BrowserThread::UI, | |
| 1368 FROM_HERE, | |
| 1369 base::Bind(&ExtensionWarningSet::NotifyWarningsOnUI, | |
| 1370 profile, | |
| 1371 conflicting_extensions, | |
| 1372 ExtensionWarningSet::kNetworkConflict)); | |
| 1373 } | |
| 1374 | |
| 1375 if (canceled) { | |
| 1376 request_time_tracker_->SetRequestCanceled(request_id); | |
| 1377 } else if (blocked_request.new_url && | |
| 1378 !blocked_request.new_url->is_empty()) { | |
| 1379 request_time_tracker_->SetRequestRedirected(request_id); | |
| 1380 } | |
| 1381 | |
| 1382 if (!blocked_request.callback.is_null()) { | |
| 1383 // This triggers onErrorOccurred. | |
| 1384 int rv = canceled ? net::ERR_BLOCKED_BY_CLIENT : net::OK; | |
| 1385 net::CompletionCallback callback = blocked_request.callback; | |
| 1386 // Ensure that request is removed before callback because the callback | |
| 1387 // might trigger the next event. | |
| 1388 blocked_requests_.erase(request_id); | |
| 1389 callback.Run(rv); | |
| 1390 } else if (!blocked_request.auth_callback.is_null()) { | |
| 1391 net::NetworkDelegate::AuthRequiredResponse response = | |
| 1392 net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION; | |
| 1393 if (canceled) { | |
| 1394 response = net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_CANCEL_AUTH; | |
| 1395 } else if (credentials_set) { | |
| 1396 response = net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_SET_AUTH; | |
| 1397 } | |
| 1398 net::NetworkDelegate::AuthCallback callback = | |
| 1399 blocked_request.auth_callback; | |
| 1400 blocked_requests_.erase(request_id); | |
| 1401 callback.Run(response); | |
| 1402 } else { | |
| 1403 blocked_requests_.erase(request_id); | |
| 1404 } | |
| 1405 } else { | |
| 1406 // Update the URLRequest to indicate it is now blocked on a different | |
| 1407 // extension. | |
| 1408 std::set<EventListener>& listeners = listeners_[profile][event_name]; | |
| 1409 | |
| 1410 for (std::set<EventListener>::iterator it = listeners.begin(); | |
| 1411 it != listeners.end(); ++it) { | |
| 1412 if (it->blocked_requests.count(request_id)) { | |
| 1413 blocked_request.request->SetLoadStateParam( | |
| 1414 l10n_util::GetStringFUTF16(IDS_LOAD_STATE_PARAMETER_EXTENSION, | |
| 1415 UTF8ToUTF16(it->extension_name))); | |
| 1416 break; | |
| 1417 } | |
| 1418 } | |
| 1419 } | |
| 1420 } | |
| 1421 | |
| 1422 bool ExtensionWebRequestEventRouter::GetAndSetSignaled(uint64 request_id, | |
| 1423 EventTypes event_type) { | |
| 1424 SignaledRequestMap::iterator iter = signaled_requests_.find(request_id); | |
| 1425 if (iter == signaled_requests_.end()) { | |
| 1426 signaled_requests_[request_id] = event_type; | |
| 1427 return false; | |
| 1428 } | |
| 1429 bool was_signaled_before = (iter->second & event_type) != 0; | |
| 1430 iter->second |= event_type; | |
| 1431 return was_signaled_before; | |
| 1432 } | |
| 1433 | |
| 1434 void ExtensionWebRequestEventRouter::ClearSignaled(uint64 request_id, | |
| 1435 EventTypes event_type) { | |
| 1436 SignaledRequestMap::iterator iter = signaled_requests_.find(request_id); | |
| 1437 if (iter == signaled_requests_.end()) | |
| 1438 return; | |
| 1439 iter->second &= ~event_type; | |
| 1440 } | |
| 1441 | |
| 1442 // Special QuotaLimitHeuristic for WebRequestHandlerBehaviorChanged. | |
| 1443 // | |
| 1444 // Each call of webRequest.handlerBehaviorChanged() clears the in-memory cache | |
| 1445 // of WebKit at the time of the next page load (top level navigation event). | |
| 1446 // This quota heuristic is intended to limit the number of times the cache is | |
| 1447 // cleared by an extension. | |
| 1448 // | |
| 1449 // As we want to account for the number of times the cache is really cleared | |
| 1450 // (opposed to the number of times webRequest.handlerBehaviorChanged() is | |
| 1451 // called), we cannot decide whether a call of | |
| 1452 // webRequest.handlerBehaviorChanged() should trigger a quota violation at the | |
| 1453 // time it is called. Instead we only decrement the bucket counter at the time | |
| 1454 // when the cache is cleared (when page loads happen). | |
| 1455 class ClearCacheQuotaHeuristic : public QuotaLimitHeuristic { | |
| 1456 public: | |
| 1457 ClearCacheQuotaHeuristic(const Config& config, BucketMapper* map) | |
| 1458 : QuotaLimitHeuristic(config, map), | |
| 1459 callback_registered_(false), | |
| 1460 weak_ptr_factory_(this) {} | |
| 1461 virtual ~ClearCacheQuotaHeuristic() {} | |
| 1462 virtual bool Apply(Bucket* bucket, | |
| 1463 const base::TimeTicks& event_time) OVERRIDE; | |
| 1464 | |
| 1465 private: | |
| 1466 // Callback that is triggered by the ExtensionWebRequestEventRouter on a page | |
| 1467 // load. | |
| 1468 // | |
| 1469 // We don't need to take care of the life time of |bucket|: It is owned by the | |
| 1470 // BucketMapper of our base class in |QuotaLimitHeuristic::bucket_mapper_|. As | |
| 1471 // long as |this| exists, the respective BucketMapper and its bucket will | |
| 1472 // exist as well. | |
| 1473 void OnPageLoad(Bucket* bucket); | |
| 1474 | |
| 1475 // Flag to prevent that we register more than one call back in-between | |
| 1476 // clearing the cache. | |
| 1477 bool callback_registered_; | |
| 1478 | |
| 1479 base::WeakPtrFactory<ClearCacheQuotaHeuristic> weak_ptr_factory_; | |
| 1480 | |
| 1481 DISALLOW_COPY_AND_ASSIGN(ClearCacheQuotaHeuristic); | |
| 1482 }; | |
| 1483 | |
| 1484 bool ClearCacheQuotaHeuristic::Apply(Bucket* bucket, | |
| 1485 const base::TimeTicks& event_time) { | |
| 1486 if (event_time > bucket->expiration()) | |
| 1487 bucket->Reset(config(), event_time); | |
| 1488 | |
| 1489 // Call bucket->DeductToken() on a new page load, this is when | |
| 1490 // webRequest.handlerBehaviorChanged() clears the cache. | |
| 1491 if (!callback_registered_) { | |
| 1492 ExtensionWebRequestEventRouter::GetInstance()->AddCallbackForPageLoad( | |
| 1493 base::Bind(&ClearCacheQuotaHeuristic::OnPageLoad, | |
| 1494 weak_ptr_factory_.GetWeakPtr(), | |
| 1495 bucket)); | |
| 1496 callback_registered_ = true; | |
| 1497 } | |
| 1498 | |
| 1499 // We only check whether tokens are left here. Deducting a token happens in | |
| 1500 // OnPageLoad(). | |
| 1501 return bucket->has_tokens(); | |
| 1502 } | |
| 1503 | |
| 1504 void ClearCacheQuotaHeuristic::OnPageLoad(Bucket* bucket) { | |
| 1505 callback_registered_ = false; | |
| 1506 bucket->DeductToken(); | |
| 1507 } | |
| 1508 | |
| 1509 bool WebRequestAddEventListener::RunImpl() { | |
| 1510 // Argument 0 is the callback, which we don't use here. | |
| 1511 | |
| 1512 ExtensionWebRequestEventRouter::RequestFilter filter; | |
| 1513 DictionaryValue* value = NULL; | |
| 1514 error_.clear(); | |
| 1515 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &value)); | |
| 1516 // Failure + an empty error string means a fatal error. | |
| 1517 EXTENSION_FUNCTION_VALIDATE(filter.InitFromValue(*value, &error_) || | |
| 1518 !error_.empty()); | |
| 1519 if (!error_.empty()) | |
| 1520 return false; | |
| 1521 | |
| 1522 int extra_info_spec = 0; | |
| 1523 if (HasOptionalArgument(2)) { | |
| 1524 ListValue* value = NULL; | |
| 1525 EXTENSION_FUNCTION_VALIDATE(args_->GetList(2, &value)); | |
| 1526 EXTENSION_FUNCTION_VALIDATE( | |
| 1527 ExtensionWebRequestEventRouter::ExtraInfoSpec::InitFromValue( | |
| 1528 *value, &extra_info_spec)); | |
| 1529 } | |
| 1530 | |
| 1531 std::string event_name; | |
| 1532 EXTENSION_FUNCTION_VALIDATE(args_->GetString(3, &event_name)); | |
| 1533 | |
| 1534 std::string sub_event_name; | |
| 1535 EXTENSION_FUNCTION_VALIDATE(args_->GetString(4, &sub_event_name)); | |
| 1536 | |
| 1537 const Extension* extension = | |
| 1538 extension_info_map()->extensions().GetByID(extension_id()); | |
| 1539 std::string extension_name = extension ? extension->name() : extension_id(); | |
| 1540 | |
| 1541 // We check automatically whether the extension has the 'webRequest' | |
| 1542 // permission. For blocking calls we require the additional permission | |
| 1543 // 'webRequestBlocking'. | |
| 1544 if ((extra_info_spec & | |
| 1545 (ExtensionWebRequestEventRouter::ExtraInfoSpec::BLOCKING | | |
| 1546 ExtensionWebRequestEventRouter::ExtraInfoSpec::ASYNC_BLOCKING)) && | |
| 1547 !extension->HasAPIPermission( | |
| 1548 ExtensionAPIPermission::kWebRequestBlocking)) { | |
| 1549 error_ = keys::kBlockingPermissionRequired; | |
| 1550 return false; | |
| 1551 } | |
| 1552 | |
| 1553 // We allow to subscribe to patterns that are broader than the host | |
| 1554 // permissions. E.g., we could subscribe to http://www.example.com/* | |
| 1555 // while having host permissions for http://www.example.com/foo/* and | |
| 1556 // http://www.example.com/bar/*. | |
| 1557 // For this reason we do only a coarse check here to warn the extension | |
| 1558 // developer if he does something obviously wrong. | |
| 1559 if (extension->GetEffectiveHostPermissions().is_empty()) { | |
| 1560 error_ = keys::kHostPermissionsRequired; | |
| 1561 return false; | |
| 1562 } | |
| 1563 | |
| 1564 ExtensionWebRequestEventRouter::GetInstance()->AddEventListener( | |
| 1565 profile_id(), extension_id(), extension_name, | |
| 1566 event_name, sub_event_name, filter, | |
| 1567 extra_info_spec, ipc_sender_weak()); | |
| 1568 | |
| 1569 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 1570 base::Bind(&ClearCacheOnNavigationOnUI)); | |
| 1571 | |
| 1572 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( | |
| 1573 &NotifyWebRequestAPIUsed, | |
| 1574 profile_id(), make_scoped_refptr(GetExtension()))); | |
| 1575 | |
| 1576 return true; | |
| 1577 } | |
| 1578 | |
| 1579 bool WebRequestEventHandled::RunImpl() { | |
| 1580 std::string event_name; | |
| 1581 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &event_name)); | |
| 1582 | |
| 1583 std::string sub_event_name; | |
| 1584 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &sub_event_name)); | |
| 1585 | |
| 1586 std::string request_id_str; | |
| 1587 EXTENSION_FUNCTION_VALIDATE(args_->GetString(2, &request_id_str)); | |
| 1588 uint64 request_id; | |
| 1589 EXTENSION_FUNCTION_VALIDATE(base::StringToUint64(request_id_str, | |
| 1590 &request_id)); | |
| 1591 | |
| 1592 scoped_ptr<ExtensionWebRequestEventRouter::EventResponse> response; | |
| 1593 if (HasOptionalArgument(3)) { | |
| 1594 DictionaryValue* value = NULL; | |
| 1595 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(3, &value)); | |
| 1596 | |
| 1597 if (!value->empty()) { | |
| 1598 base::Time install_time = | |
| 1599 extension_info_map()->GetInstallTime(extension_id()); | |
| 1600 response.reset(new ExtensionWebRequestEventRouter::EventResponse( | |
| 1601 extension_id(), install_time)); | |
| 1602 } | |
| 1603 | |
| 1604 if (value->HasKey("cancel")) { | |
| 1605 // Don't allow cancel mixed with other keys. | |
| 1606 if (value->HasKey("redirectUrl") || value->HasKey("requestHeaders")) { | |
| 1607 error_ = keys::kInvalidBlockingResponse; | |
| 1608 return false; | |
| 1609 } | |
| 1610 | |
| 1611 bool cancel = false; | |
| 1612 EXTENSION_FUNCTION_VALIDATE(value->GetBoolean("cancel", &cancel)); | |
| 1613 response->cancel = cancel; | |
| 1614 } | |
| 1615 | |
| 1616 if (value->HasKey("redirectUrl")) { | |
| 1617 std::string new_url_str; | |
| 1618 EXTENSION_FUNCTION_VALIDATE(value->GetString("redirectUrl", | |
| 1619 &new_url_str)); | |
| 1620 response->new_url = GURL(new_url_str); | |
| 1621 if (!response->new_url.is_valid()) { | |
| 1622 error_ = ExtensionErrorUtils::FormatErrorMessage( | |
| 1623 keys::kInvalidRedirectUrl, new_url_str); | |
| 1624 return false; | |
| 1625 } | |
| 1626 } | |
| 1627 | |
| 1628 if (value->HasKey("requestHeaders")) { | |
| 1629 ListValue* request_headers_value = NULL; | |
| 1630 response->request_headers.reset(new net::HttpRequestHeaders()); | |
| 1631 EXTENSION_FUNCTION_VALIDATE(value->GetList(keys::kRequestHeadersKey, | |
| 1632 &request_headers_value)); | |
| 1633 for (size_t i = 0; i < request_headers_value->GetSize(); ++i) { | |
| 1634 DictionaryValue* header_value = NULL; | |
| 1635 std::string name; | |
| 1636 std::string value; | |
| 1637 EXTENSION_FUNCTION_VALIDATE( | |
| 1638 request_headers_value->GetDictionary(i, &header_value)); | |
| 1639 EXTENSION_FUNCTION_VALIDATE( | |
| 1640 FromHeaderDictionary(header_value, &name, &value)); | |
| 1641 response->request_headers->SetHeader(name, value); | |
| 1642 } | |
| 1643 } | |
| 1644 | |
| 1645 if (value->HasKey("responseHeaders")) { | |
| 1646 scoped_ptr<helpers::ResponseHeaders> response_headers( | |
| 1647 new helpers::ResponseHeaders()); | |
| 1648 ListValue* response_headers_value = NULL; | |
| 1649 EXTENSION_FUNCTION_VALIDATE(value->GetList(keys::kResponseHeadersKey, | |
| 1650 &response_headers_value)); | |
| 1651 for (size_t i = 0; i < response_headers_value->GetSize(); ++i) { | |
| 1652 DictionaryValue* header_value = NULL; | |
| 1653 std::string name; | |
| 1654 std::string value; | |
| 1655 EXTENSION_FUNCTION_VALIDATE( | |
| 1656 response_headers_value->GetDictionary(i, &header_value)); | |
| 1657 EXTENSION_FUNCTION_VALIDATE( | |
| 1658 FromHeaderDictionary(header_value, &name, &value)); | |
| 1659 response_headers->push_back(helpers::ResponseHeader(name, value)); | |
| 1660 } | |
| 1661 response->response_headers.reset(response_headers.release()); | |
| 1662 } | |
| 1663 | |
| 1664 if (value->HasKey(keys::kAuthCredentialsKey)) { | |
| 1665 DictionaryValue* credentials_value = NULL; | |
| 1666 EXTENSION_FUNCTION_VALIDATE(value->GetDictionary( | |
| 1667 keys::kAuthCredentialsKey, | |
| 1668 &credentials_value)); | |
| 1669 string16 username; | |
| 1670 string16 password; | |
| 1671 EXTENSION_FUNCTION_VALIDATE( | |
| 1672 credentials_value->GetString(keys::kUsernameKey, &username)); | |
| 1673 EXTENSION_FUNCTION_VALIDATE( | |
| 1674 credentials_value->GetString(keys::kPasswordKey, &password)); | |
| 1675 response->auth_credentials.reset( | |
| 1676 new net::AuthCredentials(username, password)); | |
| 1677 } | |
| 1678 } | |
| 1679 | |
| 1680 ExtensionWebRequestEventRouter::GetInstance()->OnEventHandled( | |
| 1681 profile_id(), extension_id(), event_name, sub_event_name, request_id, | |
| 1682 response.release()); | |
| 1683 | |
| 1684 return true; | |
| 1685 } | |
| 1686 | |
| 1687 bool WebRequestHandlerBehaviorChanged::RunImpl() { | |
| 1688 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 1689 base::Bind(&ClearCacheOnNavigationOnUI)); | |
| 1690 return true; | |
| 1691 } | |
| 1692 | |
| 1693 void WebRequestHandlerBehaviorChanged::GetQuotaLimitHeuristics( | |
| 1694 QuotaLimitHeuristics* heuristics) const { | |
| 1695 QuotaLimitHeuristic::Config config = { | |
| 1696 20, // Refill 20 tokens per interval. | |
| 1697 base::TimeDelta::FromMinutes(10) // 10 minutes refill interval. | |
| 1698 }; | |
| 1699 QuotaLimitHeuristic::BucketMapper* bucket_mapper = | |
| 1700 new QuotaLimitHeuristic::SingletonBucketMapper(); | |
| 1701 ClearCacheQuotaHeuristic* heuristic = | |
| 1702 new ClearCacheQuotaHeuristic(config, bucket_mapper); | |
| 1703 heuristics->push_back(heuristic); | |
| 1704 } | |
| 1705 | |
| 1706 void WebRequestHandlerBehaviorChanged::OnQuotaExceeded() { | |
| 1707 // Post warning message. | |
| 1708 std::set<std::string> extension_ids; | |
| 1709 extension_ids.insert(extension_id()); | |
| 1710 BrowserThread::PostTask( | |
| 1711 BrowserThread::UI, | |
| 1712 FROM_HERE, | |
| 1713 base::Bind(&ExtensionWarningSet::NotifyWarningsOnUI, | |
| 1714 profile_id(), | |
| 1715 extension_ids, | |
| 1716 ExtensionWarningSet::kRepeatedCacheFlushes)); | |
| 1717 | |
| 1718 // Continue gracefully. | |
| 1719 Run(); | |
| 1720 } | |
| 1721 | |
| 1722 void SendExtensionWebRequestStatusToHost(content::RenderProcessHost* host) { | |
| 1723 Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext()); | |
| 1724 if (!profile || !profile->GetExtensionService()) | |
| 1725 return; | |
| 1726 | |
| 1727 bool adblock = false; | |
| 1728 bool adblock_plus = false; | |
| 1729 bool other = false; | |
| 1730 const ExtensionSet* extensions = | |
| 1731 profile->GetExtensionService()->extensions(); | |
| 1732 for (ExtensionSet::const_iterator it = extensions->begin(); | |
| 1733 it != extensions->end(); ++it) { | |
| 1734 if (profile->GetExtensionService()->HasUsedWebRequest(*it)) { | |
| 1735 if ((*it)->name().find("Adblock Plus") != std::string::npos) { | |
| 1736 adblock_plus = true; | |
| 1737 } else if ((*it)->name().find("AdBlock") != std::string::npos) { | |
| 1738 adblock = true; | |
| 1739 } else { | |
| 1740 other = true; | |
| 1741 } | |
| 1742 } | |
| 1743 } | |
| 1744 | |
| 1745 host->Send(new ExtensionMsg_UsingWebRequestAPI(adblock, adblock_plus, other)); | |
| 1746 } | |
| OLD | NEW |