Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/extensions/extension_webrequest_api.h" | 5 #include "chrome/browser/extensions/extension_webrequest_api.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/json/json_writer.h" | 9 #include "base/json/json_writer.h" |
| 10 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram.h" |
| 11 #include "base/string_number_conversions.h" | 11 #include "base/string_number_conversions.h" |
| 12 #include "base/values.h" | 12 #include "base/values.h" |
| 13 #include "chrome/browser/extensions/extension_event_router_forwarder.h" | 13 #include "chrome/browser/extensions/extension_event_router_forwarder.h" |
| 14 #include "chrome/browser/extensions/extension_webrequest_api_constants.h" | 14 #include "chrome/browser/extensions/extension_webrequest_api_constants.h" |
| 15 #include "chrome/browser/profiles/profile.h" | 15 #include "chrome/browser/profiles/profile.h" |
| 16 #include "chrome/common/extensions/extension.h" | 16 #include "chrome/common/extensions/extension.h" |
| 17 #include "chrome/common/extensions/extension_extent.h" | 17 #include "chrome/common/extensions/extension_extent.h" |
| 18 #include "chrome/common/extensions/url_pattern.h" | 18 #include "chrome/common/extensions/url_pattern.h" |
| 19 #include "chrome/common/url_constants.h" | |
| 19 #include "content/browser/browser_thread.h" | 20 #include "content/browser/browser_thread.h" |
| 20 #include "net/base/net_errors.h" | 21 #include "net/base/net_errors.h" |
| 21 #include "net/url_request/url_request.h" | 22 #include "net/url_request/url_request.h" |
| 22 #include "googleurl/src/gurl.h" | 23 #include "googleurl/src/gurl.h" |
| 23 | 24 |
| 24 namespace keys = extension_webrequest_api_constants; | 25 namespace keys = extension_webrequest_api_constants; |
| 25 | 26 |
| 26 namespace { | 27 namespace { |
| 27 | 28 |
| 28 // List of all the webRequest events. | 29 // List of all the webRequest events. |
| 29 static const char *kWebRequestEvents[] = { | 30 static const char *kWebRequestEvents[] = { |
| 30 keys::kOnBeforeRedirect, | 31 keys::kOnBeforeRedirect, |
| 31 keys::kOnBeforeRequest, | 32 keys::kOnBeforeRequest, |
| 33 keys::kOnBeforeSendHeaders, | |
| 32 keys::kOnCompleted, | 34 keys::kOnCompleted, |
| 33 keys::kOnErrorOccurred, | 35 keys::kOnErrorOccurred, |
| 34 keys::kOnHeadersReceived, | 36 keys::kOnHeadersReceived, |
| 35 keys::kOnRequestSent | 37 keys::kOnRequestSent |
| 36 }; | 38 }; |
| 37 | 39 |
| 38 // TODO(mpcomplete): should this be a set of flags? | 40 // TODO(mpcomplete): should this be a set of flags? |
| 39 static const char* kRequestFilterTypes[] = { | 41 static const char* kRequestFilterTypes[] = { |
| 40 "main_frame", | 42 "main_frame", |
| 41 "sub_frame", | 43 "sub_frame", |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 99 // Internal representation of the extraInfoSpec parameter on webRequest events, | 101 // Internal representation of the extraInfoSpec parameter on webRequest events, |
| 100 // used to specify extra information to be included with network events. | 102 // used to specify extra information to be included with network events. |
| 101 struct ExtensionWebRequestEventRouter::ExtraInfoSpec { | 103 struct ExtensionWebRequestEventRouter::ExtraInfoSpec { |
| 102 enum Flags { | 104 enum Flags { |
| 103 REQUEST_LINE = 1<<0, | 105 REQUEST_LINE = 1<<0, |
| 104 REQUEST_HEADERS = 1<<1, | 106 REQUEST_HEADERS = 1<<1, |
| 105 STATUS_LINE = 1<<2, | 107 STATUS_LINE = 1<<2, |
| 106 RESPONSE_HEADERS = 1<<3, | 108 RESPONSE_HEADERS = 1<<3, |
| 107 REDIRECT_REQUEST_LINE = 1<<4, | 109 REDIRECT_REQUEST_LINE = 1<<4, |
| 108 REDIRECT_REQUEST_HEADERS = 1<<5, | 110 REDIRECT_REQUEST_HEADERS = 1<<5, |
| 109 BLOCKING = 1<<5, | 111 BLOCKING = 1<<6, |
| 110 }; | 112 }; |
| 111 | 113 |
| 112 static bool InitFromValue(const ListValue& value, int* extra_info_spec); | 114 static bool InitFromValue(const ListValue& value, int* extra_info_spec); |
| 113 }; | 115 }; |
| 114 | 116 |
| 115 // Represents a single unique listener to an event, along with whatever filter | 117 // Represents a single unique listener to an event, along with whatever filter |
| 116 // parameters and extra_info_spec were specified at the time the listener was | 118 // parameters and extra_info_spec were specified at the time the listener was |
| 117 // added. | 119 // added. |
| 118 struct ExtensionWebRequestEventRouter::EventListener { | 120 struct ExtensionWebRequestEventRouter::EventListener { |
| 119 std::string extension_id; | 121 std::string extension_id; |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 141 | 143 |
| 142 // The callback to call when we get a response from all event handlers. | 144 // The callback to call when we get a response from all event handlers. |
| 143 net::CompletionCallback* callback; | 145 net::CompletionCallback* callback; |
| 144 | 146 |
| 145 // Time the request was issued. Used for logging purposes. | 147 // Time the request was issued. Used for logging purposes. |
| 146 base::Time request_time; | 148 base::Time request_time; |
| 147 | 149 |
| 148 BlockedRequest() : num_handlers_blocking(0), callback(NULL) {} | 150 BlockedRequest() : num_handlers_blocking(0), callback(NULL) {} |
| 149 }; | 151 }; |
| 150 | 152 |
| 153 // We use this class as UserData on any URLRequests that we are tracking. This | |
|
willchan no longer on Chromium
2011/03/22 23:05:35
Why is this simpler than adding a cancellation API
Matt Perry
2011/03/24 00:11:25
It isn't... I guess I'll do that then :).
| |
| 154 // lets us know when the URLRequest is deleted, so we can remove it from our | |
| 155 // tracked list. | |
| 156 class ExtensionWebRequestEventRouter::TrackedRequest | |
| 157 : public net::URLRequest::UserData { | |
| 158 public: | |
| 159 TrackedRequest(net::URLRequest* request, | |
| 160 ExtensionWebRequestEventRouter* router) | |
| 161 : router_(router), request_(request) { | |
| 162 request->SetUserData(router_, this); | |
| 163 } | |
| 164 virtual ~TrackedRequest() { | |
| 165 if (router_) | |
| 166 router_->OnRequestDeleted(request_); | |
| 167 } | |
| 168 | |
| 169 void StopTracking() { | |
| 170 ExtensionWebRequestEventRouter* key = router_; | |
| 171 router_ = NULL; | |
| 172 request_->SetUserData(key, NULL); // deletes |this| | |
| 173 } | |
| 174 | |
| 175 net::URLRequest* request() const { return request_; } | |
| 176 | |
| 177 private: | |
| 178 ExtensionWebRequestEventRouter* router_; | |
| 179 net::URLRequest* request_; | |
| 180 }; | |
| 181 | |
| 151 bool ExtensionWebRequestEventRouter::RequestFilter::InitFromValue( | 182 bool ExtensionWebRequestEventRouter::RequestFilter::InitFromValue( |
| 152 const DictionaryValue& value) { | 183 const DictionaryValue& value) { |
| 153 for (DictionaryValue::key_iterator key = value.begin_keys(); | 184 for (DictionaryValue::key_iterator key = value.begin_keys(); |
| 154 key != value.end_keys(); ++key) { | 185 key != value.end_keys(); ++key) { |
| 155 if (*key == "urls") { | 186 if (*key == "urls") { |
| 156 ListValue* urls_value = NULL; | 187 ListValue* urls_value = NULL; |
| 157 if (!value.GetList("urls", &urls_value)) | 188 if (!value.GetList("urls", &urls_value)) |
| 158 return false; | 189 return false; |
| 159 for (size_t i = 0; i < urls_value->GetSize(); ++i) { | 190 for (size_t i = 0; i < urls_value->GetSize(); ++i) { |
| 160 std::string url; | 191 std::string url; |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 221 | 252 |
| 222 // static | 253 // static |
| 223 ExtensionWebRequestEventRouter* ExtensionWebRequestEventRouter::GetInstance() { | 254 ExtensionWebRequestEventRouter* ExtensionWebRequestEventRouter::GetInstance() { |
| 224 return Singleton<ExtensionWebRequestEventRouter>::get(); | 255 return Singleton<ExtensionWebRequestEventRouter>::get(); |
| 225 } | 256 } |
| 226 | 257 |
| 227 ExtensionWebRequestEventRouter::ExtensionWebRequestEventRouter() { | 258 ExtensionWebRequestEventRouter::ExtensionWebRequestEventRouter() { |
| 228 } | 259 } |
| 229 | 260 |
| 230 ExtensionWebRequestEventRouter::~ExtensionWebRequestEventRouter() { | 261 ExtensionWebRequestEventRouter::~ExtensionWebRequestEventRouter() { |
| 262 for (HttpRequestMap::iterator it = http_requests_.begin(); | |
| 263 it != http_requests_.end(); ++it) { | |
| 264 it->second->StopTracking(); // deletes the object | |
| 265 } | |
| 231 } | 266 } |
| 232 | 267 |
| 233 bool ExtensionWebRequestEventRouter::OnBeforeRequest( | 268 bool ExtensionWebRequestEventRouter::OnBeforeRequest( |
| 234 ProfileId profile_id, | 269 ProfileId profile_id, |
| 235 ExtensionEventRouterForwarder* event_router, | 270 ExtensionEventRouterForwarder* event_router, |
| 236 net::URLRequest* request, | 271 net::URLRequest* request, |
| 237 net::CompletionCallback* callback) { | 272 net::CompletionCallback* callback) { |
| 238 // TODO(jochen): Figure out what to do with events from the system context. | 273 // TODO(jochen): Figure out what to do with events from the system context. |
| 239 if (profile_id == Profile::kInvalidProfileId) | 274 if (profile_id == Profile::kInvalidProfileId) |
| 240 return false; | 275 return false; |
| 241 std::vector<const EventListener*> listeners = | 276 std::vector<const EventListener*> listeners = |
| 242 GetMatchingListeners(profile_id, keys::kOnBeforeRequest, request->url()); | 277 GetMatchingListeners(profile_id, keys::kOnBeforeRequest, request->url()); |
| 243 if (listeners.empty()) | 278 if (listeners.empty()) |
| 244 return false; | 279 return false; |
| 245 | 280 |
| 281 // If this is an HTTP request, keep track of it. HTTP-specific events only | |
| 282 // have the request ID, so we'll need to look up the URLRequest from that. | |
| 283 if (request->url().SchemeIs(chrome::kHttpScheme) || | |
| 284 request->url().SchemeIs(chrome::kHttpsScheme)) { | |
| 285 http_requests_[request->identifier()] = new TrackedRequest(request, this); | |
| 286 } | |
| 287 | |
| 246 ListValue args; | 288 ListValue args; |
| 247 DictionaryValue* dict = new DictionaryValue(); | 289 DictionaryValue* dict = new DictionaryValue(); |
| 248 dict->SetString(keys::kUrlKey, request->url().spec()); | 290 dict->SetString(keys::kUrlKey, request->url().spec()); |
| 249 dict->SetString(keys::kMethodKey, request->method()); | 291 dict->SetString(keys::kMethodKey, request->method()); |
| 250 // TODO(mpcomplete): implement | 292 // TODO(mpcomplete): implement |
| 251 dict->SetInteger(keys::kTabIdKey, 0); | 293 dict->SetInteger(keys::kTabIdKey, 0); |
| 252 dict->SetString(keys::kRequestIdKey, | 294 dict->SetString(keys::kRequestIdKey, |
| 253 base::Uint64ToString(request->identifier())); | 295 base::Uint64ToString(request->identifier())); |
| 254 dict->SetString(keys::kTypeKey, "main_frame"); | 296 dict->SetString(keys::kTypeKey, "main_frame"); |
| 255 dict->SetInteger(keys::kTimeStampKey, 1); | 297 dict->SetInteger(keys::kTimeStampKey, 1); |
| 256 args.Append(dict); | 298 args.Append(dict); |
| 257 | 299 |
| 300 return DispatchEvent(profile_id, event_router, request, callback, listeners, | |
| 301 args); | |
| 302 } | |
| 303 | |
| 304 bool ExtensionWebRequestEventRouter::OnBeforeSendHeaders( | |
| 305 ProfileId profile_id, | |
| 306 ExtensionEventRouterForwarder* event_router, | |
| 307 uint64 request_id, | |
| 308 net::HttpRequestHeaders* headers, | |
| 309 net::CompletionCallback* callback) { | |
| 310 // TODO(jochen): Figure out what to do with events from the system context. | |
| 311 if (profile_id == Profile::kInvalidProfileId) | |
| 312 return false; | |
| 313 | |
| 314 HttpRequestMap::iterator iter = http_requests_.find(request_id); | |
| 315 if (iter == http_requests_.end()) | |
| 316 return false; | |
| 317 | |
| 318 net::URLRequest* request = iter->second->request(); | |
| 319 iter->second->StopTracking(); | |
| 320 http_requests_.erase(iter); | |
| 321 | |
| 322 std::vector<const EventListener*> listeners = | |
| 323 GetMatchingListeners(profile_id, keys::kOnBeforeSendHeaders, request); | |
| 324 if (listeners.empty()) | |
| 325 return false; | |
| 326 | |
| 327 ListValue args; | |
| 328 DictionaryValue* dict = new DictionaryValue(); | |
| 329 dict->SetString(keys::kRequestIdKey, | |
| 330 base::Uint64ToString(request->identifier())); | |
| 331 dict->SetString(keys::kUrlKey, request->url().spec()); | |
| 332 dict->SetDouble(keys::kTimeStampKey, base::Time::Now().ToDoubleT() * 1000); | |
| 333 // TODO(mpcomplete): request headers. | |
| 334 args.Append(dict); | |
| 335 | |
| 336 return DispatchEvent(profile_id, event_router, request, callback, listeners, | |
| 337 args); | |
| 338 } | |
| 339 | |
| 340 bool ExtensionWebRequestEventRouter::DispatchEvent( | |
| 341 ProfileId profile_id, | |
| 342 ExtensionEventRouterForwarder* event_router, | |
| 343 net::URLRequest* request, | |
| 344 net::CompletionCallback* callback, | |
| 345 const std::vector<const EventListener*>& listeners, | |
| 346 const ListValue& args) { | |
| 258 std::string json_args; | 347 std::string json_args; |
| 259 base::JSONWriter::Write(&args, false, &json_args); | 348 base::JSONWriter::Write(&args, false, &json_args); |
| 260 | 349 |
| 261 // TODO(mpcomplete): Consider consolidating common (extension_id,json_args) | 350 // TODO(mpcomplete): Consider consolidating common (extension_id,json_args) |
| 262 // pairs into a single message sent to a list of sub_event_names. | 351 // pairs into a single message sent to a list of sub_event_names. |
| 263 int num_handlers_blocking = 0; | 352 int num_handlers_blocking = 0; |
| 264 for (std::vector<const EventListener*>::iterator it = listeners.begin(); | 353 for (std::vector<const EventListener*>::const_iterator it = listeners.begin(); |
| 265 it != listeners.end(); ++it) { | 354 it != listeners.end(); ++it) { |
| 266 event_router->DispatchEventToExtension( | 355 event_router->DispatchEventToExtension( |
| 267 (*it)->extension_id, (*it)->sub_event_name, json_args, | 356 (*it)->extension_id, (*it)->sub_event_name, json_args, |
| 268 profile_id, true, GURL()); | 357 profile_id, true, GURL()); |
| 269 if ((*it)->extra_info_spec & ExtraInfoSpec::BLOCKING) { | 358 if (callback && (*it)->extra_info_spec & ExtraInfoSpec::BLOCKING) { |
| 270 (*it)->blocked_requests.insert(request->identifier()); | 359 (*it)->blocked_requests.insert(request->identifier()); |
| 271 ++num_handlers_blocking; | 360 ++num_handlers_blocking; |
| 272 } | 361 } |
| 273 } | 362 } |
| 274 | 363 |
| 275 if (num_handlers_blocking > 0) { | 364 if (num_handlers_blocking > 0) { |
| 276 CHECK(blocked_requests_.find(request->identifier()) == | 365 CHECK(blocked_requests_.find(request->identifier()) == |
| 277 blocked_requests_.end()); | 366 blocked_requests_.end()); |
| 278 blocked_requests_[request->identifier()].num_handlers_blocking = | 367 blocked_requests_[request->identifier()].num_handlers_blocking = |
| 279 num_handlers_blocking; | 368 num_handlers_blocking; |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 367 std::vector<const EventListener*> matching_listeners; | 456 std::vector<const EventListener*> matching_listeners; |
| 368 std::set<EventListener>& listeners = listeners_[profile_id][event_name]; | 457 std::set<EventListener>& listeners = listeners_[profile_id][event_name]; |
| 369 for (std::set<EventListener>::iterator it = listeners.begin(); | 458 for (std::set<EventListener>::iterator it = listeners.begin(); |
| 370 it != listeners.end(); ++it) { | 459 it != listeners.end(); ++it) { |
| 371 if (it->filter.urls.is_empty() || it->filter.urls.ContainsURL(url)) | 460 if (it->filter.urls.is_empty() || it->filter.urls.ContainsURL(url)) |
| 372 matching_listeners.push_back(&(*it)); | 461 matching_listeners.push_back(&(*it)); |
| 373 } | 462 } |
| 374 return matching_listeners; | 463 return matching_listeners; |
| 375 } | 464 } |
| 376 | 465 |
| 466 std::vector<const ExtensionWebRequestEventRouter::EventListener*> | |
| 467 ExtensionWebRequestEventRouter::GetMatchingListeners( | |
| 468 ProfileId profile_id, | |
| 469 const std::string& event_name, | |
| 470 net::URLRequest* request) { | |
| 471 int tab_id = -1; | |
| 472 int window_id = -1; | |
| 473 ResourceType::Type resource_type = ResourceType::LAST_TYPE; | |
| 474 ExtractRequestInfo(request, &tab_id, &window_id, &resource_type); | |
| 475 | |
| 476 return GetMatchingListeners( | |
| 477 profile_id, event_name, request->url(), tab_id, window_id, resource_type); | |
| 478 } | |
| 479 | |
| 377 void ExtensionWebRequestEventRouter::DecrementBlockCount(uint64 request_id, | 480 void ExtensionWebRequestEventRouter::DecrementBlockCount(uint64 request_id, |
| 378 bool cancel) { | 481 bool cancel) { |
| 379 // It's possible that this request was already cancelled by a previous event | 482 // It's possible that this request was already cancelled by a previous event |
| 380 // handler. If so, ignore this response. | 483 // handler. If so, ignore this response. |
| 381 if (blocked_requests_.find(request_id) == blocked_requests_.end()) | 484 if (blocked_requests_.find(request_id) == blocked_requests_.end()) |
| 382 return; | 485 return; |
| 383 | 486 |
| 384 BlockedRequest& blocked_request = blocked_requests_[request_id]; | 487 BlockedRequest& blocked_request = blocked_requests_[request_id]; |
| 385 int num_handlers_blocking = --blocked_request.num_handlers_blocking; | 488 int num_handlers_blocking = --blocked_request.num_handlers_blocking; |
| 386 CHECK_GE(num_handlers_blocking, 0); | 489 CHECK_GE(num_handlers_blocking, 0); |
| 387 | 490 |
| 388 HISTOGRAM_TIMES("Extensions.NetworkDelay", | 491 HISTOGRAM_TIMES("Extensions.NetworkDelay", |
| 389 base::Time::Now() - blocked_request.request_time); | 492 base::Time::Now() - blocked_request.request_time); |
| 390 | 493 |
| 391 if (num_handlers_blocking == 0 || cancel) { | 494 if (num_handlers_blocking == 0 || cancel) { |
| 392 CHECK(blocked_request.callback); | 495 CHECK(blocked_request.callback); |
| 393 blocked_request.callback->Run(cancel ? net::ERR_EMPTY_RESPONSE : net::OK); | 496 blocked_request.callback->Run(cancel ? net::ERR_EMPTY_RESPONSE : net::OK); |
| 394 blocked_requests_.erase(request_id); | 497 blocked_requests_.erase(request_id); |
| 395 } | 498 } |
| 396 } | 499 } |
| 397 | 500 |
| 501 void ExtensionWebRequestEventRouter::OnRequestDeleted( | |
| 502 net::URLRequest* request) { | |
| 503 http_requests_.erase(request->identifier()); | |
| 504 } | |
| 505 | |
| 398 bool WebRequestAddEventListener::RunImpl() { | 506 bool WebRequestAddEventListener::RunImpl() { |
| 399 // Argument 0 is the callback, which we don't use here. | 507 // Argument 0 is the callback, which we don't use here. |
| 400 | 508 |
| 401 ExtensionWebRequestEventRouter::RequestFilter filter; | 509 ExtensionWebRequestEventRouter::RequestFilter filter; |
| 402 if (HasOptionalArgument(1)) { | 510 if (HasOptionalArgument(1)) { |
| 403 DictionaryValue* value = NULL; | 511 DictionaryValue* value = NULL; |
| 404 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &value)); | 512 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &value)); |
| 405 EXTENSION_FUNCTION_VALIDATE(filter.InitFromValue(*value)); | 513 EXTENSION_FUNCTION_VALIDATE(filter.InitFromValue(*value)); |
| 406 } | 514 } |
| 407 | 515 |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 454 | 562 |
| 455 BrowserThread::PostTask( | 563 BrowserThread::PostTask( |
| 456 BrowserThread::IO, FROM_HERE, | 564 BrowserThread::IO, FROM_HERE, |
| 457 NewRunnableFunction( | 565 NewRunnableFunction( |
| 458 &EventHandledOnIOThread, | 566 &EventHandledOnIOThread, |
| 459 profile()->GetRuntimeId(), extension_id(), | 567 profile()->GetRuntimeId(), extension_id(), |
| 460 event_name, sub_event_name, request_id, cancel)); | 568 event_name, sub_event_name, request_id, cancel)); |
| 461 | 569 |
| 462 return true; | 570 return true; |
| 463 } | 571 } |
| OLD | NEW |