1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/extensions/api/web_request/web_request_api.h"
10 #include "base/bind_helpers.h"
11 #include "base/json/json_writer.h"
12 #include "base/lazy_instance.h"
13 #include "base/metrics/histogram.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/time/time.h"
18 #include "base/values.h"
19 #include "chrome/browser/browser_process.h"
20 #include "chrome/browser/chrome_content_browser_client.h"
21 #include "chrome/browser/extensions/activity_log/activity_action_constants.h"
22 #include "chrome/browser/extensions/activity_log/activity_actions.h"
23 #include "chrome/browser/extensions/activity_log/activity_log.h"
24 #include "chrome/browser/extensions/activity_log/web_request_constants.h"
25 #include "chrome/browser/extensions/api/declarative_webrequest/request_stage.h"
26 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_constants.h"
27 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry.h"
28 #include "chrome/browser/extensions/api/web_request/upload_data_presenter.h"
29 #include "chrome/browser/extensions/api/web_request/web_request_api_constants.h"
30 #include "chrome/browser/extensions/api/web_request/web_request_api_helpers.h"
31 #include "chrome/browser/extensions/api/web_request/web_request_time_tracker.h"
32 #include "chrome/browser/extensions/extension_renderer_state.h"
33 #include "chrome/browser/extensions/extension_warning_service.h"
34 #include "chrome/browser/extensions/extension_warning_set.h"
35 #include "chrome/browser/guest_view/web_view/web_view_constants.h"
36 #include "chrome/browser/guest_view/web_view/web_view_renderer_state.h"
37 #include "chrome/browser/profiles/profile.h"
38 #include "chrome/browser/profiles/profile_manager.h"
39 #include "chrome/common/extensions/api/web_request.h"
40 #include "chrome/common/extensions/extension_constants.h"
41 #include "chrome/common/url_constants.h"
42 #include "content/public/browser/browser_message_filter.h"
43 #include "content/public/browser/browser_thread.h"
44 #include "content/public/browser/render_frame_host.h"
45 #include "content/public/browser/render_process_host.h"
46 #include "content/public/browser/resource_request_info.h"
47 #include "content/public/browser/user_metrics.h"
48 #include "extensions/browser/event_router.h"
49 #include "extensions/browser/extension_message_filter.h"
50 #include "extensions/browser/extension_prefs.h"
51 #include "extensions/browser/extension_registry.h"
52 #include "extensions/browser/extension_system.h"
53 #include "extensions/browser/info_map.h"
54 #include "extensions/browser/runtime_data.h"
55 #include "extensions/common/error_utils.h"
56 #include "extensions/common/event_filtering_info.h"
57 #include "extensions/common/extension.h"
58 #include "extensions/common/extension_messages.h"
59 #include "extensions/common/extension_set.h"
60 #include "extensions/common/features/feature.h"
61 #include "extensions/common/permissions/permissions_data.h"
62 #include "extensions/common/url_pattern.h"
63 #include "grit/generated_resources.h"
64 #include "net/base/auth.h"
65 #include "net/base/net_errors.h"
66 #include "net/base/upload_data_stream.h"
67 #include "net/http/http_response_headers.h"
68 #include "net/url_request/url_request.h"
69 #include "ui/base/l10n/l10n_util.h"
72 using base::DictionaryValue
;
73 using base::ListValue
;
74 using base::StringValue
;
75 using content::BrowserMessageFilter
;
76 using content::BrowserThread
;
77 using content::ResourceRequestInfo
;
78 using content::ResourceType
;
79 using extensions::ErrorUtils
;
80 using extensions::Extension
;
81 using extensions::ExtensionWarning
;
82 using extensions::ExtensionWarningService
;
83 using extensions::ExtensionWarningSet
;
84 using extensions::InfoMap
;
85 using extensions::Feature
;
86 using extensions::RulesRegistryService
;
88 namespace helpers
= extension_web_request_api_helpers
;
89 namespace keys
= extension_web_request_api_constants
;
90 namespace web_request
= extensions::api::web_request
;
91 namespace declarative_keys
= extensions::declarative_webrequest_constants
;
92 namespace activitylog
= activity_log_web_request_constants
;
96 const char kWebRequestEventPrefix
[] = "webRequest.";
98 // List of all the webRequest events.
99 const char* const kWebRequestEvents
[] = {
100 keys::kOnBeforeRedirectEvent
,
101 web_request::OnBeforeRequest::kEventName
,
102 keys::kOnBeforeSendHeadersEvent
,
103 keys::kOnCompletedEvent
,
104 web_request::OnErrorOccurred::kEventName
,
105 keys::kOnSendHeadersEvent
,
106 keys::kOnAuthRequiredEvent
,
107 keys::kOnResponseStartedEvent
,
108 keys::kOnHeadersReceivedEvent
,
111 #define ARRAYEND(array) (array + arraysize(array))
113 const char* GetRequestStageAsString(
114 ExtensionWebRequestEventRouter::EventTypes type
) {
116 case ExtensionWebRequestEventRouter::kInvalidEvent
:
118 case ExtensionWebRequestEventRouter::kOnBeforeRequest
:
119 return keys::kOnBeforeRequest
;
120 case ExtensionWebRequestEventRouter::kOnBeforeSendHeaders
:
121 return keys::kOnBeforeSendHeaders
;
122 case ExtensionWebRequestEventRouter::kOnSendHeaders
:
123 return keys::kOnSendHeaders
;
124 case ExtensionWebRequestEventRouter::kOnHeadersReceived
:
125 return keys::kOnHeadersReceived
;
126 case ExtensionWebRequestEventRouter::kOnBeforeRedirect
:
127 return keys::kOnBeforeRedirect
;
128 case ExtensionWebRequestEventRouter::kOnAuthRequired
:
129 return keys::kOnAuthRequired
;
130 case ExtensionWebRequestEventRouter::kOnResponseStarted
:
131 return keys::kOnResponseStarted
;
132 case ExtensionWebRequestEventRouter::kOnErrorOccurred
:
133 return keys::kOnErrorOccurred
;
134 case ExtensionWebRequestEventRouter::kOnCompleted
:
135 return keys::kOnCompleted
;
138 return "Not reached";
141 // TODO(dcheng): Fix plumbing. Frame ID is not an int64--it's just an int.
142 int GetFrameId(bool is_main_frame
, int64 frame_id
) {
143 return is_main_frame
? 0 : static_cast<int>(frame_id
);
146 bool IsWebRequestEvent(const std::string
& event_name
) {
147 std::string
web_request_event_name(event_name
);
149 web_request_event_name
, webview::kWebViewEventPrefix
, true)) {
150 web_request_event_name
.replace(
151 0, strlen(webview::kWebViewEventPrefix
), kWebRequestEventPrefix
);
153 return std::find(kWebRequestEvents
, ARRAYEND(kWebRequestEvents
),
154 web_request_event_name
) != ARRAYEND(kWebRequestEvents
);
157 // Returns whether |request| has been triggered by an extension in
158 // |extension_info_map|.
159 bool IsRequestFromExtension(const net::URLRequest
* request
,
160 const InfoMap
* extension_info_map
) {
161 // |extension_info_map| is NULL for system-level requests.
162 if (!extension_info_map
)
165 const ResourceRequestInfo
* info
= ResourceRequestInfo::ForRequest(request
);
167 // If this request was not created by the ResourceDispatcher, |info| is NULL.
168 // All requests from extensions are created by the ResourceDispatcher.
172 return extension_info_map
->process_map().Contains(info
->GetChildID());
175 void ExtractRequestRoutingInfo(net::URLRequest
* request
,
176 int* render_process_host_id
,
178 if (!request
->GetUserData(NULL
))
180 const ResourceRequestInfo
* info
= ResourceRequestInfo::ForRequest(request
);
181 *render_process_host_id
= info
->GetChildID();
182 *routing_id
= info
->GetRouteID();
185 // Given a |request|, this function determines whether it originated from
186 // a <webview> guest process or not. If it is from a <webview> guest process,
187 // then |web_view_info| is returned with information about the instance ID
188 // that uniquely identifies the <webview> and its embedder.
189 bool GetWebViewInfo(net::URLRequest
* request
,
190 WebViewRendererState::WebViewInfo
* web_view_info
) {
191 int render_process_host_id
= -1;
193 ExtractRequestRoutingInfo(request
, &render_process_host_id
, &routing_id
);
194 return WebViewRendererState::GetInstance()->
195 GetInfo(render_process_host_id
, routing_id
, web_view_info
);
198 void ExtractRequestInfoDetails(net::URLRequest
* request
,
201 bool* parent_is_main_frame
,
202 int64
* parent_frame_id
,
205 int* render_process_host_id
,
207 ResourceType::Type
* resource_type
) {
208 if (!request
->GetUserData(NULL
))
211 const ResourceRequestInfo
* info
= ResourceRequestInfo::ForRequest(request
);
212 ExtensionRendererState::GetInstance()->GetTabAndWindowId(
213 info
, tab_id
, window_id
);
214 *frame_id
= info
->GetRenderFrameID();
215 *is_main_frame
= info
->IsMainFrame();
216 *parent_frame_id
= info
->GetParentRenderFrameID();
217 *parent_is_main_frame
= info
->ParentIsMainFrame();
218 *render_process_host_id
= info
->GetChildID();
219 *routing_id
= info
->GetRouteID();
221 // Restrict the resource type to the values we care about.
222 if (helpers::IsRelevantResourceType(info
->GetResourceType()))
223 *resource_type
= info
->GetResourceType();
225 *resource_type
= ResourceType::LAST_TYPE
;
228 // Extracts from |request| information for the keys requestId, url, method,
229 // frameId, tabId, type, and timeStamp and writes these into |out| to be passed
231 void ExtractRequestInfo(net::URLRequest
* request
, base::DictionaryValue
* out
) {
232 bool is_main_frame
= false;
234 bool parent_is_main_frame
= false;
235 int64 parent_frame_id
= -1;
236 int frame_id_for_extension
= -1;
237 int parent_frame_id_for_extension
= -1;
240 int render_process_host_id
= -1;
242 ResourceType::Type resource_type
= ResourceType::LAST_TYPE
;
243 ExtractRequestInfoDetails(request
, &is_main_frame
, &frame_id
,
244 &parent_is_main_frame
, &parent_frame_id
, &tab_id
,
245 &window_id
, &render_process_host_id
, &routing_id
,
247 frame_id_for_extension
= GetFrameId(is_main_frame
, frame_id
);
248 parent_frame_id_for_extension
= GetFrameId(parent_is_main_frame
,
251 out
->SetString(keys::kRequestIdKey
,
252 base::Uint64ToString(request
->identifier()));
253 out
->SetString(keys::kUrlKey
, request
->url().spec());
254 out
->SetString(keys::kMethodKey
, request
->method());
255 out
->SetInteger(keys::kFrameIdKey
, frame_id_for_extension
);
256 out
->SetInteger(keys::kParentFrameIdKey
, parent_frame_id_for_extension
);
257 out
->SetInteger(keys::kTabIdKey
, tab_id
);
258 out
->SetString(keys::kTypeKey
, helpers::ResourceTypeToString(resource_type
));
259 out
->SetDouble(keys::kTimeStampKey
, base::Time::Now().ToDoubleT() * 1000);
262 // Extracts the body from |request| and writes the data into |out|.
263 void ExtractRequestInfoBody(const net::URLRequest
* request
,
264 base::DictionaryValue
* out
) {
265 const net::UploadDataStream
* upload_data
= request
->get_upload();
267 (request
->method() != "POST" && request
->method() != "PUT"))
268 return; // Need to exit without "out->Set(keys::kRequestBodyKey, ...);" .
270 base::DictionaryValue
* requestBody
= new base::DictionaryValue();
271 out
->Set(keys::kRequestBodyKey
, requestBody
);
273 // Get the data presenters, ordered by how specific they are.
274 extensions::ParsedDataPresenter
parsed_data_presenter(*request
);
275 extensions::RawDataPresenter raw_data_presenter
;
276 extensions::UploadDataPresenter
* const presenters
[] = {
277 &parsed_data_presenter
, // 1: any parseable forms? (Specific to forms.)
278 &raw_data_presenter
// 2: any data at all? (Non-specific.)
280 // Keys for the results of the corresponding presenters.
281 static const char* const kKeys
[] = {
282 keys::kRequestBodyFormDataKey
,
283 keys::kRequestBodyRawKey
286 const ScopedVector
<net::UploadElementReader
>& readers
=
287 upload_data
->element_readers();
288 bool some_succeeded
= false;
289 for (size_t i
= 0; !some_succeeded
&& i
< arraysize(presenters
); ++i
) {
290 ScopedVector
<net::UploadElementReader
>::const_iterator reader
;
291 for (reader
= readers
.begin(); reader
!= readers
.end(); ++reader
)
292 presenters
[i
]->FeedNext(**reader
);
293 if (presenters
[i
]->Succeeded()) {
294 requestBody
->Set(kKeys
[i
], presenters
[i
]->Result().release());
295 some_succeeded
= true;
299 requestBody
->SetString(keys::kRequestBodyErrorKey
, "Unknown error.");
302 // Converts a HttpHeaders dictionary to a |name|, |value| pair. Returns
303 // true if successful.
304 bool FromHeaderDictionary(const base::DictionaryValue
* header_value
,
306 std::string
* value
) {
307 if (!header_value
->GetString(keys::kHeaderNameKey
, name
))
310 // We require either a "value" or a "binaryValue" entry.
311 if (!(header_value
->HasKey(keys::kHeaderValueKey
) ^
312 header_value
->HasKey(keys::kHeaderBinaryValueKey
)))
315 if (header_value
->HasKey(keys::kHeaderValueKey
)) {
316 if (!header_value
->GetString(keys::kHeaderValueKey
, value
)) {
319 } else if (header_value
->HasKey(keys::kHeaderBinaryValueKey
)) {
320 const base::ListValue
* list
= NULL
;
321 if (!header_value
->HasKey(keys::kHeaderBinaryValueKey
)) {
323 } else if (!header_value
->GetList(keys::kHeaderBinaryValueKey
, &list
) ||
324 !helpers::CharListToString(list
, value
)) {
331 // Converts the |name|, |value| pair of a http header to a HttpHeaders
332 // dictionary. Ownership is passed to the caller.
333 base::DictionaryValue
* ToHeaderDictionary(const std::string
& name
,
334 const std::string
& value
) {
335 base::DictionaryValue
* header
= new base::DictionaryValue();
336 header
->SetString(keys::kHeaderNameKey
, name
);
337 if (base::IsStringUTF8(value
)) {
338 header
->SetString(keys::kHeaderValueKey
, value
);
340 header
->Set(keys::kHeaderBinaryValueKey
,
341 helpers::StringToCharList(value
));
346 // Creates a list of HttpHeaders (see the extension API JSON). If |headers| is
347 // NULL, the list is empty. Ownership is passed to the caller.
348 base::ListValue
* GetResponseHeadersList(
349 const net::HttpResponseHeaders
* headers
) {
350 base::ListValue
* headers_value
= new base::ListValue();
355 while (headers
->EnumerateHeaderLines(&iter
, &name
, &value
))
356 headers_value
->Append(ToHeaderDictionary(name
, value
));
358 return headers_value
;
361 base::ListValue
* GetRequestHeadersList(const net::HttpRequestHeaders
& headers
) {
362 base::ListValue
* headers_value
= new base::ListValue();
363 for (net::HttpRequestHeaders::Iterator
it(headers
); it
.GetNext(); )
364 headers_value
->Append(ToHeaderDictionary(it
.name(), it
.value()));
365 return headers_value
;
368 // Creates a base::StringValue with the status line of |headers|. If |headers|
369 // is NULL, an empty string is returned. Ownership is passed to the caller.
370 base::StringValue
* GetStatusLine(net::HttpResponseHeaders
* headers
) {
371 return new base::StringValue(
372 headers
? headers
->GetStatusLine() : std::string());
375 void RemoveEventListenerOnUI(
377 const std::string
& event_name
,
379 const std::string
& extension_id
) {
380 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
382 Profile
* profile
= reinterpret_cast<Profile
*>(profile_id
);
383 if (!g_browser_process
->profile_manager()->IsValidProfile(profile
))
386 extensions::EventRouter
* event_router
= extensions::EventRouter::Get(profile
);
390 content::RenderProcessHost
* process
=
391 content::RenderProcessHost::FromID(process_id
);
395 event_router
->RemoveEventListener(event_name
, process
, extension_id
);
398 // Sends an event to subscribers of chrome.declarativeWebRequest.onMessage or
399 // to subscribers of webview.onMessage if the action is being operated upon
400 // a <webview> guest renderer.
401 // |extension_id| identifies the extension that sends and receives the event.
402 // |is_web_view_guest| indicates whether the action is for a <webview>.
403 // |web_view_info| is a struct containing information about the <webview>
405 // |event_argument| is passed to the event listener.
406 void SendOnMessageEventOnUI(
408 const std::string
& extension_id
,
409 bool is_web_view_guest
,
410 const WebViewRendererState::WebViewInfo
& web_view_info
,
411 scoped_ptr
<base::DictionaryValue
> event_argument
) {
412 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
414 Profile
* profile
= reinterpret_cast<Profile
*>(profile_id
);
415 if (!g_browser_process
->profile_manager()->IsValidProfile(profile
))
418 scoped_ptr
<base::ListValue
> event_args(new base::ListValue
);
419 event_args
->Append(event_argument
.release());
421 extensions::EventRouter
* event_router
= extensions::EventRouter::Get(profile
);
423 extensions::EventFilteringInfo event_filtering_info
;
425 std::string event_name
;
426 // The instance ID uniquely identifies a <webview> instance within an embedder
427 // process. We use a filter here so that only event listeners for a particular
428 // <webview> will fire.
429 if (is_web_view_guest
) {
430 event_filtering_info
.SetInstanceID(web_view_info
.instance_id
);
431 event_name
= webview::kEventMessage
;
433 event_name
= declarative_keys::kOnMessage
;
436 scoped_ptr
<extensions::Event
> event(new extensions::Event(
438 event_args
.Pass(), profile
, GURL(),
439 extensions::EventRouter::USER_GESTURE_UNKNOWN
,
440 event_filtering_info
));
441 event_router
->DispatchEventToExtension(extension_id
, event
.Pass());
444 void RemoveEventListenerOnIOThread(
445 content::BrowserContext
* browser_context
,
446 const std::string
& extension_id
,
447 const std::string
& sub_event_name
) {
448 ExtensionWebRequestEventRouter::GetInstance()->RemoveEventListener(
449 browser_context
, extension_id
, sub_event_name
);
454 namespace extensions
{
456 WebRequestAPI::WebRequestAPI(content::BrowserContext
* context
)
457 : browser_context_(context
) {
458 EventRouter
* event_router
= EventRouter::Get(browser_context_
);
459 for (size_t i
= 0; i
< arraysize(kWebRequestEvents
); ++i
) {
460 // Observe the webRequest event.
461 std::string event_name
= kWebRequestEvents
[i
];
462 event_router
->RegisterObserver(this, event_name
);
464 // Also observe the corresponding webview event.
466 0, sizeof(kWebRequestEventPrefix
) - 1, webview::kWebViewEventPrefix
);
467 event_router
->RegisterObserver(this, event_name
);
471 WebRequestAPI::~WebRequestAPI() {
472 EventRouter::Get(browser_context_
)->UnregisterObserver(this);
475 static base::LazyInstance
<BrowserContextKeyedAPIFactory
<WebRequestAPI
> >
476 g_factory
= LAZY_INSTANCE_INITIALIZER
;
479 BrowserContextKeyedAPIFactory
<WebRequestAPI
>*
480 WebRequestAPI::GetFactoryInstance() {
481 return g_factory
.Pointer();
484 void WebRequestAPI::OnListenerRemoved(const EventListenerInfo
& details
) {
485 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
486 // Note that details.event_name includes the sub-event details (e.g. "/123").
487 BrowserThread::PostTask(BrowserThread::IO
,
489 base::Bind(&RemoveEventListenerOnIOThread
,
490 details
.browser_context
,
491 details
.extension_id
,
492 details
.event_name
));
495 } // namespace extensions
497 // Represents a single unique listener to an event, along with whatever filter
498 // parameters and extra_info_spec were specified at the time the listener was
500 // NOTE(benjhayden) New APIs should not use this sub_event_name trick! It does
501 // not play well with event pages. See downloads.onDeterminingFilename and
502 // ExtensionDownloadsEventRouter for an alternative approach.
503 struct ExtensionWebRequestEventRouter::EventListener
{
504 std::string extension_id
;
505 std::string extension_name
;
506 std::string sub_event_name
;
507 RequestFilter filter
;
509 int embedder_process_id
;
510 int webview_instance_id
;
511 base::WeakPtr
<IPC::Sender
> ipc_sender
;
512 mutable std::set
<uint64
> blocked_requests
;
514 // Comparator to work with std::set.
515 bool operator<(const EventListener
& that
) const {
516 if (extension_id
< that
.extension_id
)
518 if (extension_id
== that
.extension_id
&&
519 sub_event_name
< that
.sub_event_name
)
524 EventListener() : extra_info_spec(0) {}
527 // Contains info about requests that are blocked waiting for a response from
529 struct ExtensionWebRequestEventRouter::BlockedRequest
{
530 // The request that is being blocked.
531 net::URLRequest
* request
;
533 // Whether the request originates from an incognito tab.
536 // The event that we're currently blocked on.
539 // The number of event handlers that we are awaiting a response from.
540 int num_handlers_blocking
;
542 // Pointer to NetLog to report significant changes to the request for
544 const net::BoundNetLog
* net_log
;
546 // The callback to call when we get a response from all event handlers.
547 net::CompletionCallback callback
;
549 // If non-empty, this contains the new URL that the request will redirect to.
550 // Only valid for OnBeforeRequest and OnHeadersReceived.
553 // The request headers that will be issued along with this request. Only valid
554 // for OnBeforeSendHeaders.
555 net::HttpRequestHeaders
* request_headers
;
557 // The response headers that were received from the server. Only valid for
558 // OnHeadersReceived.
559 scoped_refptr
<const net::HttpResponseHeaders
> original_response_headers
;
561 // Location where to override response headers. Only valid for
562 // OnHeadersReceived.
563 scoped_refptr
<net::HttpResponseHeaders
>* override_response_headers
;
565 // If non-empty, this contains the auth credentials that may be filled in.
566 // Only valid for OnAuthRequired.
567 net::AuthCredentials
* auth_credentials
;
569 // The callback to invoke for auth. If |auth_callback.is_null()| is false,
570 // |callback| must be NULL.
571 // Only valid for OnAuthRequired.
572 net::NetworkDelegate::AuthCallback auth_callback
;
574 // Time the request was paused. Used for logging purposes.
575 base::Time blocking_time
;
577 // Changes requested by extensions.
578 helpers::EventResponseDeltas response_deltas
;
580 // Provider of meta data about extensions, only used and non-NULL for events
581 // that are delayed until the rules registry is ready.
582 InfoMap
* extension_info_map
;
587 event(kInvalidEvent
),
588 num_handlers_blocking(0),
591 request_headers(NULL
),
592 override_response_headers(NULL
),
593 auth_credentials(NULL
),
594 extension_info_map(NULL
) {}
597 bool ExtensionWebRequestEventRouter::RequestFilter::InitFromValue(
598 const base::DictionaryValue
& value
, std::string
* error
) {
599 if (!value
.HasKey("urls"))
602 for (base::DictionaryValue::Iterator
it(value
); !it
.IsAtEnd(); it
.Advance()) {
603 if (it
.key() == "urls") {
604 const base::ListValue
* urls_value
= NULL
;
605 if (!it
.value().GetAsList(&urls_value
))
607 for (size_t i
= 0; i
< urls_value
->GetSize(); ++i
) {
610 URLPattern::SCHEME_HTTP
| URLPattern::SCHEME_HTTPS
|
611 URLPattern::SCHEME_FTP
| URLPattern::SCHEME_FILE
|
612 URLPattern::SCHEME_EXTENSION
);
613 if (!urls_value
->GetString(i
, &url
) ||
614 pattern
.Parse(url
) != URLPattern::PARSE_SUCCESS
) {
615 *error
= ErrorUtils::FormatErrorMessage(
616 keys::kInvalidRequestFilterUrl
, url
);
619 urls
.AddPattern(pattern
);
621 } else if (it
.key() == "types") {
622 const base::ListValue
* types_value
= NULL
;
623 if (!it
.value().GetAsList(&types_value
))
625 for (size_t i
= 0; i
< types_value
->GetSize(); ++i
) {
626 std::string type_str
;
627 ResourceType::Type type
;
628 if (!types_value
->GetString(i
, &type_str
) ||
629 !helpers::ParseResourceType(type_str
, &type
))
631 types
.push_back(type
);
633 } else if (it
.key() == "tabId") {
634 if (!it
.value().GetAsInteger(&tab_id
))
636 } else if (it
.key() == "windowId") {
637 if (!it
.value().GetAsInteger(&window_id
))
647 bool ExtensionWebRequestEventRouter::ExtraInfoSpec::InitFromValue(
648 const base::ListValue
& value
, int* extra_info_spec
) {
649 *extra_info_spec
= 0;
650 for (size_t i
= 0; i
< value
.GetSize(); ++i
) {
652 if (!value
.GetString(i
, &str
))
655 if (str
== "requestHeaders")
656 *extra_info_spec
|= REQUEST_HEADERS
;
657 else if (str
== "responseHeaders")
658 *extra_info_spec
|= RESPONSE_HEADERS
;
659 else if (str
== "blocking")
660 *extra_info_spec
|= BLOCKING
;
661 else if (str
== "asyncBlocking")
662 *extra_info_spec
|= ASYNC_BLOCKING
;
663 else if (str
== "requestBody")
664 *extra_info_spec
|= REQUEST_BODY
;
668 // BLOCKING and ASYNC_BLOCKING are mutually exclusive.
669 if ((*extra_info_spec
& BLOCKING
) && (*extra_info_spec
& ASYNC_BLOCKING
))
676 ExtensionWebRequestEventRouter::EventResponse::EventResponse(
677 const std::string
& extension_id
, const base::Time
& extension_install_time
)
678 : extension_id(extension_id
),
679 extension_install_time(extension_install_time
),
683 ExtensionWebRequestEventRouter::EventResponse::~EventResponse() {
687 ExtensionWebRequestEventRouter::RequestFilter::RequestFilter()
688 : tab_id(-1), window_id(-1) {
691 ExtensionWebRequestEventRouter::RequestFilter::~RequestFilter() {
695 // ExtensionWebRequestEventRouter
699 ExtensionWebRequestEventRouter
* ExtensionWebRequestEventRouter::GetInstance() {
700 return Singleton
<ExtensionWebRequestEventRouter
>::get();
703 ExtensionWebRequestEventRouter::ExtensionWebRequestEventRouter()
704 : request_time_tracker_(new ExtensionWebRequestTimeTracker
) {
707 ExtensionWebRequestEventRouter::~ExtensionWebRequestEventRouter() {
710 void ExtensionWebRequestEventRouter::RegisterRulesRegistry(
712 const RulesRegistryService::WebViewKey
& webview_key
,
713 scoped_refptr
<extensions::WebRequestRulesRegistry
> rules_registry
) {
714 RulesRegistryKey
key(profile
, webview_key
);
715 if (rules_registry
.get())
716 rules_registries_
[key
] = rules_registry
;
718 rules_registries_
.erase(key
);
721 int ExtensionWebRequestEventRouter::OnBeforeRequest(
723 InfoMap
* extension_info_map
,
724 net::URLRequest
* request
,
725 const net::CompletionCallback
& callback
,
727 // We hide events from the system context as well as sensitive requests.
729 WebRequestPermissions::HideRequest(extension_info_map
, request
))
732 if (IsPageLoad(request
))
735 request_time_tracker_
->LogRequestStartTime(request
->identifier(),
740 // Whether to initialized blocked_requests_.
741 bool initialize_blocked_requests
= false;
743 initialize_blocked_requests
|=
744 ProcessDeclarativeRules(profile
, extension_info_map
,
745 web_request::OnBeforeRequest::kEventName
, request
,
746 extensions::ON_BEFORE_REQUEST
, NULL
);
748 int extra_info_spec
= 0;
749 std::vector
<const EventListener
*> listeners
=
750 GetMatchingListeners(profile
, extension_info_map
,
751 web_request::OnBeforeRequest::kEventName
, request
,
753 if (!listeners
.empty() &&
754 !GetAndSetSignaled(request
->identifier(), kOnBeforeRequest
)) {
755 base::ListValue args
;
756 base::DictionaryValue
* dict
= new base::DictionaryValue();
757 ExtractRequestInfo(request
, dict
);
758 if (extra_info_spec
& ExtraInfoSpec::REQUEST_BODY
)
759 ExtractRequestInfoBody(request
, dict
);
762 initialize_blocked_requests
|=
763 DispatchEvent(profile
, request
, listeners
, args
);
766 if (!initialize_blocked_requests
)
767 return net::OK
; // Nobody saw a reason for modifying the request.
769 blocked_requests_
[request
->identifier()].event
= kOnBeforeRequest
;
770 blocked_requests_
[request
->identifier()].is_incognito
|=
771 IsIncognitoProfile(profile
);
772 blocked_requests_
[request
->identifier()].request
= request
;
773 blocked_requests_
[request
->identifier()].callback
= callback
;
774 blocked_requests_
[request
->identifier()].new_url
= new_url
;
775 blocked_requests_
[request
->identifier()].net_log
= &request
->net_log();
777 if (blocked_requests_
[request
->identifier()].num_handlers_blocking
== 0) {
778 // If there are no blocking handlers, only the declarative rules tried
779 // to modify the request and we can respond synchronously.
780 return ExecuteDeltas(profile
, request
->identifier(),
781 false /* call_callback*/);
783 return net::ERR_IO_PENDING
;
787 int ExtensionWebRequestEventRouter::OnBeforeSendHeaders(
789 InfoMap
* extension_info_map
,
790 net::URLRequest
* request
,
791 const net::CompletionCallback
& callback
,
792 net::HttpRequestHeaders
* headers
) {
793 // We hide events from the system context as well as sensitive requests.
795 WebRequestPermissions::HideRequest(extension_info_map
, request
))
798 bool initialize_blocked_requests
= false;
800 initialize_blocked_requests
|=
801 ProcessDeclarativeRules(profile
, extension_info_map
,
802 keys::kOnBeforeSendHeadersEvent
, request
,
803 extensions::ON_BEFORE_SEND_HEADERS
, NULL
);
805 int extra_info_spec
= 0;
806 std::vector
<const EventListener
*> listeners
=
807 GetMatchingListeners(profile
, extension_info_map
,
808 keys::kOnBeforeSendHeadersEvent
, request
,
810 if (!listeners
.empty() &&
811 !GetAndSetSignaled(request
->identifier(), kOnBeforeSendHeaders
)) {
812 base::ListValue args
;
813 base::DictionaryValue
* dict
= new base::DictionaryValue();
814 ExtractRequestInfo(request
, dict
);
815 if (extra_info_spec
& ExtraInfoSpec::REQUEST_HEADERS
)
816 dict
->Set(keys::kRequestHeadersKey
, GetRequestHeadersList(*headers
));
819 initialize_blocked_requests
|=
820 DispatchEvent(profile
, request
, listeners
, args
);
823 if (!initialize_blocked_requests
)
824 return net::OK
; // Nobody saw a reason for modifying the request.
826 blocked_requests_
[request
->identifier()].event
= kOnBeforeSendHeaders
;
827 blocked_requests_
[request
->identifier()].is_incognito
|=
828 IsIncognitoProfile(profile
);
829 blocked_requests_
[request
->identifier()].request
= request
;
830 blocked_requests_
[request
->identifier()].callback
= callback
;
831 blocked_requests_
[request
->identifier()].request_headers
= headers
;
832 blocked_requests_
[request
->identifier()].net_log
= &request
->net_log();
834 if (blocked_requests_
[request
->identifier()].num_handlers_blocking
== 0) {
835 // If there are no blocking handlers, only the declarative rules tried
836 // to modify the request and we can respond synchronously.
837 return ExecuteDeltas(profile
, request
->identifier(),
838 false /* call_callback*/);
840 return net::ERR_IO_PENDING
;
844 void ExtensionWebRequestEventRouter::OnSendHeaders(
846 InfoMap
* extension_info_map
,
847 net::URLRequest
* request
,
848 const net::HttpRequestHeaders
& headers
) {
849 // We hide events from the system context as well as sensitive requests.
851 WebRequestPermissions::HideRequest(extension_info_map
, request
))
854 if (GetAndSetSignaled(request
->identifier(), kOnSendHeaders
))
857 ClearSignaled(request
->identifier(), kOnBeforeRedirect
);
859 int extra_info_spec
= 0;
860 std::vector
<const EventListener
*> listeners
=
861 GetMatchingListeners(profile
, extension_info_map
,
862 keys::kOnSendHeadersEvent
, request
,
864 if (listeners
.empty())
867 base::ListValue args
;
868 base::DictionaryValue
* dict
= new base::DictionaryValue();
869 ExtractRequestInfo(request
, dict
);
870 if (extra_info_spec
& ExtraInfoSpec::REQUEST_HEADERS
)
871 dict
->Set(keys::kRequestHeadersKey
, GetRequestHeadersList(headers
));
874 DispatchEvent(profile
, request
, listeners
, args
);
877 int ExtensionWebRequestEventRouter::OnHeadersReceived(
879 InfoMap
* extension_info_map
,
880 net::URLRequest
* request
,
881 const net::CompletionCallback
& callback
,
882 const net::HttpResponseHeaders
* original_response_headers
,
883 scoped_refptr
<net::HttpResponseHeaders
>* override_response_headers
,
884 GURL
* allowed_unsafe_redirect_url
) {
885 // We hide events from the system context as well as sensitive requests.
887 WebRequestPermissions::HideRequest(extension_info_map
, request
))
890 bool initialize_blocked_requests
= false;
892 initialize_blocked_requests
|=
893 ProcessDeclarativeRules(profile
, extension_info_map
,
894 keys::kOnHeadersReceivedEvent
, request
,
895 extensions::ON_HEADERS_RECEIVED
,
896 original_response_headers
);
898 int extra_info_spec
= 0;
899 std::vector
<const EventListener
*> listeners
=
900 GetMatchingListeners(profile
, extension_info_map
,
901 keys::kOnHeadersReceivedEvent
, request
,
904 if (!listeners
.empty() &&
905 !GetAndSetSignaled(request
->identifier(), kOnHeadersReceived
)) {
906 base::ListValue args
;
907 base::DictionaryValue
* dict
= new base::DictionaryValue();
908 ExtractRequestInfo(request
, dict
);
909 dict
->SetString(keys::kStatusLineKey
,
910 original_response_headers
->GetStatusLine());
911 if (extra_info_spec
& ExtraInfoSpec::RESPONSE_HEADERS
) {
912 dict
->Set(keys::kResponseHeadersKey
,
913 GetResponseHeadersList(original_response_headers
));
917 initialize_blocked_requests
|=
918 DispatchEvent(profile
, request
, listeners
, args
);
921 if (!initialize_blocked_requests
)
922 return net::OK
; // Nobody saw a reason for modifying the request.
924 blocked_requests_
[request
->identifier()].event
= kOnHeadersReceived
;
925 blocked_requests_
[request
->identifier()].is_incognito
|=
926 IsIncognitoProfile(profile
);
927 blocked_requests_
[request
->identifier()].request
= request
;
928 blocked_requests_
[request
->identifier()].callback
= callback
;
929 blocked_requests_
[request
->identifier()].net_log
= &request
->net_log();
930 blocked_requests_
[request
->identifier()].override_response_headers
=
931 override_response_headers
;
932 blocked_requests_
[request
->identifier()].original_response_headers
=
933 original_response_headers
;
934 blocked_requests_
[request
->identifier()].new_url
=
935 allowed_unsafe_redirect_url
;
937 if (blocked_requests_
[request
->identifier()].num_handlers_blocking
== 0) {
938 // If there are no blocking handlers, only the declarative rules tried
939 // to modify the request and we can respond synchronously.
940 return ExecuteDeltas(profile
, request
->identifier(),
941 false /* call_callback*/);
943 return net::ERR_IO_PENDING
;
947 net::NetworkDelegate::AuthRequiredResponse
948 ExtensionWebRequestEventRouter::OnAuthRequired(
950 InfoMap
* extension_info_map
,
951 net::URLRequest
* request
,
952 const net::AuthChallengeInfo
& auth_info
,
953 const net::NetworkDelegate::AuthCallback
& callback
,
954 net::AuthCredentials
* credentials
) {
955 // No profile means that this is for authentication challenges in the
956 // system context. Skip in that case. Also skip sensitive requests.
958 WebRequestPermissions::HideRequest(extension_info_map
, request
))
959 return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION
;
961 int extra_info_spec
= 0;
962 std::vector
<const EventListener
*> listeners
=
963 GetMatchingListeners(profile
, extension_info_map
,
964 keys::kOnAuthRequiredEvent
, request
,
966 if (listeners
.empty())
967 return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION
;
969 base::ListValue args
;
970 base::DictionaryValue
* dict
= new base::DictionaryValue();
971 ExtractRequestInfo(request
, dict
);
972 dict
->SetBoolean(keys::kIsProxyKey
, auth_info
.is_proxy
);
973 if (!auth_info
.scheme
.empty())
974 dict
->SetString(keys::kSchemeKey
, auth_info
.scheme
);
975 if (!auth_info
.realm
.empty())
976 dict
->SetString(keys::kRealmKey
, auth_info
.realm
);
977 base::DictionaryValue
* challenger
= new base::DictionaryValue();
978 challenger
->SetString(keys::kHostKey
, auth_info
.challenger
.host());
979 challenger
->SetInteger(keys::kPortKey
, auth_info
.challenger
.port());
980 dict
->Set(keys::kChallengerKey
, challenger
);
981 dict
->Set(keys::kStatusLineKey
, GetStatusLine(request
->response_headers()));
982 if (extra_info_spec
& ExtraInfoSpec::RESPONSE_HEADERS
) {
983 dict
->Set(keys::kResponseHeadersKey
,
984 GetResponseHeadersList(request
->response_headers()));
988 if (DispatchEvent(profile
, request
, listeners
, args
)) {
989 blocked_requests_
[request
->identifier()].event
= kOnAuthRequired
;
990 blocked_requests_
[request
->identifier()].is_incognito
|=
991 IsIncognitoProfile(profile
);
992 blocked_requests_
[request
->identifier()].request
= request
;
993 blocked_requests_
[request
->identifier()].auth_callback
= callback
;
994 blocked_requests_
[request
->identifier()].auth_credentials
= credentials
;
995 blocked_requests_
[request
->identifier()].net_log
= &request
->net_log();
996 return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_IO_PENDING
;
998 return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION
;
1001 void ExtensionWebRequestEventRouter::OnBeforeRedirect(
1003 InfoMap
* extension_info_map
,
1004 net::URLRequest
* request
,
1005 const GURL
& new_location
) {
1006 // We hide events from the system context as well as sensitive requests.
1008 WebRequestPermissions::HideRequest(extension_info_map
, request
))
1011 if (GetAndSetSignaled(request
->identifier(), kOnBeforeRedirect
))
1014 ClearSignaled(request
->identifier(), kOnBeforeRequest
);
1015 ClearSignaled(request
->identifier(), kOnBeforeSendHeaders
);
1016 ClearSignaled(request
->identifier(), kOnSendHeaders
);
1017 ClearSignaled(request
->identifier(), kOnHeadersReceived
);
1019 int extra_info_spec
= 0;
1020 std::vector
<const EventListener
*> listeners
=
1021 GetMatchingListeners(profile
, extension_info_map
,
1022 keys::kOnBeforeRedirectEvent
, request
,
1024 if (listeners
.empty())
1027 int http_status_code
= request
->GetResponseCode();
1029 std::string response_ip
= request
->GetSocketAddress().host();
1031 base::ListValue args
;
1032 base::DictionaryValue
* dict
= new base::DictionaryValue();
1033 ExtractRequestInfo(request
, dict
);
1034 dict
->SetString(keys::kRedirectUrlKey
, new_location
.spec());
1035 dict
->SetInteger(keys::kStatusCodeKey
, http_status_code
);
1036 if (!response_ip
.empty())
1037 dict
->SetString(keys::kIpKey
, response_ip
);
1038 dict
->SetBoolean(keys::kFromCache
, request
->was_cached());
1039 dict
->Set(keys::kStatusLineKey
, GetStatusLine(request
->response_headers()));
1040 if (extra_info_spec
& ExtraInfoSpec::RESPONSE_HEADERS
) {
1041 dict
->Set(keys::kResponseHeadersKey
,
1042 GetResponseHeadersList(request
->response_headers()));
1046 DispatchEvent(profile
, request
, listeners
, args
);
1049 void ExtensionWebRequestEventRouter::OnResponseStarted(
1051 InfoMap
* extension_info_map
,
1052 net::URLRequest
* request
) {
1053 // We hide events from the system context as well as sensitive requests.
1055 WebRequestPermissions::HideRequest(extension_info_map
, request
))
1058 // OnResponseStarted is even triggered, when the request was cancelled.
1059 if (request
->status().status() != net::URLRequestStatus::SUCCESS
)
1062 int extra_info_spec
= 0;
1063 std::vector
<const EventListener
*> listeners
=
1064 GetMatchingListeners(profile
, extension_info_map
,
1065 keys::kOnResponseStartedEvent
, request
,
1067 if (listeners
.empty())
1070 // UrlRequestFileJobs do not send headers, so we simulate their behavior.
1071 int response_code
= 200;
1072 if (request
->response_headers())
1073 response_code
= request
->response_headers()->response_code();
1075 std::string response_ip
= request
->GetSocketAddress().host();
1077 base::ListValue args
;
1078 base::DictionaryValue
* dict
= new base::DictionaryValue();
1079 ExtractRequestInfo(request
, dict
);
1080 if (!response_ip
.empty())
1081 dict
->SetString(keys::kIpKey
, response_ip
);
1082 dict
->SetBoolean(keys::kFromCache
, request
->was_cached());
1083 dict
->SetInteger(keys::kStatusCodeKey
, response_code
);
1084 dict
->Set(keys::kStatusLineKey
, GetStatusLine(request
->response_headers()));
1085 if (extra_info_spec
& ExtraInfoSpec::RESPONSE_HEADERS
) {
1086 dict
->Set(keys::kResponseHeadersKey
,
1087 GetResponseHeadersList(request
->response_headers()));
1091 DispatchEvent(profile
, request
, listeners
, args
);
1094 void ExtensionWebRequestEventRouter::OnCompleted(void* profile
,
1095 InfoMap
* extension_info_map
,
1096 net::URLRequest
* request
) {
1097 // We hide events from the system context as well as sensitive requests.
1098 // However, if the request first became sensitive after redirecting we have
1099 // already signaled it and thus we have to signal the end of it. This is
1100 // risk-free because the handler cannot modify the request now.
1102 (WebRequestPermissions::HideRequest(extension_info_map
, request
) &&
1103 !WasSignaled(*request
)))
1106 request_time_tracker_
->LogRequestEndTime(request
->identifier(),
1109 DCHECK(request
->status().status() == net::URLRequestStatus::SUCCESS
);
1111 DCHECK(!GetAndSetSignaled(request
->identifier(), kOnCompleted
));
1113 ClearPendingCallbacks(request
);
1115 int extra_info_spec
= 0;
1116 std::vector
<const EventListener
*> listeners
=
1117 GetMatchingListeners(profile
, extension_info_map
,
1118 keys::kOnCompletedEvent
, request
, &extra_info_spec
);
1119 if (listeners
.empty())
1122 // UrlRequestFileJobs do not send headers, so we simulate their behavior.
1123 int response_code
= 200;
1124 if (request
->response_headers())
1125 response_code
= request
->response_headers()->response_code();
1127 std::string response_ip
= request
->GetSocketAddress().host();
1129 base::ListValue args
;
1130 base::DictionaryValue
* dict
= new base::DictionaryValue();
1131 ExtractRequestInfo(request
, dict
);
1132 dict
->SetInteger(keys::kStatusCodeKey
, response_code
);
1133 if (!response_ip
.empty())
1134 dict
->SetString(keys::kIpKey
, response_ip
);
1135 dict
->SetBoolean(keys::kFromCache
, request
->was_cached());
1136 dict
->Set(keys::kStatusLineKey
, GetStatusLine(request
->response_headers()));
1137 if (extra_info_spec
& ExtraInfoSpec::RESPONSE_HEADERS
) {
1138 dict
->Set(keys::kResponseHeadersKey
,
1139 GetResponseHeadersList(request
->response_headers()));
1143 DispatchEvent(profile
, request
, listeners
, args
);
1146 void ExtensionWebRequestEventRouter::OnErrorOccurred(
1148 InfoMap
* extension_info_map
,
1149 net::URLRequest
* request
,
1151 // We hide events from the system context as well as sensitive requests.
1152 // However, if the request first became sensitive after redirecting we have
1153 // already signaled it and thus we have to signal the end of it. This is
1154 // risk-free because the handler cannot modify the request now.
1156 (WebRequestPermissions::HideRequest(extension_info_map
, request
) &&
1157 !WasSignaled(*request
)))
1160 request_time_tracker_
->LogRequestEndTime(request
->identifier(),
1163 DCHECK(request
->status().status() == net::URLRequestStatus::FAILED
||
1164 request
->status().status() == net::URLRequestStatus::CANCELED
);
1166 DCHECK(!GetAndSetSignaled(request
->identifier(), kOnErrorOccurred
));
1168 ClearPendingCallbacks(request
);
1170 int extra_info_spec
= 0;
1171 std::vector
<const EventListener
*> listeners
=
1172 GetMatchingListeners(profile
, extension_info_map
,
1173 web_request::OnErrorOccurred::kEventName
, request
,
1175 if (listeners
.empty())
1178 base::ListValue args
;
1179 base::DictionaryValue
* dict
= new base::DictionaryValue();
1180 ExtractRequestInfo(request
, dict
);
1182 std::string response_ip
= request
->GetSocketAddress().host();
1183 if (!response_ip
.empty())
1184 dict
->SetString(keys::kIpKey
, response_ip
);
1186 dict
->SetBoolean(keys::kFromCache
, request
->was_cached());
1187 dict
->SetString(keys::kErrorKey
,
1188 net::ErrorToString(request
->status().error()));
1191 DispatchEvent(profile
, request
, listeners
, args
);
1194 void ExtensionWebRequestEventRouter::OnURLRequestDestroyed(
1195 void* profile
, net::URLRequest
* request
) {
1196 ClearPendingCallbacks(request
);
1198 signaled_requests_
.erase(request
->identifier());
1200 request_time_tracker_
->LogRequestEndTime(request
->identifier(),
1204 void ExtensionWebRequestEventRouter::ClearPendingCallbacks(
1205 net::URLRequest
* request
) {
1206 blocked_requests_
.erase(request
->identifier());
1209 bool ExtensionWebRequestEventRouter::DispatchEvent(
1211 net::URLRequest
* request
,
1212 const std::vector
<const EventListener
*>& listeners
,
1213 const base::ListValue
& args
) {
1214 // TODO(mpcomplete): Consider consolidating common (extension_id,json_args)
1215 // pairs into a single message sent to a list of sub_event_names.
1216 int num_handlers_blocking
= 0;
1217 for (std::vector
<const EventListener
*>::const_iterator it
= listeners
.begin();
1218 it
!= listeners
.end(); ++it
) {
1219 // Filter out the optional keys that this listener didn't request.
1220 scoped_ptr
<base::ListValue
> args_filtered(args
.DeepCopy());
1221 base::DictionaryValue
* dict
= NULL
;
1222 CHECK(args_filtered
->GetDictionary(0, &dict
) && dict
);
1223 if (!((*it
)->extra_info_spec
& ExtraInfoSpec::REQUEST_HEADERS
))
1224 dict
->Remove(keys::kRequestHeadersKey
, NULL
);
1225 if (!((*it
)->extra_info_spec
& ExtraInfoSpec::RESPONSE_HEADERS
))
1226 dict
->Remove(keys::kResponseHeadersKey
, NULL
);
1228 extensions::EventRouter::DispatchEvent(
1229 (*it
)->ipc_sender
.get(), profile_id
,
1230 (*it
)->extension_id
, (*it
)->sub_event_name
,
1231 args_filtered
.Pass(),
1232 extensions::EventRouter::USER_GESTURE_UNKNOWN
,
1233 extensions::EventFilteringInfo());
1234 if ((*it
)->extra_info_spec
&
1235 (ExtraInfoSpec::BLOCKING
| ExtraInfoSpec::ASYNC_BLOCKING
)) {
1236 (*it
)->blocked_requests
.insert(request
->identifier());
1237 // If this is the first delegate blocking the request, go ahead and log
1239 if (num_handlers_blocking
== 0) {
1240 std::string delegate_info
=
1241 l10n_util::GetStringFUTF8(IDS_LOAD_STATE_PARAMETER_EXTENSION
,
1242 base::UTF8ToUTF16((*it
)->extension_name
));
1243 // LobAndReport allows extensions that block requests to be displayed in
1244 // the load status bar.
1245 request
->LogAndReportBlockedBy(delegate_info
.c_str());
1247 ++num_handlers_blocking
;
1251 if (num_handlers_blocking
> 0) {
1252 blocked_requests_
[request
->identifier()].request
= request
;
1253 blocked_requests_
[request
->identifier()].is_incognito
|=
1254 IsIncognitoProfile(profile_id
);
1255 blocked_requests_
[request
->identifier()].num_handlers_blocking
+=
1256 num_handlers_blocking
;
1257 blocked_requests_
[request
->identifier()].blocking_time
= base::Time::Now();
1265 void ExtensionWebRequestEventRouter::OnEventHandled(
1267 const std::string
& extension_id
,
1268 const std::string
& event_name
,
1269 const std::string
& sub_event_name
,
1271 EventResponse
* response
) {
1272 EventListener listener
;
1273 listener
.extension_id
= extension_id
;
1274 listener
.sub_event_name
= sub_event_name
;
1276 // The listener may have been removed (e.g. due to the process going away)
1277 // before we got here.
1278 std::set
<EventListener
>::iterator found
=
1279 listeners_
[profile
][event_name
].find(listener
);
1280 if (found
!= listeners_
[profile
][event_name
].end())
1281 found
->blocked_requests
.erase(request_id
);
1283 DecrementBlockCount(profile
, extension_id
, event_name
, request_id
, response
);
1286 bool ExtensionWebRequestEventRouter::AddEventListener(
1288 const std::string
& extension_id
,
1289 const std::string
& extension_name
,
1290 const std::string
& event_name
,
1291 const std::string
& sub_event_name
,
1292 const RequestFilter
& filter
,
1293 int extra_info_spec
,
1294 int embedder_process_id
,
1295 int webview_instance_id
,
1296 base::WeakPtr
<IPC::Sender
> ipc_sender
) {
1297 if (!IsWebRequestEvent(event_name
))
1300 EventListener listener
;
1301 listener
.extension_id
= extension_id
;
1302 listener
.extension_name
= extension_name
;
1303 listener
.sub_event_name
= sub_event_name
;
1304 listener
.filter
= filter
;
1305 listener
.extra_info_spec
= extra_info_spec
;
1306 listener
.ipc_sender
= ipc_sender
;
1307 listener
.embedder_process_id
= embedder_process_id
;
1308 listener
.webview_instance_id
= webview_instance_id
;
1309 if (listener
.webview_instance_id
) {
1310 content::RecordAction(
1311 base::UserMetricsAction("WebView.WebRequest.AddListener"));
1314 if (listeners_
[profile
][event_name
].count(listener
) != 0u) {
1315 // This is likely an abuse of the API by a malicious extension.
1318 listeners_
[profile
][event_name
].insert(listener
);
1322 void ExtensionWebRequestEventRouter::RemoveEventListener(
1324 const std::string
& extension_id
,
1325 const std::string
& sub_event_name
) {
1326 std::string event_name
=
1327 extensions::EventRouter::GetBaseEventName(sub_event_name
);
1328 DCHECK(IsWebRequestEvent(event_name
));
1330 EventListener listener
;
1331 listener
.extension_id
= extension_id
;
1332 listener
.sub_event_name
= sub_event_name
;
1334 // It's possible for AddEventListener to fail asynchronously. In that case,
1335 // the renderer believes the listener exists, while the browser does not.
1336 // Ignore a RemoveEventListener in that case.
1337 std::set
<EventListener
>::iterator found
=
1338 listeners_
[profile
][event_name
].find(listener
);
1339 if (found
== listeners_
[profile
][event_name
].end())
1342 CHECK_EQ(listeners_
[profile
][event_name
].count(listener
), 1u) <<
1343 "extension=" << extension_id
<< " event=" << event_name
;
1345 // Unblock any request that this event listener may have been blocking.
1346 for (std::set
<uint64
>::iterator it
= found
->blocked_requests
.begin();
1347 it
!= found
->blocked_requests
.end(); ++it
) {
1348 DecrementBlockCount(profile
, extension_id
, event_name
, *it
, NULL
);
1351 listeners_
[profile
][event_name
].erase(listener
);
1353 helpers::ClearCacheOnNavigation();
1356 void ExtensionWebRequestEventRouter::RemoveWebViewEventListeners(
1358 const std::string
& extension_id
,
1359 int embedder_process_id
,
1360 int webview_instance_id
) {
1361 // Iterate over all listeners of all WebRequest events to delete
1362 // any listeners that belong to the provided <webview>.
1363 ListenerMapForProfile
& map_for_profile
= listeners_
[profile
];
1364 for (ListenerMapForProfile::iterator event_iter
= map_for_profile
.begin();
1365 event_iter
!= map_for_profile
.end(); ++event_iter
) {
1366 std::vector
<EventListener
> listeners_to_delete
;
1367 std::set
<EventListener
>& listeners
= event_iter
->second
;
1368 for (std::set
<EventListener
>::iterator listener_iter
= listeners
.begin();
1369 listener_iter
!= listeners
.end(); ++listener_iter
) {
1370 const EventListener
& listener
= *listener_iter
;
1371 if (listener
.embedder_process_id
== embedder_process_id
&&
1372 listener
.webview_instance_id
== webview_instance_id
)
1373 listeners_to_delete
.push_back(listener
);
1375 for (size_t i
= 0; i
< listeners_to_delete
.size(); ++i
) {
1376 EventListener
& listener
= listeners_to_delete
[i
];
1377 content::BrowserThread::PostTask(
1378 content::BrowserThread::UI
,
1380 base::Bind(&RemoveEventListenerOnUI
,
1382 listener
.sub_event_name
,
1383 embedder_process_id
,
1389 void ExtensionWebRequestEventRouter::OnOTRProfileCreated(
1390 void* original_profile
, void* otr_profile
) {
1391 cross_profile_map_
[original_profile
] = std::make_pair(false, otr_profile
);
1392 cross_profile_map_
[otr_profile
] = std::make_pair(true, original_profile
);
1395 void ExtensionWebRequestEventRouter::OnOTRProfileDestroyed(
1396 void* original_profile
, void* otr_profile
) {
1397 cross_profile_map_
.erase(otr_profile
);
1398 cross_profile_map_
.erase(original_profile
);
1401 void ExtensionWebRequestEventRouter::AddCallbackForPageLoad(
1402 const base::Closure
& callback
) {
1403 callbacks_for_page_load_
.push_back(callback
);
1406 bool ExtensionWebRequestEventRouter::IsPageLoad(
1407 net::URLRequest
* request
) const {
1408 bool is_main_frame
= false;
1409 int64 frame_id
= -1;
1410 bool parent_is_main_frame
= false;
1411 int64 parent_frame_id
= -1;
1414 int render_process_host_id
= -1;
1415 int routing_id
= -1;
1416 ResourceType::Type resource_type
= ResourceType::LAST_TYPE
;
1418 ExtractRequestInfoDetails(request
, &is_main_frame
, &frame_id
,
1419 &parent_is_main_frame
, &parent_frame_id
,
1420 &tab_id
, &window_id
, &render_process_host_id
,
1421 &routing_id
, &resource_type
);
1423 return resource_type
== ResourceType::MAIN_FRAME
;
1426 void ExtensionWebRequestEventRouter::NotifyPageLoad() {
1427 for (CallbacksForPageLoad::const_iterator i
=
1428 callbacks_for_page_load_
.begin();
1429 i
!= callbacks_for_page_load_
.end(); ++i
) {
1432 callbacks_for_page_load_
.clear();
1435 void* ExtensionWebRequestEventRouter::GetCrossProfile(void* profile
) const {
1436 CrossProfileMap::const_iterator cross_profile
=
1437 cross_profile_map_
.find(profile
);
1438 if (cross_profile
== cross_profile_map_
.end())
1440 return cross_profile
->second
.second
;
1443 bool ExtensionWebRequestEventRouter::IsIncognitoProfile(void* profile
) const {
1444 CrossProfileMap::const_iterator cross_profile
=
1445 cross_profile_map_
.find(profile
);
1446 if (cross_profile
== cross_profile_map_
.end())
1448 return cross_profile
->second
.first
;
1451 bool ExtensionWebRequestEventRouter::WasSignaled(
1452 const net::URLRequest
& request
) const {
1453 SignaledRequestMap::const_iterator flag
=
1454 signaled_requests_
.find(request
.identifier());
1455 return (flag
!= signaled_requests_
.end()) && (flag
->second
!= 0);
1458 void ExtensionWebRequestEventRouter::GetMatchingListenersImpl(
1460 InfoMap
* extension_info_map
,
1461 bool crosses_incognito
,
1462 const std::string
& event_name
,
1466 int render_process_host_id
,
1468 ResourceType::Type resource_type
,
1469 bool is_async_request
,
1470 bool is_request_from_extension
,
1471 int* extra_info_spec
,
1472 std::vector
<const ExtensionWebRequestEventRouter::EventListener
*>*
1473 matching_listeners
) {
1474 std::string
web_request_event_name(event_name
);
1475 WebViewRendererState::WebViewInfo web_view_info
;
1476 bool is_web_view_guest
= WebViewRendererState::GetInstance()->
1477 GetInfo(render_process_host_id
, routing_id
, &web_view_info
);
1478 if (is_web_view_guest
) {
1479 web_request_event_name
.replace(
1480 0, sizeof(kWebRequestEventPrefix
) - 1, webview::kWebViewEventPrefix
);
1483 std::set
<EventListener
>& listeners
=
1484 listeners_
[profile
][web_request_event_name
];
1485 for (std::set
<EventListener
>::iterator it
= listeners
.begin();
1486 it
!= listeners
.end(); ++it
) {
1487 if (!it
->ipc_sender
.get()) {
1488 // The IPC sender has been deleted. This listener will be removed soon
1489 // via a call to RemoveEventListener. For now, just skip it.
1493 if (is_web_view_guest
&&
1494 (it
->embedder_process_id
!= web_view_info
.embedder_process_id
||
1495 it
->webview_instance_id
!= web_view_info
.instance_id
))
1498 if (!it
->filter
.urls
.is_empty() && !it
->filter
.urls
.MatchesURL(url
))
1500 if (it
->filter
.tab_id
!= -1 && tab_id
!= it
->filter
.tab_id
)
1502 if (it
->filter
.window_id
!= -1 && window_id
!= it
->filter
.window_id
)
1504 if (!it
->filter
.types
.empty() &&
1505 std::find(it
->filter
.types
.begin(), it
->filter
.types
.end(),
1506 resource_type
) == it
->filter
.types
.end())
1509 if (!is_web_view_guest
&& !WebRequestPermissions::CanExtensionAccessURL(
1510 extension_info_map
, it
->extension_id
, url
, crosses_incognito
,
1511 WebRequestPermissions::REQUIRE_HOST_PERMISSION
))
1514 bool blocking_listener
=
1515 (it
->extra_info_spec
&
1516 (ExtraInfoSpec::BLOCKING
| ExtraInfoSpec::ASYNC_BLOCKING
)) != 0;
1518 // We do not want to notify extensions about XHR requests that are
1519 // triggered by themselves. This is a workaround to prevent deadlocks
1520 // in case of synchronous XHR requests that block the extension renderer
1521 // and therefore prevent the extension from processing the request
1522 // handler. This is only a problem for blocking listeners.
1523 // http://crbug.com/105656
1524 bool synchronous_xhr_from_extension
= !is_async_request
&&
1525 is_request_from_extension
&& resource_type
== ResourceType::XHR
;
1527 // Only send webRequest events for URLs the extension has access to.
1528 if (blocking_listener
&& synchronous_xhr_from_extension
)
1531 matching_listeners
->push_back(&(*it
));
1532 *extra_info_spec
|= it
->extra_info_spec
;
1536 std::vector
<const ExtensionWebRequestEventRouter::EventListener
*>
1537 ExtensionWebRequestEventRouter::GetMatchingListeners(
1539 InfoMap
* extension_info_map
,
1540 const std::string
& event_name
,
1541 net::URLRequest
* request
,
1542 int* extra_info_spec
) {
1543 // TODO(mpcomplete): handle profile == NULL (should collect all listeners).
1544 *extra_info_spec
= 0;
1546 bool is_main_frame
= false;
1547 int64 frame_id
= -1;
1548 bool parent_is_main_frame
= false;
1549 int64 parent_frame_id
= -1;
1552 int render_process_host_id
= -1;
1553 int routing_id
= -1;
1554 ResourceType::Type resource_type
= ResourceType::LAST_TYPE
;
1555 const GURL
& url
= request
->url();
1557 ExtractRequestInfoDetails(request
, &is_main_frame
, &frame_id
,
1558 &parent_is_main_frame
, &parent_frame_id
,
1559 &tab_id
, &window_id
, &render_process_host_id
,
1560 &routing_id
, &resource_type
);
1562 std::vector
<const ExtensionWebRequestEventRouter::EventListener
*>
1565 bool is_request_from_extension
=
1566 IsRequestFromExtension(request
, extension_info_map
);
1568 const ResourceRequestInfo
* info
= ResourceRequestInfo::ForRequest(request
);
1569 // We are conservative here and assume requests are asynchronous in case
1570 // we don't have an info object. We don't want to risk a deadlock.
1571 bool is_async_request
= !info
|| info
->IsAsync();
1573 GetMatchingListenersImpl(
1574 profile
, extension_info_map
, false, event_name
, url
,
1575 tab_id
, window_id
, render_process_host_id
, routing_id
, resource_type
,
1576 is_async_request
, is_request_from_extension
, extra_info_spec
,
1577 &matching_listeners
);
1578 void* cross_profile
= GetCrossProfile(profile
);
1579 if (cross_profile
) {
1580 GetMatchingListenersImpl(
1581 cross_profile
, extension_info_map
, true, event_name
, url
, tab_id
,
1582 window_id
, render_process_host_id
, routing_id
, resource_type
,
1583 is_async_request
, is_request_from_extension
, extra_info_spec
,
1584 &matching_listeners
);
1587 return matching_listeners
;
1592 helpers::EventResponseDelta
* CalculateDelta(
1593 ExtensionWebRequestEventRouter::BlockedRequest
* blocked_request
,
1594 ExtensionWebRequestEventRouter::EventResponse
* response
) {
1595 switch (blocked_request
->event
) {
1596 case ExtensionWebRequestEventRouter::kOnBeforeRequest
:
1597 return helpers::CalculateOnBeforeRequestDelta(
1598 response
->extension_id
, response
->extension_install_time
,
1599 response
->cancel
, response
->new_url
);
1600 case ExtensionWebRequestEventRouter::kOnBeforeSendHeaders
: {
1601 net::HttpRequestHeaders
* old_headers
= blocked_request
->request_headers
;
1602 net::HttpRequestHeaders
* new_headers
= response
->request_headers
.get();
1603 return helpers::CalculateOnBeforeSendHeadersDelta(
1604 response
->extension_id
, response
->extension_install_time
,
1605 response
->cancel
, old_headers
, new_headers
);
1607 case ExtensionWebRequestEventRouter::kOnHeadersReceived
: {
1608 const net::HttpResponseHeaders
* old_headers
=
1609 blocked_request
->original_response_headers
.get();
1610 helpers::ResponseHeaders
* new_headers
=
1611 response
->response_headers
.get();
1612 return helpers::CalculateOnHeadersReceivedDelta(
1613 response
->extension_id
,
1614 response
->extension_install_time
,
1620 case ExtensionWebRequestEventRouter::kOnAuthRequired
:
1621 return helpers::CalculateOnAuthRequiredDelta(
1622 response
->extension_id
, response
->extension_install_time
,
1623 response
->cancel
, &response
->auth_credentials
);
1631 base::Value
* SerializeResponseHeaders(const helpers::ResponseHeaders
& headers
) {
1632 scoped_ptr
<base::ListValue
> serialized_headers(new base::ListValue());
1633 for (helpers::ResponseHeaders::const_iterator i
= headers
.begin();
1634 i
!= headers
.end(); ++i
) {
1635 serialized_headers
->Append(ToHeaderDictionary(i
->first
, i
->second
));
1637 return serialized_headers
.release();
1640 // Convert a RequestCookieModifications/ResponseCookieModifications object to a
1641 // base::ListValue which summarizes the changes made. This is templated since
1642 // the two types (request/response) are different but contain essentially the
1644 template<typename CookieType
>
1645 base::ListValue
* SummarizeCookieModifications(
1646 const std::vector
<linked_ptr
<CookieType
> >& modifications
) {
1647 scoped_ptr
<base::ListValue
> cookie_modifications(new base::ListValue());
1648 for (typename
std::vector
<linked_ptr
<CookieType
> >::const_iterator i
=
1649 modifications
.begin();
1650 i
!= modifications
.end(); ++i
) {
1651 scoped_ptr
<base::DictionaryValue
> summary(new base::DictionaryValue());
1652 const CookieType
& mod
= *i
->get();
1655 summary
->SetString(activitylog::kCookieModificationTypeKey
,
1656 activitylog::kCookieModificationAdd
);
1659 summary
->SetString(activitylog::kCookieModificationTypeKey
,
1660 activitylog::kCookieModificationEdit
);
1662 case helpers::REMOVE
:
1663 summary
->SetString(activitylog::kCookieModificationTypeKey
,
1664 activitylog::kCookieModificationRemove
);
1668 if (mod
.filter
->name
)
1669 summary
->SetString(activitylog::kCookieFilterNameKey
,
1670 *mod
.modification
->name
);
1671 if (mod
.filter
->domain
)
1672 summary
->SetString(activitylog::kCookieFilterDomainKey
,
1673 *mod
.modification
->name
);
1675 if (mod
.modification
) {
1676 if (mod
.modification
->name
)
1677 summary
->SetString(activitylog::kCookieModDomainKey
,
1678 *mod
.modification
->name
);
1679 if (mod
.modification
->domain
)
1680 summary
->SetString(activitylog::kCookieModDomainKey
,
1681 *mod
.modification
->name
);
1683 cookie_modifications
->Append(summary
.release());
1685 return cookie_modifications
.release();
1688 // Converts an EventResponseDelta object to a dictionary value suitable for the
1690 scoped_ptr
<base::DictionaryValue
> SummarizeResponseDelta(
1691 const std::string
& event_name
,
1692 const helpers::EventResponseDelta
& delta
) {
1693 scoped_ptr
<base::DictionaryValue
> details(new base::DictionaryValue());
1695 details
->SetBoolean(activitylog::kCancelKey
, true);
1697 if (!delta
.new_url
.is_empty()) {
1698 details
->SetString(activitylog::kNewUrlKey
, delta
.new_url
.spec());
1701 scoped_ptr
<base::ListValue
> modified_headers(new base::ListValue());
1702 net::HttpRequestHeaders::Iterator
iter(delta
.modified_request_headers
);
1703 while (iter
.GetNext()) {
1704 modified_headers
->Append(ToHeaderDictionary(iter
.name(), iter
.value()));
1706 if (!modified_headers
->empty()) {
1707 details
->Set(activitylog::kModifiedRequestHeadersKey
,
1708 modified_headers
.release());
1711 scoped_ptr
<base::ListValue
> deleted_headers(new base::ListValue());
1712 deleted_headers
->AppendStrings(delta
.deleted_request_headers
);
1713 if (!deleted_headers
->empty()) {
1714 details
->Set(activitylog::kDeletedRequestHeadersKey
,
1715 deleted_headers
.release());
1718 if (!delta
.added_response_headers
.empty()) {
1719 details
->Set(activitylog::kAddedRequestHeadersKey
,
1720 SerializeResponseHeaders(delta
.added_response_headers
));
1722 if (!delta
.deleted_response_headers
.empty()) {
1723 details
->Set(activitylog::kDeletedResponseHeadersKey
,
1724 SerializeResponseHeaders(delta
.deleted_response_headers
));
1726 if (delta
.auth_credentials
) {
1727 details
->SetString(activitylog::kAuthCredentialsKey
,
1729 delta
.auth_credentials
->username()) + ":*");
1732 if (!delta
.response_cookie_modifications
.empty()) {
1734 activitylog::kResponseCookieModificationsKey
,
1735 SummarizeCookieModifications(delta
.response_cookie_modifications
));
1738 return details
.Pass();
1741 void LogExtensionActivity(void* profile_id
,
1743 const std::string
& extension_id
,
1745 const std::string
& api_call
,
1746 scoped_ptr
<base::DictionaryValue
> details
) {
1747 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
1748 BrowserThread::PostTask(BrowserThread::UI
,
1750 base::Bind(&LogExtensionActivity
,
1756 base::Passed(&details
)));
1758 Profile
* profile
= static_cast<Profile
*>(profile_id
);
1759 if (!g_browser_process
->profile_manager()->IsValidProfile(profile
))
1761 scoped_refptr
<extensions::Action
> action
=
1762 new extensions::Action(extension_id
,
1764 extensions::Action::ACTION_WEB_REQUEST
,
1766 action
->set_page_url(url
);
1767 action
->set_page_incognito(is_incognito
);
1768 action
->mutable_other()->Set(activity_log_constants::kActionWebRequest
,
1770 extensions::ActivityLog::GetInstance(profile
)->LogAction(action
);
1776 void ExtensionWebRequestEventRouter::DecrementBlockCount(
1778 const std::string
& extension_id
,
1779 const std::string
& event_name
,
1781 EventResponse
* response
) {
1782 scoped_ptr
<EventResponse
> response_scoped(response
);
1784 // It's possible that this request was deleted, or cancelled by a previous
1785 // event handler. If so, ignore this response.
1786 if (blocked_requests_
.find(request_id
) == blocked_requests_
.end())
1789 BlockedRequest
& blocked_request
= blocked_requests_
[request_id
];
1790 int num_handlers_blocking
= --blocked_request
.num_handlers_blocking
;
1791 CHECK_GE(num_handlers_blocking
, 0);
1794 helpers::EventResponseDelta
* delta
=
1795 CalculateDelta(&blocked_request
, response
);
1797 LogExtensionActivity(profile
,
1798 blocked_request
.is_incognito
,
1800 blocked_request
.request
->url(),
1802 SummarizeResponseDelta(event_name
, *delta
));
1804 blocked_request
.response_deltas
.push_back(
1805 linked_ptr
<helpers::EventResponseDelta
>(delta
));
1808 base::TimeDelta block_time
=
1809 base::Time::Now() - blocked_request
.blocking_time
;
1810 if (!extension_id
.empty()) {
1811 request_time_tracker_
->IncrementExtensionBlockTime(
1812 extension_id
, request_id
, block_time
);
1814 // |extension_id| is empty for requests blocked on startup waiting for the
1815 // declarative rules to be read from disk.
1816 UMA_HISTOGRAM_TIMES("Extensions.NetworkDelayStartup", block_time
);
1819 if (num_handlers_blocking
== 0) {
1820 blocked_request
.request
->LogUnblocked();
1821 ExecuteDeltas(profile
, request_id
, true);
1823 // Update the URLRequest to make sure it's tagged with an extension that's
1824 // still blocking it. This may end up being the same extension as before.
1825 std::set
<EventListener
>& listeners
= listeners_
[profile
][event_name
];
1827 for (std::set
<EventListener
>::iterator it
= listeners
.begin();
1828 it
!= listeners
.end(); ++it
) {
1829 if (it
->blocked_requests
.count(request_id
) == 0)
1831 std::string delegate_info
=
1832 l10n_util::GetStringFUTF8(IDS_LOAD_STATE_PARAMETER_EXTENSION
,
1833 base::UTF8ToUTF16(it
->extension_name
));
1834 blocked_request
.request
->LogAndReportBlockedBy(delegate_info
.c_str());
1840 void ExtensionWebRequestEventRouter::SendMessages(
1842 const BlockedRequest
& blocked_request
) {
1843 const helpers::EventResponseDeltas
& deltas
= blocked_request
.response_deltas
;
1844 for (helpers::EventResponseDeltas::const_iterator delta
= deltas
.begin();
1845 delta
!= deltas
.end(); ++delta
) {
1846 const std::set
<std::string
>& messages
= (*delta
)->messages_to_extension
;
1847 for (std::set
<std::string
>::const_iterator message
= messages
.begin();
1848 message
!= messages
.end(); ++message
) {
1849 scoped_ptr
<base::DictionaryValue
> argument(new base::DictionaryValue
);
1850 ExtractRequestInfo(blocked_request
.request
, argument
.get());
1851 WebViewRendererState::WebViewInfo web_view_info
;
1852 bool is_web_view_guest
= GetWebViewInfo(blocked_request
.request
,
1854 argument
->SetString(keys::kMessageKey
, *message
);
1855 argument
->SetString(keys::kStageKey
,
1856 GetRequestStageAsString(blocked_request
.event
));
1858 BrowserThread::PostTask(
1861 base::Bind(&SendOnMessageEventOnUI
,
1863 (*delta
)->extension_id
,
1866 base::Passed(&argument
)));
1871 int ExtensionWebRequestEventRouter::ExecuteDeltas(
1874 bool call_callback
) {
1875 BlockedRequest
& blocked_request
= blocked_requests_
[request_id
];
1876 CHECK(blocked_request
.num_handlers_blocking
== 0);
1877 helpers::EventResponseDeltas
& deltas
= blocked_request
.response_deltas
;
1878 base::TimeDelta block_time
=
1879 base::Time::Now() - blocked_request
.blocking_time
;
1880 request_time_tracker_
->IncrementTotalBlockTime(request_id
, block_time
);
1882 bool credentials_set
= false;
1884 deltas
.sort(&helpers::InDecreasingExtensionInstallationTimeOrder
);
1885 ExtensionWarningSet warnings
;
1887 bool canceled
= false;
1888 helpers::MergeCancelOfResponses(
1889 blocked_request
.response_deltas
,
1891 blocked_request
.net_log
);
1893 if (blocked_request
.event
== kOnBeforeRequest
) {
1894 CHECK(!blocked_request
.callback
.is_null());
1895 helpers::MergeOnBeforeRequestResponses(
1896 blocked_request
.response_deltas
,
1897 blocked_request
.new_url
,
1899 blocked_request
.net_log
);
1900 } else if (blocked_request
.event
== kOnBeforeSendHeaders
) {
1901 CHECK(!blocked_request
.callback
.is_null());
1902 helpers::MergeOnBeforeSendHeadersResponses(
1903 blocked_request
.response_deltas
,
1904 blocked_request
.request_headers
,
1906 blocked_request
.net_log
);
1907 } else if (blocked_request
.event
== kOnHeadersReceived
) {
1908 CHECK(!blocked_request
.callback
.is_null());
1909 helpers::MergeOnHeadersReceivedResponses(
1910 blocked_request
.response_deltas
,
1911 blocked_request
.original_response_headers
.get(),
1912 blocked_request
.override_response_headers
,
1913 blocked_request
.new_url
,
1915 blocked_request
.net_log
);
1916 } else if (blocked_request
.event
== kOnAuthRequired
) {
1917 CHECK(blocked_request
.callback
.is_null());
1918 CHECK(!blocked_request
.auth_callback
.is_null());
1919 credentials_set
= helpers::MergeOnAuthRequiredResponses(
1920 blocked_request
.response_deltas
,
1921 blocked_request
.auth_credentials
,
1923 blocked_request
.net_log
);
1928 SendMessages(profile
, blocked_request
);
1930 if (!warnings
.empty()) {
1931 BrowserThread::PostTask(
1934 base::Bind(&ExtensionWarningService::NotifyWarningsOnUI
,
1935 profile
, warnings
));
1939 request_time_tracker_
->SetRequestCanceled(request_id
);
1940 } else if (blocked_request
.new_url
&&
1941 !blocked_request
.new_url
->is_empty()) {
1942 request_time_tracker_
->SetRequestRedirected(request_id
);
1945 // This triggers onErrorOccurred if canceled is true.
1946 int rv
= canceled
? net::ERR_BLOCKED_BY_CLIENT
: net::OK
;
1948 if (!blocked_request
.callback
.is_null()) {
1949 net::CompletionCallback callback
= blocked_request
.callback
;
1950 // Ensure that request is removed before callback because the callback
1951 // might trigger the next event.
1952 blocked_requests_
.erase(request_id
);
1955 } else if (!blocked_request
.auth_callback
.is_null()) {
1956 net::NetworkDelegate::AuthRequiredResponse response
=
1957 net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION
;
1959 response
= net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_CANCEL_AUTH
;
1960 } else if (credentials_set
) {
1961 response
= net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_SET_AUTH
;
1963 net::NetworkDelegate::AuthCallback callback
= blocked_request
.auth_callback
;
1964 blocked_requests_
.erase(request_id
);
1966 callback
.Run(response
);
1968 blocked_requests_
.erase(request_id
);
1973 bool ExtensionWebRequestEventRouter::ProcessDeclarativeRules(
1975 InfoMap
* extension_info_map
,
1976 const std::string
& event_name
,
1977 net::URLRequest
* request
,
1978 extensions::RequestStage request_stage
,
1979 const net::HttpResponseHeaders
* original_response_headers
) {
1980 WebViewRendererState::WebViewInfo web_view_info
;
1981 bool is_web_view_guest
= GetWebViewInfo(request
, &web_view_info
);
1983 RulesRegistryService::WebViewKey
webview_key(
1984 is_web_view_guest
? web_view_info
.embedder_process_id
: 0,
1985 is_web_view_guest
? web_view_info
.instance_id
: 0);
1986 RulesRegistryKey
rules_key(profile
, webview_key
);
1987 // If this check fails, check that the active stages are up-to-date in
1988 // browser/extensions/api/declarative_webrequest/request_stage.h .
1989 DCHECK(request_stage
& extensions::kActiveStages
);
1991 // Rules of the current |profile| may apply but we need to check also whether
1992 // there are applicable rules from extensions whose background page
1993 // spans from regular to incognito mode.
1995 // First parameter identifies the registry, the second indicates whether the
1996 // registry belongs to the cross profile.
1997 typedef std::pair
<extensions::WebRequestRulesRegistry
*, bool>
1999 typedef std::vector
<RelevantRegistry
> RelevantRegistries
;
2000 RelevantRegistries relevant_registries
;
2002 if (rules_registries_
.find(rules_key
) != rules_registries_
.end()) {
2003 relevant_registries
.push_back(
2004 std::make_pair(rules_registries_
[rules_key
].get(), false));
2007 void* cross_profile
= GetCrossProfile(profile
);
2008 RulesRegistryKey
cross_profile_rules_key(cross_profile
, webview_key
);
2009 if (cross_profile
&&
2010 rules_registries_
.find(cross_profile_rules_key
) !=
2011 rules_registries_
.end()) {
2012 relevant_registries
.push_back(
2013 std::make_pair(rules_registries_
[cross_profile_rules_key
].get(), true));
2016 // The following block is experimentally enabled and its impact on load time
2017 // logged with UMA Extensions.NetworkDelayRegistryLoad. crbug.com/175961
2018 for (RelevantRegistries::iterator i
= relevant_registries
.begin();
2019 i
!= relevant_registries
.end(); ++i
) {
2020 extensions::WebRequestRulesRegistry
* rules_registry
= i
->first
;
2021 if (!rules_registry
->ready().is_signaled()) {
2022 // The rules registry is still loading. Block this request until it
2024 rules_registry
->ready().Post(
2026 base::Bind(&ExtensionWebRequestEventRouter::OnRulesRegistryReady
,
2030 request
->identifier(),
2032 blocked_requests_
[request
->identifier()].num_handlers_blocking
++;
2033 blocked_requests_
[request
->identifier()].request
= request
;
2034 blocked_requests_
[request
->identifier()].is_incognito
|=
2035 IsIncognitoProfile(profile
);
2036 blocked_requests_
[request
->identifier()].blocking_time
=
2038 blocked_requests_
[request
->identifier()].original_response_headers
=
2039 original_response_headers
;
2040 blocked_requests_
[request
->identifier()].extension_info_map
=
2046 base::Time start
= base::Time::Now();
2048 bool deltas_created
= false;
2049 for (RelevantRegistries::iterator i
= relevant_registries
.begin();
2050 i
!= relevant_registries
.end(); ++i
) {
2051 extensions::WebRequestRulesRegistry
* rules_registry
=
2053 helpers::EventResponseDeltas result
=
2054 rules_registry
->CreateDeltas(
2056 extensions::WebRequestData(
2057 request
, request_stage
, original_response_headers
),
2060 if (!result
.empty()) {
2061 helpers::EventResponseDeltas
& deltas
=
2062 blocked_requests_
[request
->identifier()].response_deltas
;
2063 deltas
.insert(deltas
.end(), result
.begin(), result
.end());
2064 deltas_created
= true;
2068 base::TimeDelta elapsed_time
= start
- base::Time::Now();
2069 UMA_HISTOGRAM_TIMES("Extensions.DeclarativeWebRequestNetworkDelay",
2072 return deltas_created
;
2075 void ExtensionWebRequestEventRouter::OnRulesRegistryReady(
2077 const std::string
& event_name
,
2079 extensions::RequestStage request_stage
) {
2080 // It's possible that this request was deleted, or cancelled by a previous
2081 // event handler. If so, ignore this response.
2082 if (blocked_requests_
.find(request_id
) == blocked_requests_
.end())
2085 BlockedRequest
& blocked_request
= blocked_requests_
[request_id
];
2086 base::TimeDelta block_time
=
2087 base::Time::Now() - blocked_request
.blocking_time
;
2088 UMA_HISTOGRAM_TIMES("Extensions.NetworkDelayRegistryLoad", block_time
);
2090 ProcessDeclarativeRules(profile
,
2091 blocked_request
.extension_info_map
,
2093 blocked_request
.request
,
2095 blocked_request
.original_response_headers
.get());
2096 // Reset to NULL so that nobody relies on this being set.
2097 blocked_request
.extension_info_map
= NULL
;
2098 DecrementBlockCount(profile
, std::string(), event_name
, request_id
, NULL
);
2101 bool ExtensionWebRequestEventRouter::GetAndSetSignaled(uint64 request_id
,
2102 EventTypes event_type
) {
2103 SignaledRequestMap::iterator iter
= signaled_requests_
.find(request_id
);
2104 if (iter
== signaled_requests_
.end()) {
2105 signaled_requests_
[request_id
] = event_type
;
2108 bool was_signaled_before
= (iter
->second
& event_type
) != 0;
2109 iter
->second
|= event_type
;
2110 return was_signaled_before
;
2113 void ExtensionWebRequestEventRouter::ClearSignaled(uint64 request_id
,
2114 EventTypes event_type
) {
2115 SignaledRequestMap::iterator iter
= signaled_requests_
.find(request_id
);
2116 if (iter
== signaled_requests_
.end())
2118 iter
->second
&= ~event_type
;
2121 // Special QuotaLimitHeuristic for WebRequestHandlerBehaviorChangedFunction.
2123 // Each call of webRequest.handlerBehaviorChanged() clears the in-memory cache
2124 // of WebKit at the time of the next page load (top level navigation event).
2125 // This quota heuristic is intended to limit the number of times the cache is
2126 // cleared by an extension.
2128 // As we want to account for the number of times the cache is really cleared
2129 // (opposed to the number of times webRequest.handlerBehaviorChanged() is
2130 // called), we cannot decide whether a call of
2131 // webRequest.handlerBehaviorChanged() should trigger a quota violation at the
2132 // time it is called. Instead we only decrement the bucket counter at the time
2133 // when the cache is cleared (when page loads happen).
2134 class ClearCacheQuotaHeuristic
: public extensions::QuotaLimitHeuristic
{
2136 ClearCacheQuotaHeuristic(const Config
& config
, BucketMapper
* map
)
2137 : QuotaLimitHeuristic(
2140 "MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES"),
2141 callback_registered_(false),
2142 weak_ptr_factory_(this) {}
2143 virtual ~ClearCacheQuotaHeuristic() {}
2144 virtual bool Apply(Bucket
* bucket
,
2145 const base::TimeTicks
& event_time
) OVERRIDE
;
2148 // Callback that is triggered by the ExtensionWebRequestEventRouter on a page
2151 // We don't need to take care of the life time of |bucket|: It is owned by the
2152 // BucketMapper of our base class in |QuotaLimitHeuristic::bucket_mapper_|. As
2153 // long as |this| exists, the respective BucketMapper and its bucket will
2155 void OnPageLoad(Bucket
* bucket
);
2157 // Flag to prevent that we register more than one call back in-between
2158 // clearing the cache.
2159 bool callback_registered_
;
2161 base::WeakPtrFactory
<ClearCacheQuotaHeuristic
> weak_ptr_factory_
;
2163 DISALLOW_COPY_AND_ASSIGN(ClearCacheQuotaHeuristic
);
2166 bool ClearCacheQuotaHeuristic::Apply(Bucket
* bucket
,
2167 const base::TimeTicks
& event_time
) {
2168 if (event_time
> bucket
->expiration())
2169 bucket
->Reset(config(), event_time
);
2171 // Call bucket->DeductToken() on a new page load, this is when
2172 // webRequest.handlerBehaviorChanged() clears the cache.
2173 if (!callback_registered_
) {
2174 ExtensionWebRequestEventRouter::GetInstance()->AddCallbackForPageLoad(
2175 base::Bind(&ClearCacheQuotaHeuristic::OnPageLoad
,
2176 weak_ptr_factory_
.GetWeakPtr(),
2178 callback_registered_
= true;
2181 // We only check whether tokens are left here. Deducting a token happens in
2183 return bucket
->has_tokens();
2186 void ClearCacheQuotaHeuristic::OnPageLoad(Bucket
* bucket
) {
2187 callback_registered_
= false;
2188 bucket
->DeductToken();
2191 bool WebRequestInternalAddEventListenerFunction::RunSync() {
2192 // Argument 0 is the callback, which we don't use here.
2193 ExtensionWebRequestEventRouter::RequestFilter filter
;
2194 base::DictionaryValue
* value
= NULL
;
2196 EXTENSION_FUNCTION_VALIDATE(args_
->GetDictionary(1, &value
));
2197 // Failure + an empty error string means a fatal error.
2198 EXTENSION_FUNCTION_VALIDATE(filter
.InitFromValue(*value
, &error_
) ||
2200 if (!error_
.empty())
2203 int extra_info_spec
= 0;
2204 if (HasOptionalArgument(2)) {
2205 base::ListValue
* value
= NULL
;
2206 EXTENSION_FUNCTION_VALIDATE(args_
->GetList(2, &value
));
2207 EXTENSION_FUNCTION_VALIDATE(
2208 ExtensionWebRequestEventRouter::ExtraInfoSpec::InitFromValue(
2209 *value
, &extra_info_spec
));
2212 std::string event_name
;
2213 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(3, &event_name
));
2215 std::string sub_event_name
;
2216 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(4, &sub_event_name
));
2218 int webview_instance_id
= 0;
2219 EXTENSION_FUNCTION_VALIDATE(args_
->GetInteger(5, &webview_instance_id
));
2221 base::WeakPtr
<extensions::ExtensionMessageFilter
> ipc_sender
=
2223 int embedder_process_id
=
2224 ipc_sender
.get() ? ipc_sender
->render_process_id() : -1;
2226 const Extension
* extension
=
2227 extension_info_map()->extensions().GetByID(extension_id());
2228 std::string extension_name
= extension
? extension
->name() : extension_id();
2230 bool is_web_view_guest
= webview_instance_id
!= 0;
2231 // We check automatically whether the extension has the 'webRequest'
2232 // permission. For blocking calls we require the additional permission
2233 // 'webRequestBlocking'.
2234 if ((!is_web_view_guest
&&
2236 (ExtensionWebRequestEventRouter::ExtraInfoSpec::BLOCKING
|
2237 ExtensionWebRequestEventRouter::ExtraInfoSpec::ASYNC_BLOCKING
)) &&
2238 !extension
->permissions_data()->HasAPIPermission(
2239 extensions::APIPermission::kWebRequestBlocking
)) {
2240 error_
= keys::kBlockingPermissionRequired
;
2244 // We allow to subscribe to patterns that are broader than the host
2245 // permissions. E.g., we could subscribe to http://www.example.com/*
2246 // while having host permissions for http://www.example.com/foo/* and
2247 // http://www.example.com/bar/*.
2248 // For this reason we do only a coarse check here to warn the extension
2249 // developer if he does something obviously wrong.
2250 if (!is_web_view_guest
&&
2251 extension
->permissions_data()->GetEffectiveHostPermissions().is_empty()) {
2252 error_
= keys::kHostPermissionsRequired
;
2257 ExtensionWebRequestEventRouter::GetInstance()->AddEventListener(
2258 profile_id(), extension_id(), extension_name
,
2259 event_name
, sub_event_name
, filter
, extra_info_spec
,
2260 embedder_process_id
, webview_instance_id
, ipc_sender_weak());
2261 EXTENSION_FUNCTION_VALIDATE(success
);
2263 helpers::ClearCacheOnNavigation();
2265 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, base::Bind(
2266 &helpers::NotifyWebRequestAPIUsed
,
2267 profile_id(), make_scoped_refptr(GetExtension())));
2272 void WebRequestInternalEventHandledFunction::RespondWithError(
2273 const std::string
& event_name
,
2274 const std::string
& sub_event_name
,
2276 scoped_ptr
<ExtensionWebRequestEventRouter::EventResponse
> response
,
2277 const std::string
& error
) {
2279 ExtensionWebRequestEventRouter::GetInstance()->OnEventHandled(
2285 response
.release());
2288 bool WebRequestInternalEventHandledFunction::RunSync() {
2289 std::string event_name
;
2290 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(0, &event_name
));
2292 std::string sub_event_name
;
2293 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(1, &sub_event_name
));
2295 std::string request_id_str
;
2296 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(2, &request_id_str
));
2298 EXTENSION_FUNCTION_VALIDATE(base::StringToUint64(request_id_str
,
2301 scoped_ptr
<ExtensionWebRequestEventRouter::EventResponse
> response
;
2302 if (HasOptionalArgument(3)) {
2303 base::DictionaryValue
* value
= NULL
;
2304 EXTENSION_FUNCTION_VALIDATE(args_
->GetDictionary(3, &value
));
2306 if (!value
->empty()) {
2307 base::Time install_time
=
2308 extension_info_map()->GetInstallTime(extension_id());
2309 response
.reset(new ExtensionWebRequestEventRouter::EventResponse(
2310 extension_id(), install_time
));
2313 if (value
->HasKey("cancel")) {
2314 // Don't allow cancel mixed with other keys.
2315 if (value
->size() != 1) {
2316 RespondWithError(event_name
,
2320 keys::kInvalidBlockingResponse
);
2324 bool cancel
= false;
2325 EXTENSION_FUNCTION_VALIDATE(value
->GetBoolean("cancel", &cancel
));
2326 response
->cancel
= cancel
;
2329 if (value
->HasKey("redirectUrl")) {
2330 std::string new_url_str
;
2331 EXTENSION_FUNCTION_VALIDATE(value
->GetString("redirectUrl",
2333 response
->new_url
= GURL(new_url_str
);
2334 if (!response
->new_url
.is_valid()) {
2335 RespondWithError(event_name
,
2339 ErrorUtils::FormatErrorMessage(
2340 keys::kInvalidRedirectUrl
, new_url_str
));
2345 const bool hasRequestHeaders
= value
->HasKey("requestHeaders");
2346 const bool hasResponseHeaders
= value
->HasKey("responseHeaders");
2347 if (hasRequestHeaders
|| hasResponseHeaders
) {
2348 if (hasRequestHeaders
&& hasResponseHeaders
) {
2349 // Allow only one of the keys, not both.
2350 RespondWithError(event_name
,
2354 keys::kInvalidHeaderKeyCombination
);
2358 base::ListValue
* headers_value
= NULL
;
2359 scoped_ptr
<net::HttpRequestHeaders
> request_headers
;
2360 scoped_ptr
<helpers::ResponseHeaders
> response_headers
;
2361 if (hasRequestHeaders
) {
2362 request_headers
.reset(new net::HttpRequestHeaders());
2363 EXTENSION_FUNCTION_VALIDATE(value
->GetList(keys::kRequestHeadersKey
,
2366 response_headers
.reset(new helpers::ResponseHeaders());
2367 EXTENSION_FUNCTION_VALIDATE(value
->GetList(keys::kResponseHeadersKey
,
2371 for (size_t i
= 0; i
< headers_value
->GetSize(); ++i
) {
2372 base::DictionaryValue
* header_value
= NULL
;
2375 EXTENSION_FUNCTION_VALIDATE(
2376 headers_value
->GetDictionary(i
, &header_value
));
2377 if (!FromHeaderDictionary(header_value
, &name
, &value
)) {
2378 std::string serialized_header
;
2379 base::JSONWriter::Write(header_value
, &serialized_header
);
2380 RespondWithError(event_name
,
2384 ErrorUtils::FormatErrorMessage(keys::kInvalidHeader
,
2385 serialized_header
));
2388 if (!helpers::IsValidHeaderName(name
)) {
2389 RespondWithError(event_name
,
2393 keys::kInvalidHeaderName
);
2396 if (!helpers::IsValidHeaderValue(value
)) {
2397 RespondWithError(event_name
,
2401 ErrorUtils::FormatErrorMessage(
2402 keys::kInvalidHeaderValue
, name
));
2405 if (hasRequestHeaders
)
2406 request_headers
->SetHeader(name
, value
);
2408 response_headers
->push_back(helpers::ResponseHeader(name
, value
));
2410 if (hasRequestHeaders
)
2411 response
->request_headers
.reset(request_headers
.release());
2413 response
->response_headers
.reset(response_headers
.release());
2416 if (value
->HasKey(keys::kAuthCredentialsKey
)) {
2417 base::DictionaryValue
* credentials_value
= NULL
;
2418 EXTENSION_FUNCTION_VALIDATE(value
->GetDictionary(
2419 keys::kAuthCredentialsKey
,
2420 &credentials_value
));
2421 base::string16 username
;
2422 base::string16 password
;
2423 EXTENSION_FUNCTION_VALIDATE(
2424 credentials_value
->GetString(keys::kUsernameKey
, &username
));
2425 EXTENSION_FUNCTION_VALIDATE(
2426 credentials_value
->GetString(keys::kPasswordKey
, &password
));
2427 response
->auth_credentials
.reset(
2428 new net::AuthCredentials(username
, password
));
2432 ExtensionWebRequestEventRouter::GetInstance()->OnEventHandled(
2433 profile_id(), extension_id(), event_name
, sub_event_name
, request_id
,
2434 response
.release());
2439 void WebRequestHandlerBehaviorChangedFunction::GetQuotaLimitHeuristics(
2440 extensions::QuotaLimitHeuristics
* heuristics
) const {
2441 extensions::QuotaLimitHeuristic::Config config
= {
2442 // See web_request.json for current value.
2443 web_request::MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES
,
2444 base::TimeDelta::FromMinutes(10)};
2445 extensions::QuotaLimitHeuristic::BucketMapper
* bucket_mapper
=
2446 new extensions::QuotaLimitHeuristic::SingletonBucketMapper();
2447 ClearCacheQuotaHeuristic
* heuristic
=
2448 new ClearCacheQuotaHeuristic(config
, bucket_mapper
);
2449 heuristics
->push_back(heuristic
);
2452 void WebRequestHandlerBehaviorChangedFunction::OnQuotaExceeded(
2453 const std::string
& violation_error
) {
2454 // Post warning message.
2455 ExtensionWarningSet warnings
;
2457 ExtensionWarning::CreateRepeatedCacheFlushesWarning(extension_id()));
2458 BrowserThread::PostTask(
2461 base::Bind(&ExtensionWarningService::NotifyWarningsOnUI
,
2462 profile_id(), warnings
));
2464 // Continue gracefully.
2468 bool WebRequestHandlerBehaviorChangedFunction::RunSync() {
2469 helpers::ClearCacheOnNavigation();
2473 void SendExtensionWebRequestStatusToHost(content::RenderProcessHost
* host
) {
2474 Profile
* profile
= Profile::FromBrowserContext(host
->GetBrowserContext());
2478 bool webrequest_used
= false;
2479 const extensions::ExtensionSet
& extensions
=
2480 extensions::ExtensionRegistry::Get(profile
)->enabled_extensions();
2481 extensions::RuntimeData
* runtime_data
=
2482 extensions::ExtensionSystem::Get(profile
)->runtime_data();
2483 for (extensions::ExtensionSet::const_iterator it
= extensions
.begin();
2484 !webrequest_used
&& it
!= extensions
.end();
2486 webrequest_used
|= runtime_data
->HasUsedWebRequest(it
->get());
2489 host
->Send(new ExtensionMsg_UsingWebRequestAPI(webrequest_used
));