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