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 3// found in the LICENSE file. 4 5#include "chrome/browser/extensions/extension_webrequest_api.h" 6 7#include <algorithm> 8 9#include "base/json/json_writer.h" 10#include "base/metrics/histogram.h" 11#include "base/string_number_conversions.h" 12#include "base/values.h" 13#include "chrome/browser/extensions/extension_event_router_forwarder.h" 14#include "chrome/browser/extensions/extension_tab_id_map.h" 15#include "chrome/browser/extensions/extension_webrequest_api_constants.h" 16#include "chrome/browser/profiles/profile.h" 17#include "chrome/common/extensions/extension.h" 18#include "chrome/common/extensions/extension_error_utils.h" 19#include "chrome/common/extensions/extension_extent.h" 20#include "chrome/common/extensions/url_pattern.h" 21#include "chrome/common/url_constants.h" 22#include "content/browser/browser_thread.h" 23#include "content/browser/renderer_host/resource_dispatcher_host.h" 24#include "content/browser/renderer_host/resource_dispatcher_host_request_info.h" 25#include "net/base/net_errors.h" 26#include "net/url_request/url_request.h" 27#include "googleurl/src/gurl.h" 28 29namespace keys = extension_webrequest_api_constants; 30 31namespace { 32 33// List of all the webRequest events. 34static const char* const kWebRequestEvents[] = { 35 keys::kOnBeforeRedirect, 36 keys::kOnBeforeRequest, 37 keys::kOnBeforeSendHeaders, 38 keys::kOnCompleted, 39 keys::kOnErrorOccurred, 40 keys::kOnHeadersReceived, 41 keys::kOnRequestSent 42}; 43 44static const char* kResourceTypeStrings[] = { 45 "main_frame", 46 "sub_frame", 47 "stylesheet", 48 "script", 49 "image", 50 "object", 51 "other", 52}; 53 54static ResourceType::Type kResourceTypeValues[] = { 55 ResourceType::MAIN_FRAME, 56 ResourceType::SUB_FRAME, 57 ResourceType::STYLESHEET, 58 ResourceType::SCRIPT, 59 ResourceType::IMAGE, 60 ResourceType::OBJECT, 61 ResourceType::LAST_TYPE, // represents "other" 62}; 63 64COMPILE_ASSERT( 65 arraysize(kResourceTypeStrings) == arraysize(kResourceTypeValues), 66 keep_resource_types_in_sync); 67 68#define ARRAYEND(array) (array + arraysize(array)) 69 70static bool IsWebRequestEvent(const std::string& event_name) { 71 return std::find(kWebRequestEvents, ARRAYEND(kWebRequestEvents), 72 event_name) != ARRAYEND(kWebRequestEvents); 73} 74 75static const char* ResourceTypeToString(ResourceType::Type type) { 76 ResourceType::Type* iter = 77 std::find(kResourceTypeValues, ARRAYEND(kResourceTypeValues), type); 78 if (iter == ARRAYEND(kResourceTypeValues)) 79 return "other"; 80 81 return kResourceTypeStrings[iter - kResourceTypeValues]; 82} 83 84static bool ParseResourceType(const std::string& type_str, 85 ResourceType::Type* type) { 86 const char** iter = 87 std::find(kResourceTypeStrings, ARRAYEND(kResourceTypeStrings), type_str); 88 if (iter == ARRAYEND(kResourceTypeStrings)) 89 return false; 90 *type = kResourceTypeValues[iter - kResourceTypeStrings]; 91 return true; 92} 93 94static void ExtractRequestInfo(net::URLRequest* request, 95 int* tab_id, 96 int* window_id, 97 ResourceType::Type* resource_type) { 98 if (!request->GetUserData(NULL)) 99 return; 100 101 ResourceDispatcherHostRequestInfo* info = 102 ResourceDispatcherHost::InfoForRequest(request); 103 ExtensionTabIdMap::GetInstance()->GetTabAndWindowId( 104 info->child_id(), info->route_id(), tab_id, window_id); 105 106 // Restrict the resource type to the values we care about. 107 ResourceType::Type* iter = 108 std::find(kResourceTypeValues, ARRAYEND(kResourceTypeValues), 109 info->resource_type()); 110 *resource_type = (iter != ARRAYEND(kResourceTypeValues)) ? 111 *iter : ResourceType::LAST_TYPE; 112} 113 114static void AddEventListenerOnIOThread( 115 ProfileId profile_id, 116 const std::string& extension_id, 117 const std::string& event_name, 118 const std::string& sub_event_name, 119 const ExtensionWebRequestEventRouter::RequestFilter& filter, 120 int extra_info_spec) { 121 ExtensionWebRequestEventRouter::GetInstance()->AddEventListener( 122 profile_id, extension_id, event_name, sub_event_name, filter, 123 extra_info_spec); 124} 125 126static void EventHandledOnIOThread( 127 ProfileId profile_id, 128 const std::string& extension_id, 129 const std::string& event_name, 130 const std::string& sub_event_name, 131 uint64 request_id, 132 bool cancel, 133 const GURL& new_url) { 134 ExtensionWebRequestEventRouter::GetInstance()->OnEventHandled( 135 profile_id, extension_id, event_name, sub_event_name, request_id, 136 cancel, new_url); 137} 138 139} // namespace 140 141// Internal representation of the webRequest.RequestFilter type, used to 142// filter what network events an extension cares about. 143struct ExtensionWebRequestEventRouter::RequestFilter { 144 ExtensionExtent urls; 145 std::vector<ResourceType::Type> types; 146 int tab_id; 147 int window_id; 148 149 RequestFilter() : tab_id(-1), window_id(-1) {} 150 bool InitFromValue(const DictionaryValue& value); 151}; 152 153// Internal representation of the extraInfoSpec parameter on webRequest events, 154// used to specify extra information to be included with network events. 155struct ExtensionWebRequestEventRouter::ExtraInfoSpec { 156 enum Flags { 157 REQUEST_LINE = 1<<0, 158 REQUEST_HEADERS = 1<<1, 159 STATUS_LINE = 1<<2, 160 RESPONSE_HEADERS = 1<<3, 161 REDIRECT_REQUEST_LINE = 1<<4, 162 REDIRECT_REQUEST_HEADERS = 1<<5, 163 BLOCKING = 1<<6, 164 }; 165 166 static bool InitFromValue(const ListValue& value, int* extra_info_spec); 167}; 168 169// Represents a single unique listener to an event, along with whatever filter 170// parameters and extra_info_spec were specified at the time the listener was 171// added. 172struct ExtensionWebRequestEventRouter::EventListener { 173 std::string extension_id; 174 std::string sub_event_name; 175 RequestFilter filter; 176 int extra_info_spec; 177 mutable std::set<uint64> blocked_requests; 178 179 // Comparator to work with std::set. 180 bool operator<(const EventListener& that) const { 181 if (extension_id < that.extension_id) 182 return true; 183 if (extension_id == that.extension_id && 184 sub_event_name < that.sub_event_name) 185 return true; 186 return false; 187 } 188}; 189 190// Contains info about requests that are blocked waiting for a response from 191// an extension. 192struct ExtensionWebRequestEventRouter::BlockedRequest { 193 // The number of event handlers that we are awaiting a response from. 194 int num_handlers_blocking; 195 196 // The callback to call when we get a response from all event handlers. 197 net::CompletionCallback* callback; 198 199 // If non-empty, this contains the new URL that the request will redirect to. 200 GURL* new_url; 201 202 // Time the request was issued. Used for logging purposes. 203 base::Time request_time; 204 205 BlockedRequest() : num_handlers_blocking(0), callback(NULL), new_url(NULL) {} 206}; 207 208bool ExtensionWebRequestEventRouter::RequestFilter::InitFromValue( 209 const DictionaryValue& value) { 210 for (DictionaryValue::key_iterator key = value.begin_keys(); 211 key != value.end_keys(); ++key) { 212 if (*key == "urls") { 213 ListValue* urls_value = NULL; 214 if (!value.GetList("urls", &urls_value)) 215 return false; 216 for (size_t i = 0; i < urls_value->GetSize(); ++i) { 217 std::string url; 218 URLPattern pattern(URLPattern::SCHEME_ALL); 219 if (!urls_value->GetString(i, &url) || 220 pattern.Parse(url, URLPattern::PARSE_STRICT) != 221 URLPattern::PARSE_SUCCESS) 222 return false; 223 urls.AddPattern(pattern); 224 } 225 } else if (*key == "types") { 226 ListValue* types_value = NULL; 227 if (!value.GetList("types", &types_value)) 228 return false; 229 for (size_t i = 0; i < types_value->GetSize(); ++i) { 230 std::string type_str; 231 ResourceType::Type type; 232 if (!types_value->GetString(i, &type_str) || 233 !ParseResourceType(type_str, &type)) 234 return false; 235 types.push_back(type); 236 } 237 } else if (*key == "tabId") { 238 if (!value.GetInteger("tabId", &tab_id)) 239 return false; 240 } else if (*key == "windowId") { 241 if (!value.GetInteger("windowId", &window_id)) 242 return false; 243 } else { 244 return false; 245 } 246 } 247 return true; 248} 249 250// static 251bool ExtensionWebRequestEventRouter::ExtraInfoSpec::InitFromValue( 252 const ListValue& value, int* extra_info_spec) { 253 *extra_info_spec = 0; 254 for (size_t i = 0; i < value.GetSize(); ++i) { 255 std::string str; 256 if (!value.GetString(i, &str)) 257 return false; 258 259 // TODO(mpcomplete): not all of these are valid for every event. 260 if (str == "requestLine") 261 *extra_info_spec |= REQUEST_LINE; 262 else if (str == "requestHeaders") 263 *extra_info_spec |= REQUEST_HEADERS; 264 else if (str == "statusLine") 265 *extra_info_spec |= STATUS_LINE; 266 else if (str == "responseHeaders") 267 *extra_info_spec |= RESPONSE_HEADERS; 268 else if (str == "redirectRequestLine") 269 *extra_info_spec |= REDIRECT_REQUEST_LINE; 270 else if (str == "redirectRequestHeaders") 271 *extra_info_spec |= REDIRECT_REQUEST_HEADERS; 272 else if (str == "blocking") 273 *extra_info_spec |= BLOCKING; 274 else 275 return false; 276 } 277 return true; 278} 279 280// static 281ExtensionWebRequestEventRouter* ExtensionWebRequestEventRouter::GetInstance() { 282 return Singleton<ExtensionWebRequestEventRouter>::get(); 283} 284 285ExtensionWebRequestEventRouter::ExtensionWebRequestEventRouter() { 286} 287 288ExtensionWebRequestEventRouter::~ExtensionWebRequestEventRouter() { 289} 290 291int ExtensionWebRequestEventRouter::OnBeforeRequest( 292 ProfileId profile_id, 293 ExtensionEventRouterForwarder* event_router, 294 net::URLRequest* request, 295 net::CompletionCallback* callback, 296 GURL* new_url) { 297 // TODO(jochen): Figure out what to do with events from the system context. 298 if (profile_id == Profile::kInvalidProfileId) 299 return net::OK; 300 301 int tab_id = -1; 302 int window_id = -1; 303 ResourceType::Type resource_type = ResourceType::LAST_TYPE; 304 ExtractRequestInfo(request, &tab_id, &window_id, &resource_type); 305 306 std::vector<const EventListener*> listeners = 307 GetMatchingListeners(profile_id, keys::kOnBeforeRequest, request->url(), 308 tab_id, window_id, resource_type); 309 if (listeners.empty()) 310 return net::OK; 311 312 // If this is an HTTP request, keep track of it. HTTP-specific events only 313 // have the request ID, so we'll need to look up the URLRequest from that. 314 if (request->url().SchemeIs(chrome::kHttpScheme) || 315 request->url().SchemeIs(chrome::kHttpsScheme)) { 316 http_requests_[request->identifier()] = request; 317 } 318 319 ListValue args; 320 DictionaryValue* dict = new DictionaryValue(); 321 dict->SetString(keys::kRequestIdKey, 322 base::Uint64ToString(request->identifier())); 323 dict->SetString(keys::kUrlKey, request->url().spec()); 324 dict->SetString(keys::kMethodKey, request->method()); 325 dict->SetInteger(keys::kTabIdKey, tab_id); 326 dict->SetString(keys::kTypeKey, ResourceTypeToString(resource_type)); 327 dict->SetDouble(keys::kTimeStampKey, 328 request->request_time().ToDoubleT() * 1000); 329 args.Append(dict); 330 331 if (DispatchEvent(profile_id, event_router, request, callback, listeners, 332 args)) { 333 blocked_requests_[request->identifier()].new_url = new_url; 334 return net::ERR_IO_PENDING; 335 } 336 return net::OK; 337} 338 339int ExtensionWebRequestEventRouter::OnBeforeSendHeaders( 340 ProfileId profile_id, 341 ExtensionEventRouterForwarder* event_router, 342 uint64 request_id, 343 net::CompletionCallback* callback, 344 net::HttpRequestHeaders* headers) { 345 // TODO(jochen): Figure out what to do with events from the system context. 346 if (profile_id == Profile::kInvalidProfileId) 347 return net::OK; 348 349 HttpRequestMap::iterator iter = http_requests_.find(request_id); 350 if (iter == http_requests_.end()) 351 return net::OK; 352 353 net::URLRequest* request = iter->second; 354 http_requests_.erase(iter); 355 356 std::vector<const EventListener*> listeners = 357 GetMatchingListeners(profile_id, keys::kOnBeforeSendHeaders, request); 358 if (listeners.empty()) 359 return net::OK; 360 361 ListValue args; 362 DictionaryValue* dict = new DictionaryValue(); 363 dict->SetString(keys::kRequestIdKey, 364 base::Uint64ToString(request->identifier())); 365 dict->SetString(keys::kUrlKey, request->url().spec()); 366 dict->SetDouble(keys::kTimeStampKey, 367 request->request_time().ToDoubleT() * 1000); 368 // TODO(mpcomplete): request headers. 369 args.Append(dict); 370 371 if (DispatchEvent(profile_id, event_router, request, callback, listeners, 372 args)) 373 return net::ERR_IO_PENDING; 374 return net::OK; 375} 376 377void ExtensionWebRequestEventRouter::OnURLRequestDestroyed( 378 ProfileId profile_id, net::URLRequest* request) { 379 http_requests_.erase(request->identifier()); 380 blocked_requests_.erase(request->identifier()); 381} 382 383bool ExtensionWebRequestEventRouter::DispatchEvent( 384 ProfileId profile_id, 385 ExtensionEventRouterForwarder* event_router, 386 net::URLRequest* request, 387 net::CompletionCallback* callback, 388 const std::vector<const EventListener*>& listeners, 389 const ListValue& args) { 390 std::string json_args; 391 base::JSONWriter::Write(&args, false, &json_args); 392 393 // TODO(mpcomplete): Consider consolidating common (extension_id,json_args) 394 // pairs into a single message sent to a list of sub_event_names. 395 int num_handlers_blocking = 0; 396 for (std::vector<const EventListener*>::const_iterator it = listeners.begin(); 397 it != listeners.end(); ++it) { 398 event_router->DispatchEventToExtension( 399 (*it)->extension_id, (*it)->sub_event_name, json_args, 400 profile_id, true, GURL()); 401 if (callback && (*it)->extra_info_spec & ExtraInfoSpec::BLOCKING) { 402 (*it)->blocked_requests.insert(request->identifier()); 403 ++num_handlers_blocking; 404 } 405 } 406 407 if (num_handlers_blocking > 0) { 408 CHECK(blocked_requests_.find(request->identifier()) == 409 blocked_requests_.end()); 410 blocked_requests_[request->identifier()].num_handlers_blocking = 411 num_handlers_blocking; 412 blocked_requests_[request->identifier()].callback = callback; 413 blocked_requests_[request->identifier()].request_time = 414 request->request_time(); 415 416 return true; 417 } 418 419 return false; 420} 421 422void ExtensionWebRequestEventRouter::OnEventHandled( 423 ProfileId profile_id, 424 const std::string& extension_id, 425 const std::string& event_name, 426 const std::string& sub_event_name, 427 uint64 request_id, 428 bool cancel, 429 const GURL& new_url) { 430 EventListener listener; 431 listener.extension_id = extension_id; 432 listener.sub_event_name = sub_event_name; 433 434 // The listener may have been removed (e.g. due to the process going away) 435 // before we got here. 436 std::set<EventListener>::iterator found = 437 listeners_[profile_id][event_name].find(listener); 438 if (found != listeners_[profile_id][event_name].end()) 439 found->blocked_requests.erase(request_id); 440 441 DecrementBlockCount(request_id, cancel, new_url); 442} 443 444void ExtensionWebRequestEventRouter::AddEventListener( 445 ProfileId profile_id, 446 const std::string& extension_id, 447 const std::string& event_name, 448 const std::string& sub_event_name, 449 const RequestFilter& filter, 450 int extra_info_spec) { 451 if (!IsWebRequestEvent(event_name)) 452 return; 453 454 EventListener listener; 455 listener.extension_id = extension_id; 456 listener.sub_event_name = sub_event_name; 457 listener.filter = filter; 458 listener.extra_info_spec = extra_info_spec; 459 460 CHECK_EQ(listeners_[profile_id][event_name].count(listener), 0u) << 461 "extension=" << extension_id << " event=" << event_name; 462 listeners_[profile_id][event_name].insert(listener); 463} 464 465void ExtensionWebRequestEventRouter::RemoveEventListener( 466 ProfileId profile_id, 467 const std::string& extension_id, 468 const std::string& sub_event_name) { 469 size_t slash_sep = sub_event_name.find('/'); 470 std::string event_name = sub_event_name.substr(0, slash_sep); 471 472 if (!IsWebRequestEvent(event_name)) 473 return; 474 475 EventListener listener; 476 listener.extension_id = extension_id; 477 listener.sub_event_name = sub_event_name; 478 479 CHECK_EQ(listeners_[profile_id][event_name].count(listener), 1u) << 480 "extension=" << extension_id << " event=" << event_name; 481 482 // Unblock any request that this event listener may have been blocking. 483 std::set<EventListener>::iterator found = 484 listeners_[profile_id][event_name].find(listener); 485 for (std::set<uint64>::iterator it = found->blocked_requests.begin(); 486 it != found->blocked_requests.end(); ++it) { 487 DecrementBlockCount(*it, false, GURL()); 488 } 489 490 listeners_[profile_id][event_name].erase(listener); 491} 492 493std::vector<const ExtensionWebRequestEventRouter::EventListener*> 494ExtensionWebRequestEventRouter::GetMatchingListeners( 495 ProfileId profile_id, 496 const std::string& event_name, 497 const GURL& url, 498 int tab_id, 499 int window_id, 500 ResourceType::Type resource_type) { 501 // TODO(mpcomplete): handle profile_id == invalid (should collect all 502 // listeners). 503 std::vector<const EventListener*> matching_listeners; 504 std::set<EventListener>& listeners = listeners_[profile_id][event_name]; 505 for (std::set<EventListener>::iterator it = listeners.begin(); 506 it != listeners.end(); ++it) { 507 if (!it->filter.urls.is_empty() && !it->filter.urls.ContainsURL(url)) 508 continue; 509 if (it->filter.tab_id != -1 && tab_id != it->filter.tab_id) 510 continue; 511 if (it->filter.window_id != -1 && window_id != it->filter.window_id) 512 continue; 513 if (!it->filter.types.empty() && 514 std::find(it->filter.types.begin(), it->filter.types.end(), 515 resource_type) == it->filter.types.end()) 516 continue; 517 518 matching_listeners.push_back(&(*it)); 519 } 520 return matching_listeners; 521} 522 523std::vector<const ExtensionWebRequestEventRouter::EventListener*> 524ExtensionWebRequestEventRouter::GetMatchingListeners( 525 ProfileId profile_id, 526 const std::string& event_name, 527 net::URLRequest* request) { 528 int tab_id = -1; 529 int window_id = -1; 530 ResourceType::Type resource_type = ResourceType::LAST_TYPE; 531 ExtractRequestInfo(request, &tab_id, &window_id, &resource_type); 532 533 return GetMatchingListeners( 534 profile_id, event_name, request->url(), tab_id, window_id, resource_type); 535} 536 537void ExtensionWebRequestEventRouter::DecrementBlockCount(uint64 request_id, 538 bool cancel, 539 const GURL& new_url) { 540 // It's possible that this request was deleted, or cancelled by a previous 541 // event handler. If so, ignore this response. 542 if (blocked_requests_.find(request_id) == blocked_requests_.end()) 543 return; 544 545 BlockedRequest& blocked_request = blocked_requests_[request_id]; 546 int num_handlers_blocking = --blocked_request.num_handlers_blocking; 547 CHECK_GE(num_handlers_blocking, 0); 548 549 if (num_handlers_blocking == 0 || cancel || !new_url.is_empty()) { 550 HISTOGRAM_TIMES("Extensions.NetworkDelay", 551 base::Time::Now() - blocked_request.request_time); 552 553 CHECK(blocked_request.callback); 554 if (!new_url.is_empty()) { 555 CHECK(new_url.is_valid()); 556 *blocked_request.new_url = new_url; 557 } 558 blocked_request.callback->Run(cancel ? net::ERR_EMPTY_RESPONSE : net::OK); 559 blocked_requests_.erase(request_id); 560 } 561} 562 563bool WebRequestAddEventListener::RunImpl() { 564 // Argument 0 is the callback, which we don't use here. 565 566 ExtensionWebRequestEventRouter::RequestFilter filter; 567 if (HasOptionalArgument(1)) { 568 DictionaryValue* value = NULL; 569 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &value)); 570 EXTENSION_FUNCTION_VALIDATE(filter.InitFromValue(*value)); 571 } 572 573 int extra_info_spec = 0; 574 if (HasOptionalArgument(2)) { 575 ListValue* value = NULL; 576 EXTENSION_FUNCTION_VALIDATE(args_->GetList(2, &value)); 577 EXTENSION_FUNCTION_VALIDATE( 578 ExtensionWebRequestEventRouter::ExtraInfoSpec::InitFromValue( 579 *value, &extra_info_spec)); 580 } 581 582 std::string event_name; 583 EXTENSION_FUNCTION_VALIDATE(args_->GetString(3, &event_name)); 584 585 std::string sub_event_name; 586 EXTENSION_FUNCTION_VALIDATE(args_->GetString(4, &sub_event_name)); 587 588 BrowserThread::PostTask( 589 BrowserThread::IO, FROM_HERE, 590 NewRunnableFunction( 591 &AddEventListenerOnIOThread, 592 profile()->GetRuntimeId(), extension_id(), 593 event_name, sub_event_name, filter, extra_info_spec)); 594 595 return true; 596} 597 598bool WebRequestEventHandled::RunImpl() { 599 std::string event_name; 600 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &event_name)); 601 602 std::string sub_event_name; 603 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &sub_event_name)); 604 605 std::string request_id_str; 606 EXTENSION_FUNCTION_VALIDATE(args_->GetString(2, &request_id_str)); 607 // TODO(mpcomplete): string-to-uint64? 608 int64 request_id; 609 EXTENSION_FUNCTION_VALIDATE(base::StringToInt64(request_id_str, &request_id)); 610 611 bool cancel = false; 612 GURL new_url; 613 if (HasOptionalArgument(3)) { 614 DictionaryValue* value = NULL; 615 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(3, &value)); 616 617 if (value->HasKey("cancel")) 618 EXTENSION_FUNCTION_VALIDATE(value->GetBoolean("cancel", &cancel)); 619 620 std::string new_url_str; 621 if (value->HasKey("redirectUrl")) { 622 EXTENSION_FUNCTION_VALIDATE(value->GetString("redirectUrl", 623 &new_url_str)); 624 new_url = GURL(new_url_str); 625 if (!new_url.is_valid()) { 626 error_ = ExtensionErrorUtils::FormatErrorMessage( 627 keys::kInvalidRedirectUrl, new_url_str); 628 return false; 629 } 630 } 631 } 632 633 BrowserThread::PostTask( 634 BrowserThread::IO, FROM_HERE, 635 NewRunnableFunction( 636 &EventHandledOnIOThread, 637 profile()->GetRuntimeId(), extension_id(), 638 event_name, sub_event_name, request_id, cancel, new_url)); 639 640 return true; 641} 642