• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
29 namespace keys = extension_webrequest_api_constants;
30 
31 namespace {
32 
33 // List of all the webRequest events.
34 static 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 
44 static const char* kResourceTypeStrings[] = {
45   "main_frame",
46   "sub_frame",
47   "stylesheet",
48   "script",
49   "image",
50   "object",
51   "other",
52 };
53 
54 static 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 
64 COMPILE_ASSERT(
65     arraysize(kResourceTypeStrings) == arraysize(kResourceTypeValues),
66     keep_resource_types_in_sync);
67 
68 #define ARRAYEND(array) (array + arraysize(array))
69 
IsWebRequestEvent(const std::string & event_name)70 static bool IsWebRequestEvent(const std::string& event_name) {
71   return std::find(kWebRequestEvents, ARRAYEND(kWebRequestEvents),
72                    event_name) != ARRAYEND(kWebRequestEvents);
73 }
74 
ResourceTypeToString(ResourceType::Type type)75 static 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 
ParseResourceType(const std::string & type_str,ResourceType::Type * type)84 static 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 
ExtractRequestInfo(net::URLRequest * request,int * tab_id,int * window_id,ResourceType::Type * resource_type)94 static 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 
AddEventListenerOnIOThread(ProfileId profile_id,const std::string & extension_id,const std::string & event_name,const std::string & sub_event_name,const ExtensionWebRequestEventRouter::RequestFilter & filter,int extra_info_spec)114 static 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 
EventHandledOnIOThread(ProfileId profile_id,const std::string & extension_id,const std::string & event_name,const std::string & sub_event_name,uint64 request_id,bool cancel,const GURL & new_url)126 static 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.
143 struct ExtensionWebRequestEventRouter::RequestFilter {
144   ExtensionExtent urls;
145   std::vector<ResourceType::Type> types;
146   int tab_id;
147   int window_id;
148 
RequestFilterExtensionWebRequestEventRouter::RequestFilter149   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.
155 struct 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.
172 struct 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.
operator <ExtensionWebRequestEventRouter::EventListener180   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.
192 struct 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 
BlockedRequestExtensionWebRequestEventRouter::BlockedRequest205   BlockedRequest() : num_handlers_blocking(0), callback(NULL), new_url(NULL) {}
206 };
207 
InitFromValue(const DictionaryValue & value)208 bool 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
InitFromValue(const ListValue & value,int * extra_info_spec)251 bool 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
GetInstance()281 ExtensionWebRequestEventRouter* ExtensionWebRequestEventRouter::GetInstance() {
282   return Singleton<ExtensionWebRequestEventRouter>::get();
283 }
284 
ExtensionWebRequestEventRouter()285 ExtensionWebRequestEventRouter::ExtensionWebRequestEventRouter() {
286 }
287 
~ExtensionWebRequestEventRouter()288 ExtensionWebRequestEventRouter::~ExtensionWebRequestEventRouter() {
289 }
290 
OnBeforeRequest(ProfileId profile_id,ExtensionEventRouterForwarder * event_router,net::URLRequest * request,net::CompletionCallback * callback,GURL * new_url)291 int 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 
OnBeforeSendHeaders(ProfileId profile_id,ExtensionEventRouterForwarder * event_router,uint64 request_id,net::CompletionCallback * callback,net::HttpRequestHeaders * headers)339 int 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 
OnURLRequestDestroyed(ProfileId profile_id,net::URLRequest * request)377 void ExtensionWebRequestEventRouter::OnURLRequestDestroyed(
378     ProfileId profile_id, net::URLRequest* request) {
379   http_requests_.erase(request->identifier());
380   blocked_requests_.erase(request->identifier());
381 }
382 
DispatchEvent(ProfileId profile_id,ExtensionEventRouterForwarder * event_router,net::URLRequest * request,net::CompletionCallback * callback,const std::vector<const EventListener * > & listeners,const ListValue & args)383 bool 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 
OnEventHandled(ProfileId profile_id,const std::string & extension_id,const std::string & event_name,const std::string & sub_event_name,uint64 request_id,bool cancel,const GURL & new_url)422 void 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 
AddEventListener(ProfileId profile_id,const std::string & extension_id,const std::string & event_name,const std::string & sub_event_name,const RequestFilter & filter,int extra_info_spec)444 void 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 
RemoveEventListener(ProfileId profile_id,const std::string & extension_id,const std::string & sub_event_name)465 void 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 
493 std::vector<const ExtensionWebRequestEventRouter::EventListener*>
GetMatchingListeners(ProfileId profile_id,const std::string & event_name,const GURL & url,int tab_id,int window_id,ResourceType::Type resource_type)494 ExtensionWebRequestEventRouter::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 
523 std::vector<const ExtensionWebRequestEventRouter::EventListener*>
GetMatchingListeners(ProfileId profile_id,const std::string & event_name,net::URLRequest * request)524 ExtensionWebRequestEventRouter::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 
DecrementBlockCount(uint64 request_id,bool cancel,const GURL & new_url)537 void 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 
RunImpl()563 bool 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 
RunImpl()598 bool 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