OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "chrome/browser/extensions/api/web_request/web_request_api.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "base/bind.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/extensions/api/web_request/upload_data_presenter.h" | |
20 #include "chrome/browser/extensions/api/web_request/web_request_time_tracker.h" | |
21 #include "chrome/common/extensions/api/web_request.h" | |
22 #include "content/public/browser/browser_message_filter.h" | |
23 #include "content/public/browser/browser_thread.h" | |
24 #include "content/public/browser/render_frame_host.h" | |
25 #include "content/public/browser/render_process_host.h" | |
26 #include "content/public/browser/resource_request_info.h" | |
27 #include "content/public/browser/user_metrics.h" | |
28 #include "extensions/browser/api/activity_log/web_request_constants.h" | |
29 #include "extensions/browser/api/declarative_webrequest/request_stage.h" | |
30 #include "extensions/browser/api/declarative_webrequest/webrequest_constants.h" | |
31 #include "extensions/browser/api/declarative_webrequest/webrequest_rules_registr
y.h" | |
32 #include "extensions/browser/api/extensions_api_client.h" | |
33 #include "extensions/browser/api/web_request/web_request_api_constants.h" | |
34 #include "extensions/browser/api/web_request/web_request_api_helpers.h" | |
35 #include "extensions/browser/api/web_request/web_request_api_utils.h" | |
36 #include "extensions/browser/api/web_request/web_request_api_utils.h" | |
37 #include "extensions/browser/api/web_request/web_request_event_router_delegate.h
" | |
38 #include "extensions/browser/event_router.h" | |
39 #include "extensions/browser/extension_message_filter.h" | |
40 #include "extensions/browser/extension_prefs.h" | |
41 #include "extensions/browser/extension_registry.h" | |
42 #include "extensions/browser/extension_system.h" | |
43 #include "extensions/browser/extensions_browser_client.h" | |
44 #include "extensions/browser/guest_view/web_view/web_view_constants.h" | |
45 #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h" | |
46 #include "extensions/browser/info_map.h" | |
47 #include "extensions/browser/runtime_data.h" | |
48 #include "extensions/browser/warning_service.h" | |
49 #include "extensions/browser/warning_set.h" | |
50 #include "extensions/common/error_utils.h" | |
51 #include "extensions/common/event_filtering_info.h" | |
52 #include "extensions/common/extension.h" | |
53 #include "extensions/common/features/feature.h" | |
54 #include "extensions/common/permissions/permissions_data.h" | |
55 #include "extensions/common/url_pattern.h" | |
56 #include "extensions/strings/grit/extensions_strings.h" | |
57 #include "net/base/auth.h" | |
58 #include "net/base/net_errors.h" | |
59 #include "net/base/upload_data_stream.h" | |
60 #include "net/http/http_response_headers.h" | |
61 #include "net/http/http_util.h" | |
62 #include "net/url_request/url_request.h" | |
63 #include "ui/base/l10n/l10n_util.h" | |
64 #include "url/gurl.h" | |
65 | |
66 using base::DictionaryValue; | |
67 using base::ListValue; | |
68 using base::StringValue; | |
69 using content::BrowserMessageFilter; | |
70 using content::BrowserThread; | |
71 using content::ResourceRequestInfo; | |
72 using content::ResourceType; | |
73 using extensions::ErrorUtils; | |
74 using extensions::Extension; | |
75 using extensions::InfoMap; | |
76 using extensions::Feature; | |
77 using extensions::RulesRegistryService; | |
78 using extensions::Warning; | |
79 using extensions::WarningService; | |
80 using extensions::WarningSet; | |
81 | |
82 namespace activitylog = activity_log_web_request_constants; | |
83 namespace helpers = extension_web_request_api_helpers; | |
84 namespace utils = extension_web_request_api_utils; | |
85 namespace keys = extension_web_request_api_constants; | |
86 namespace web_request = extensions::api::web_request; | |
87 namespace declarative_keys = extensions::declarative_webrequest_constants; | |
88 | |
89 namespace { | |
90 | |
91 const char kWebRequestEventPrefix[] = "webRequest."; | |
92 | |
93 // List of all the webRequest events. | |
94 const char* const kWebRequestEvents[] = { | |
95 keys::kOnBeforeRedirectEvent, | |
96 web_request::OnBeforeRequest::kEventName, | |
97 keys::kOnBeforeSendHeadersEvent, | |
98 keys::kOnCompletedEvent, | |
99 web_request::OnErrorOccurred::kEventName, | |
100 keys::kOnSendHeadersEvent, | |
101 keys::kOnAuthRequiredEvent, | |
102 keys::kOnResponseStartedEvent, | |
103 keys::kOnHeadersReceivedEvent, | |
104 }; | |
105 | |
106 const size_t kWebRequestEventsLength = arraysize(kWebRequestEvents); | |
107 | |
108 const char* GetRequestStageAsString( | |
109 ExtensionWebRequestEventRouter::EventTypes type) { | |
110 switch (type) { | |
111 case ExtensionWebRequestEventRouter::kInvalidEvent: | |
112 return "Invalid"; | |
113 case ExtensionWebRequestEventRouter::kOnBeforeRequest: | |
114 return keys::kOnBeforeRequest; | |
115 case ExtensionWebRequestEventRouter::kOnBeforeSendHeaders: | |
116 return keys::kOnBeforeSendHeaders; | |
117 case ExtensionWebRequestEventRouter::kOnSendHeaders: | |
118 return keys::kOnSendHeaders; | |
119 case ExtensionWebRequestEventRouter::kOnHeadersReceived: | |
120 return keys::kOnHeadersReceived; | |
121 case ExtensionWebRequestEventRouter::kOnBeforeRedirect: | |
122 return keys::kOnBeforeRedirect; | |
123 case ExtensionWebRequestEventRouter::kOnAuthRequired: | |
124 return keys::kOnAuthRequired; | |
125 case ExtensionWebRequestEventRouter::kOnResponseStarted: | |
126 return keys::kOnResponseStarted; | |
127 case ExtensionWebRequestEventRouter::kOnErrorOccurred: | |
128 return keys::kOnErrorOccurred; | |
129 case ExtensionWebRequestEventRouter::kOnCompleted: | |
130 return keys::kOnCompleted; | |
131 } | |
132 NOTREACHED(); | |
133 return "Not reached"; | |
134 } | |
135 | |
136 int GetFrameId(bool is_main_frame, int frame_id) { | |
137 return is_main_frame ? 0 : frame_id; | |
138 } | |
139 | |
140 bool IsWebRequestEvent(const std::string& event_name) { | |
141 std::string web_request_event_name(event_name); | |
142 if (StartsWithASCII( | |
143 web_request_event_name, webview::kWebViewEventPrefix, true)) { | |
144 web_request_event_name.replace( | |
145 0, strlen(webview::kWebViewEventPrefix), kWebRequestEventPrefix); | |
146 } | |
147 return std::find( | |
148 kWebRequestEvents, | |
149 kWebRequestEvents + kWebRequestEventsLength, | |
150 web_request_event_name) != (kWebRequestEvents + kWebRequestEventsLength); | |
151 } | |
152 | |
153 // Returns whether |request| has been triggered by an extension in | |
154 // |extension_info_map|. | |
155 bool IsRequestFromExtension(const net::URLRequest* request, | |
156 const InfoMap* extension_info_map) { | |
157 // |extension_info_map| is NULL for system-level requests. | |
158 if (!extension_info_map) | |
159 return false; | |
160 | |
161 const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request); | |
162 | |
163 // If this request was not created by the ResourceDispatcher, |info| is NULL. | |
164 // All requests from extensions are created by the ResourceDispatcher. | |
165 if (!info) | |
166 return false; | |
167 | |
168 return extension_info_map->process_map().Contains(info->GetChildID()); | |
169 } | |
170 | |
171 void ExtractRequestRoutingInfo(net::URLRequest* request, | |
172 int* render_process_host_id, | |
173 int* routing_id) { | |
174 if (!request->GetUserData(NULL)) | |
175 return; | |
176 const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request); | |
177 *render_process_host_id = info->GetChildID(); | |
178 *routing_id = info->GetRouteID(); | |
179 } | |
180 | |
181 // Given a |request|, this function determines whether it originated from | |
182 // a <webview> guest process or not. If it is from a <webview> guest process, | |
183 // then |web_view_info| is returned with information about the instance ID | |
184 // that uniquely identifies the <webview> and its embedder. | |
185 bool GetWebViewInfo( | |
186 net::URLRequest* request, | |
187 extensions::WebViewRendererState::WebViewInfo* web_view_info) { | |
188 int render_process_host_id = -1; | |
189 int routing_id = -1; | |
190 ExtractRequestRoutingInfo(request, &render_process_host_id, &routing_id); | |
191 return extensions::WebViewRendererState::GetInstance()-> | |
192 GetInfo(render_process_host_id, routing_id, web_view_info); | |
193 } | |
194 | |
195 void ExtractRequestInfoDetails(net::URLRequest* request, | |
196 bool* is_main_frame, | |
197 int* frame_id, | |
198 bool* parent_is_main_frame, | |
199 int* parent_frame_id, | |
200 int* render_process_host_id, | |
201 int* routing_id, | |
202 ResourceType* resource_type) { | |
203 if (!request->GetUserData(NULL)) | |
204 return; | |
205 | |
206 const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request); | |
207 *frame_id = info->GetRenderFrameID(); | |
208 *is_main_frame = info->IsMainFrame(); | |
209 *parent_frame_id = info->GetParentRenderFrameID(); | |
210 *parent_is_main_frame = info->ParentIsMainFrame(); | |
211 *render_process_host_id = info->GetChildID(); | |
212 *routing_id = info->GetRouteID(); | |
213 | |
214 // Restrict the resource type to the values we care about. | |
215 if (utils::IsRelevantResourceType(info->GetResourceType())) | |
216 *resource_type = info->GetResourceType(); | |
217 else | |
218 *resource_type = content::RESOURCE_TYPE_LAST_TYPE; | |
219 } | |
220 | |
221 // Extracts the body from |request| and writes the data into |out|. | |
222 void ExtractRequestInfoBody(const net::URLRequest* request, | |
223 base::DictionaryValue* out) { | |
224 const net::UploadDataStream* upload_data = request->get_upload(); | |
225 if (!upload_data || | |
226 (request->method() != "POST" && request->method() != "PUT")) | |
227 return; // Need to exit without "out->Set(keys::kRequestBodyKey, ...);" . | |
228 | |
229 base::DictionaryValue* requestBody = new base::DictionaryValue(); | |
230 out->Set(keys::kRequestBodyKey, requestBody); | |
231 | |
232 // Get the data presenters, ordered by how specific they are. | |
233 extensions::ParsedDataPresenter parsed_data_presenter(*request); | |
234 extensions::RawDataPresenter raw_data_presenter; | |
235 extensions::UploadDataPresenter* const presenters[] = { | |
236 &parsed_data_presenter, // 1: any parseable forms? (Specific to forms.) | |
237 &raw_data_presenter // 2: any data at all? (Non-specific.) | |
238 }; | |
239 // Keys for the results of the corresponding presenters. | |
240 static const char* const kKeys[] = { | |
241 keys::kRequestBodyFormDataKey, | |
242 keys::kRequestBodyRawKey | |
243 }; | |
244 | |
245 const ScopedVector<net::UploadElementReader>& readers = | |
246 upload_data->element_readers(); | |
247 bool some_succeeded = false; | |
248 for (size_t i = 0; !some_succeeded && i < arraysize(presenters); ++i) { | |
249 ScopedVector<net::UploadElementReader>::const_iterator reader; | |
250 for (reader = readers.begin(); reader != readers.end(); ++reader) | |
251 presenters[i]->FeedNext(**reader); | |
252 if (presenters[i]->Succeeded()) { | |
253 requestBody->Set(kKeys[i], presenters[i]->Result().release()); | |
254 some_succeeded = true; | |
255 } | |
256 } | |
257 if (!some_succeeded) | |
258 requestBody->SetString(keys::kRequestBodyErrorKey, "Unknown error."); | |
259 } | |
260 | |
261 // Converts a HttpHeaders dictionary to a |name|, |value| pair. Returns | |
262 // true if successful. | |
263 bool FromHeaderDictionary(const base::DictionaryValue* header_value, | |
264 std::string* name, | |
265 std::string* value) { | |
266 if (!header_value->GetString(keys::kHeaderNameKey, name)) | |
267 return false; | |
268 | |
269 // We require either a "value" or a "binaryValue" entry. | |
270 if (!(header_value->HasKey(keys::kHeaderValueKey) ^ | |
271 header_value->HasKey(keys::kHeaderBinaryValueKey))) | |
272 return false; | |
273 | |
274 if (header_value->HasKey(keys::kHeaderValueKey)) { | |
275 if (!header_value->GetString(keys::kHeaderValueKey, value)) { | |
276 return false; | |
277 } | |
278 } else if (header_value->HasKey(keys::kHeaderBinaryValueKey)) { | |
279 const base::ListValue* list = NULL; | |
280 if (!header_value->HasKey(keys::kHeaderBinaryValueKey)) { | |
281 *value = ""; | |
282 } else if (!header_value->GetList(keys::kHeaderBinaryValueKey, &list) || | |
283 !helpers::CharListToString(list, value)) { | |
284 return false; | |
285 } | |
286 } | |
287 return true; | |
288 } | |
289 | |
290 // Creates a list of HttpHeaders (see the extension API JSON). If |headers| is | |
291 // NULL, the list is empty. Ownership is passed to the caller. | |
292 base::ListValue* GetResponseHeadersList( | |
293 const net::HttpResponseHeaders* headers) { | |
294 base::ListValue* headers_value = new base::ListValue(); | |
295 if (headers) { | |
296 void* iter = NULL; | |
297 std::string name; | |
298 std::string value; | |
299 while (headers->EnumerateHeaderLines(&iter, &name, &value)) | |
300 headers_value->Append(helpers::CreateHeaderDictionary(name, value)); | |
301 } | |
302 return headers_value; | |
303 } | |
304 | |
305 base::ListValue* GetRequestHeadersList(const net::HttpRequestHeaders& headers) { | |
306 base::ListValue* headers_value = new base::ListValue(); | |
307 for (net::HttpRequestHeaders::Iterator it(headers); it.GetNext(); ) | |
308 headers_value->Append( | |
309 helpers::CreateHeaderDictionary(it.name(), it.value())); | |
310 return headers_value; | |
311 } | |
312 | |
313 // Creates a base::StringValue with the status line of |headers|. If |headers| | |
314 // is NULL, an empty string is returned. Ownership is passed to the caller. | |
315 base::StringValue* GetStatusLine(net::HttpResponseHeaders* headers) { | |
316 return new base::StringValue( | |
317 headers ? headers->GetStatusLine() : std::string()); | |
318 } | |
319 | |
320 void RemoveEventListenerOnUI( | |
321 void* browser_context_id, | |
322 const std::string& event_name, | |
323 int process_id, | |
324 const std::string& extension_id) { | |
325 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
326 | |
327 content::BrowserContext* browser_context = | |
328 reinterpret_cast<content::BrowserContext*>(browser_context_id); | |
329 if (!extensions::ExtensionsBrowserClient::Get()->IsValidContext( | |
330 browser_context)) | |
331 return; | |
332 | |
333 extensions::EventRouter* event_router = | |
334 extensions::EventRouter::Get(browser_context); | |
335 if (!event_router) | |
336 return; | |
337 | |
338 content::RenderProcessHost* process = | |
339 content::RenderProcessHost::FromID(process_id); | |
340 if (!process) | |
341 return; | |
342 | |
343 event_router->RemoveEventListener(event_name, process, extension_id); | |
344 } | |
345 | |
346 // Sends an event to subscribers of chrome.declarativeWebRequest.onMessage or | |
347 // to subscribers of webview.onMessage if the action is being operated upon | |
348 // a <webview> guest renderer. | |
349 // |extension_id| identifies the extension that sends and receives the event. | |
350 // |is_web_view_guest| indicates whether the action is for a <webview>. | |
351 // |web_view_info| is a struct containing information about the <webview> | |
352 // embedder. | |
353 // |event_argument| is passed to the event listener. | |
354 void SendOnMessageEventOnUI( | |
355 void* browser_context_id, | |
356 const std::string& extension_id, | |
357 bool is_web_view_guest, | |
358 const extensions::WebViewRendererState::WebViewInfo& web_view_info, | |
359 scoped_ptr<base::DictionaryValue> event_argument) { | |
360 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
361 | |
362 content::BrowserContext* browser_context = | |
363 reinterpret_cast<content::BrowserContext*>(browser_context_id); | |
364 if (!extensions::ExtensionsBrowserClient::Get()->IsValidContext( | |
365 browser_context)) | |
366 return; | |
367 | |
368 scoped_ptr<base::ListValue> event_args(new base::ListValue); | |
369 event_args->Append(event_argument.release()); | |
370 | |
371 extensions::EventRouter* event_router = | |
372 extensions::EventRouter::Get(browser_context); | |
373 | |
374 extensions::EventFilteringInfo event_filtering_info; | |
375 | |
376 std::string event_name; | |
377 // The instance ID uniquely identifies a <webview> instance within an embedder | |
378 // process. We use a filter here so that only event listeners for a particular | |
379 // <webview> will fire. | |
380 if (is_web_view_guest) { | |
381 event_filtering_info.SetInstanceID(web_view_info.instance_id); | |
382 event_name = webview::kEventMessage; | |
383 } else { | |
384 event_name = declarative_keys::kOnMessage; | |
385 } | |
386 | |
387 scoped_ptr<extensions::Event> event(new extensions::Event( | |
388 event_name, | |
389 event_args.Pass(), browser_context, GURL(), | |
390 extensions::EventRouter::USER_GESTURE_UNKNOWN, | |
391 event_filtering_info)); | |
392 event_router->DispatchEventToExtension(extension_id, event.Pass()); | |
393 } | |
394 | |
395 void RemoveEventListenerOnIOThread( | |
396 content::BrowserContext* browser_context, | |
397 const std::string& extension_id, | |
398 const std::string& sub_event_name) { | |
399 ExtensionWebRequestEventRouter::GetInstance()->RemoveEventListener( | |
400 browser_context, extension_id, sub_event_name); | |
401 } | |
402 | |
403 } // namespace | |
404 | |
405 namespace extensions { | |
406 | |
407 WebRequestAPI::WebRequestAPI(content::BrowserContext* context) | |
408 : browser_context_(context) { | |
409 EventRouter* event_router = EventRouter::Get(browser_context_); | |
410 for (size_t i = 0; i < arraysize(kWebRequestEvents); ++i) { | |
411 // Observe the webRequest event. | |
412 std::string event_name = kWebRequestEvents[i]; | |
413 event_router->RegisterObserver(this, event_name); | |
414 | |
415 // Also observe the corresponding webview event. | |
416 event_name.replace( | |
417 0, sizeof(kWebRequestEventPrefix) - 1, webview::kWebViewEventPrefix); | |
418 event_router->RegisterObserver(this, event_name); | |
419 } | |
420 } | |
421 | |
422 WebRequestAPI::~WebRequestAPI() { | |
423 EventRouter::Get(browser_context_)->UnregisterObserver(this); | |
424 } | |
425 | |
426 static base::LazyInstance<BrowserContextKeyedAPIFactory<WebRequestAPI> > | |
427 g_factory = LAZY_INSTANCE_INITIALIZER; | |
428 | |
429 // static | |
430 BrowserContextKeyedAPIFactory<WebRequestAPI>* | |
431 WebRequestAPI::GetFactoryInstance() { | |
432 return g_factory.Pointer(); | |
433 } | |
434 | |
435 void WebRequestAPI::OnListenerRemoved(const EventListenerInfo& details) { | |
436 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
437 // Note that details.event_name includes the sub-event details (e.g. "/123"). | |
438 BrowserThread::PostTask(BrowserThread::IO, | |
439 FROM_HERE, | |
440 base::Bind(&RemoveEventListenerOnIOThread, | |
441 details.browser_context, | |
442 details.extension_id, | |
443 details.event_name)); | |
444 } | |
445 | |
446 } // namespace extensions | |
447 | |
448 // Represents a single unique listener to an event, along with whatever filter | |
449 // parameters and extra_info_spec were specified at the time the listener was | |
450 // added. | |
451 // NOTE(benjhayden) New APIs should not use this sub_event_name trick! It does | |
452 // not play well with event pages. See downloads.onDeterminingFilename and | |
453 // ExtensionDownloadsEventRouter for an alternative approach. | |
454 struct ExtensionWebRequestEventRouter::EventListener { | |
455 std::string extension_id; | |
456 std::string extension_name; | |
457 std::string sub_event_name; | |
458 RequestFilter filter; | |
459 int extra_info_spec; | |
460 int embedder_process_id; | |
461 int webview_instance_id; | |
462 base::WeakPtr<IPC::Sender> ipc_sender; | |
463 mutable std::set<uint64> blocked_requests; | |
464 | |
465 // Comparator to work with std::set. | |
466 bool operator<(const EventListener& that) const { | |
467 if (extension_id < that.extension_id) | |
468 return true; | |
469 if (extension_id == that.extension_id && | |
470 sub_event_name < that.sub_event_name) | |
471 return true; | |
472 return false; | |
473 } | |
474 | |
475 EventListener() : extra_info_spec(0) {} | |
476 }; | |
477 | |
478 // Contains info about requests that are blocked waiting for a response from | |
479 // an extension. | |
480 struct ExtensionWebRequestEventRouter::BlockedRequest { | |
481 // The request that is being blocked. | |
482 net::URLRequest* request; | |
483 | |
484 // Whether the request originates from an incognito tab. | |
485 bool is_incognito; | |
486 | |
487 // The event that we're currently blocked on. | |
488 EventTypes event; | |
489 | |
490 // The number of event handlers that we are awaiting a response from. | |
491 int num_handlers_blocking; | |
492 | |
493 // Pointer to NetLog to report significant changes to the request for | |
494 // debugging. | |
495 const net::BoundNetLog* net_log; | |
496 | |
497 // The callback to call when we get a response from all event handlers. | |
498 net::CompletionCallback callback; | |
499 | |
500 // If non-empty, this contains the new URL that the request will redirect to. | |
501 // Only valid for OnBeforeRequest and OnHeadersReceived. | |
502 GURL* new_url; | |
503 | |
504 // The request headers that will be issued along with this request. Only valid | |
505 // for OnBeforeSendHeaders. | |
506 net::HttpRequestHeaders* request_headers; | |
507 | |
508 // The response headers that were received from the server. Only valid for | |
509 // OnHeadersReceived. | |
510 scoped_refptr<const net::HttpResponseHeaders> original_response_headers; | |
511 | |
512 // Location where to override response headers. Only valid for | |
513 // OnHeadersReceived. | |
514 scoped_refptr<net::HttpResponseHeaders>* override_response_headers; | |
515 | |
516 // If non-empty, this contains the auth credentials that may be filled in. | |
517 // Only valid for OnAuthRequired. | |
518 net::AuthCredentials* auth_credentials; | |
519 | |
520 // The callback to invoke for auth. If |auth_callback.is_null()| is false, | |
521 // |callback| must be NULL. | |
522 // Only valid for OnAuthRequired. | |
523 net::NetworkDelegate::AuthCallback auth_callback; | |
524 | |
525 // Time the request was paused. Used for logging purposes. | |
526 base::Time blocking_time; | |
527 | |
528 // Changes requested by extensions. | |
529 helpers::EventResponseDeltas response_deltas; | |
530 | |
531 // Provider of meta data about extensions, only used and non-NULL for events | |
532 // that are delayed until the rules registry is ready. | |
533 InfoMap* extension_info_map; | |
534 | |
535 BlockedRequest() | |
536 : request(NULL), | |
537 is_incognito(false), | |
538 event(kInvalidEvent), | |
539 num_handlers_blocking(0), | |
540 net_log(NULL), | |
541 new_url(NULL), | |
542 request_headers(NULL), | |
543 override_response_headers(NULL), | |
544 auth_credentials(NULL), | |
545 extension_info_map(NULL) {} | |
546 }; | |
547 | |
548 bool ExtensionWebRequestEventRouter::RequestFilter::InitFromValue( | |
549 const base::DictionaryValue& value, std::string* error) { | |
550 if (!value.HasKey("urls")) | |
551 return false; | |
552 | |
553 for (base::DictionaryValue::Iterator it(value); !it.IsAtEnd(); it.Advance()) { | |
554 if (it.key() == "urls") { | |
555 const base::ListValue* urls_value = NULL; | |
556 if (!it.value().GetAsList(&urls_value)) | |
557 return false; | |
558 for (size_t i = 0; i < urls_value->GetSize(); ++i) { | |
559 std::string url; | |
560 URLPattern pattern( | |
561 URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS | | |
562 URLPattern::SCHEME_FTP | URLPattern::SCHEME_FILE | | |
563 URLPattern::SCHEME_EXTENSION); | |
564 if (!urls_value->GetString(i, &url) || | |
565 pattern.Parse(url) != URLPattern::PARSE_SUCCESS) { | |
566 *error = ErrorUtils::FormatErrorMessage( | |
567 keys::kInvalidRequestFilterUrl, url); | |
568 return false; | |
569 } | |
570 urls.AddPattern(pattern); | |
571 } | |
572 } else if (it.key() == "types") { | |
573 const base::ListValue* types_value = NULL; | |
574 if (!it.value().GetAsList(&types_value)) | |
575 return false; | |
576 for (size_t i = 0; i < types_value->GetSize(); ++i) { | |
577 std::string type_str; | |
578 ResourceType type; | |
579 if (!types_value->GetString(i, &type_str) || | |
580 !utils::ParseResourceType(type_str, &type)) | |
581 return false; | |
582 types.push_back(type); | |
583 } | |
584 } else if (it.key() == "tabId") { | |
585 if (!it.value().GetAsInteger(&tab_id)) | |
586 return false; | |
587 } else if (it.key() == "windowId") { | |
588 if (!it.value().GetAsInteger(&window_id)) | |
589 return false; | |
590 } else { | |
591 return false; | |
592 } | |
593 } | |
594 return true; | |
595 } | |
596 | |
597 // static | |
598 bool ExtensionWebRequestEventRouter::ExtraInfoSpec::InitFromValue( | |
599 const base::ListValue& value, int* extra_info_spec) { | |
600 *extra_info_spec = 0; | |
601 for (size_t i = 0; i < value.GetSize(); ++i) { | |
602 std::string str; | |
603 if (!value.GetString(i, &str)) | |
604 return false; | |
605 | |
606 if (str == "requestHeaders") | |
607 *extra_info_spec |= REQUEST_HEADERS; | |
608 else if (str == "responseHeaders") | |
609 *extra_info_spec |= RESPONSE_HEADERS; | |
610 else if (str == "blocking") | |
611 *extra_info_spec |= BLOCKING; | |
612 else if (str == "asyncBlocking") | |
613 *extra_info_spec |= ASYNC_BLOCKING; | |
614 else if (str == "requestBody") | |
615 *extra_info_spec |= REQUEST_BODY; | |
616 else | |
617 return false; | |
618 | |
619 // BLOCKING and ASYNC_BLOCKING are mutually exclusive. | |
620 if ((*extra_info_spec & BLOCKING) && (*extra_info_spec & ASYNC_BLOCKING)) | |
621 return false; | |
622 } | |
623 return true; | |
624 } | |
625 | |
626 | |
627 ExtensionWebRequestEventRouter::EventResponse::EventResponse( | |
628 const std::string& extension_id, const base::Time& extension_install_time) | |
629 : extension_id(extension_id), | |
630 extension_install_time(extension_install_time), | |
631 cancel(false) { | |
632 } | |
633 | |
634 ExtensionWebRequestEventRouter::EventResponse::~EventResponse() { | |
635 } | |
636 | |
637 ExtensionWebRequestEventRouter::RequestFilter::RequestFilter() | |
638 : tab_id(-1), window_id(-1) { | |
639 } | |
640 | |
641 ExtensionWebRequestEventRouter::RequestFilter::~RequestFilter() { | |
642 } | |
643 | |
644 // | |
645 // ExtensionWebRequestEventRouter | |
646 // | |
647 | |
648 // static | |
649 ExtensionWebRequestEventRouter* ExtensionWebRequestEventRouter::GetInstance() { | |
650 return Singleton<ExtensionWebRequestEventRouter>::get(); | |
651 } | |
652 | |
653 ExtensionWebRequestEventRouter::ExtensionWebRequestEventRouter() | |
654 : request_time_tracker_(new ExtensionWebRequestTimeTracker) { | |
655 web_request_event_router_delegate_.reset( | |
656 extensions::ExtensionsAPIClient::Get()-> | |
657 CreateWebRequestEventRouterDelegate()); | |
658 } | |
659 | |
660 ExtensionWebRequestEventRouter::~ExtensionWebRequestEventRouter() { | |
661 } | |
662 | |
663 void ExtensionWebRequestEventRouter::RegisterRulesRegistry( | |
664 void* browser_context, | |
665 const extensions::RulesRegistry::WebViewKey& webview_key, | |
666 scoped_refptr<extensions::WebRequestRulesRegistry> rules_registry) { | |
667 RulesRegistryKey key(browser_context, webview_key); | |
668 if (rules_registry.get()) | |
669 rules_registries_[key] = rules_registry; | |
670 else | |
671 rules_registries_.erase(key); | |
672 } | |
673 | |
674 void ExtensionWebRequestEventRouter::ExtractRequestInfo( | |
675 net::URLRequest* request, base::DictionaryValue* out) { | |
676 bool is_main_frame = false; | |
677 int frame_id = -1; | |
678 bool parent_is_main_frame = false; | |
679 int parent_frame_id = -1; | |
680 int frame_id_for_extension = -1; | |
681 int parent_frame_id_for_extension = -1; | |
682 int render_process_host_id = -1; | |
683 int routing_id = -1; | |
684 ResourceType resource_type = content::RESOURCE_TYPE_LAST_TYPE; | |
685 ExtractRequestInfoDetails(request, &is_main_frame, &frame_id, | |
686 &parent_is_main_frame, &parent_frame_id, | |
687 &render_process_host_id, &routing_id, | |
688 &resource_type); | |
689 frame_id_for_extension = GetFrameId(is_main_frame, frame_id); | |
690 parent_frame_id_for_extension = GetFrameId(parent_is_main_frame, | |
691 parent_frame_id); | |
692 | |
693 out->SetString(keys::kRequestIdKey, | |
694 base::Uint64ToString(request->identifier())); | |
695 out->SetString(keys::kUrlKey, request->url().spec()); | |
696 out->SetString(keys::kMethodKey, request->method()); | |
697 out->SetInteger(keys::kFrameIdKey, frame_id_for_extension); | |
698 out->SetInteger(keys::kParentFrameIdKey, parent_frame_id_for_extension); | |
699 out->SetString(keys::kTypeKey, utils::ResourceTypeToString(resource_type)); | |
700 out->SetDouble(keys::kTimeStampKey, base::Time::Now().ToDoubleT() * 1000); | |
701 if (web_request_event_router_delegate_) { | |
702 web_request_event_router_delegate_->ExtractExtraRequestDetails( | |
703 request, out); | |
704 } | |
705 } | |
706 | |
707 int ExtensionWebRequestEventRouter::OnBeforeRequest( | |
708 void* browser_context, | |
709 InfoMap* extension_info_map, | |
710 net::URLRequest* request, | |
711 const net::CompletionCallback& callback, | |
712 GURL* new_url) { | |
713 // We hide events from the system context as well as sensitive requests. | |
714 if (!browser_context || | |
715 WebRequestPermissions::HideRequest(extension_info_map, request)) | |
716 return net::OK; | |
717 | |
718 if (IsPageLoad(request)) | |
719 NotifyPageLoad(); | |
720 | |
721 request_time_tracker_->LogRequestStartTime(request->identifier(), | |
722 base::Time::Now(), | |
723 request->url(), | |
724 browser_context); | |
725 | |
726 // Whether to initialized blocked_requests_. | |
727 bool initialize_blocked_requests = false; | |
728 | |
729 initialize_blocked_requests |= | |
730 ProcessDeclarativeRules(browser_context, extension_info_map, | |
731 web_request::OnBeforeRequest::kEventName, request, | |
732 extensions::ON_BEFORE_REQUEST, NULL); | |
733 | |
734 int extra_info_spec = 0; | |
735 std::vector<const EventListener*> listeners = | |
736 GetMatchingListeners(browser_context, extension_info_map, | |
737 web_request::OnBeforeRequest::kEventName, request, | |
738 &extra_info_spec); | |
739 if (!listeners.empty() && | |
740 !GetAndSetSignaled(request->identifier(), kOnBeforeRequest)) { | |
741 base::ListValue args; | |
742 base::DictionaryValue* dict = new base::DictionaryValue(); | |
743 ExtractRequestInfo(request, dict); | |
744 if (extra_info_spec & ExtraInfoSpec::REQUEST_BODY) | |
745 ExtractRequestInfoBody(request, dict); | |
746 args.Append(dict); | |
747 | |
748 initialize_blocked_requests |= | |
749 DispatchEvent(browser_context, request, listeners, args); | |
750 } | |
751 | |
752 if (!initialize_blocked_requests) | |
753 return net::OK; // Nobody saw a reason for modifying the request. | |
754 | |
755 blocked_requests_[request->identifier()].event = kOnBeforeRequest; | |
756 blocked_requests_[request->identifier()].is_incognito |= | |
757 IsIncognitoBrowserContext(browser_context); | |
758 blocked_requests_[request->identifier()].request = request; | |
759 blocked_requests_[request->identifier()].callback = callback; | |
760 blocked_requests_[request->identifier()].new_url = new_url; | |
761 blocked_requests_[request->identifier()].net_log = &request->net_log(); | |
762 | |
763 if (blocked_requests_[request->identifier()].num_handlers_blocking == 0) { | |
764 // If there are no blocking handlers, only the declarative rules tried | |
765 // to modify the request and we can respond synchronously. | |
766 return ExecuteDeltas(browser_context, request->identifier(), | |
767 false /* call_callback*/); | |
768 } else { | |
769 return net::ERR_IO_PENDING; | |
770 } | |
771 } | |
772 | |
773 int ExtensionWebRequestEventRouter::OnBeforeSendHeaders( | |
774 void* browser_context, | |
775 InfoMap* extension_info_map, | |
776 net::URLRequest* request, | |
777 const net::CompletionCallback& callback, | |
778 net::HttpRequestHeaders* headers) { | |
779 // We hide events from the system context as well as sensitive requests. | |
780 if (!browser_context || | |
781 WebRequestPermissions::HideRequest(extension_info_map, request)) | |
782 return net::OK; | |
783 | |
784 bool initialize_blocked_requests = false; | |
785 | |
786 initialize_blocked_requests |= | |
787 ProcessDeclarativeRules(browser_context, extension_info_map, | |
788 keys::kOnBeforeSendHeadersEvent, request, | |
789 extensions::ON_BEFORE_SEND_HEADERS, NULL); | |
790 | |
791 int extra_info_spec = 0; | |
792 std::vector<const EventListener*> listeners = | |
793 GetMatchingListeners(browser_context, extension_info_map, | |
794 keys::kOnBeforeSendHeadersEvent, request, | |
795 &extra_info_spec); | |
796 if (!listeners.empty() && | |
797 !GetAndSetSignaled(request->identifier(), kOnBeforeSendHeaders)) { | |
798 base::ListValue args; | |
799 base::DictionaryValue* dict = new base::DictionaryValue(); | |
800 ExtractRequestInfo(request, dict); | |
801 if (extra_info_spec & ExtraInfoSpec::REQUEST_HEADERS) | |
802 dict->Set(keys::kRequestHeadersKey, GetRequestHeadersList(*headers)); | |
803 args.Append(dict); | |
804 | |
805 initialize_blocked_requests |= | |
806 DispatchEvent(browser_context, request, listeners, args); | |
807 } | |
808 | |
809 if (!initialize_blocked_requests) | |
810 return net::OK; // Nobody saw a reason for modifying the request. | |
811 | |
812 blocked_requests_[request->identifier()].event = kOnBeforeSendHeaders; | |
813 blocked_requests_[request->identifier()].is_incognito |= | |
814 IsIncognitoBrowserContext(browser_context); | |
815 blocked_requests_[request->identifier()].request = request; | |
816 blocked_requests_[request->identifier()].callback = callback; | |
817 blocked_requests_[request->identifier()].request_headers = headers; | |
818 blocked_requests_[request->identifier()].net_log = &request->net_log(); | |
819 | |
820 if (blocked_requests_[request->identifier()].num_handlers_blocking == 0) { | |
821 // If there are no blocking handlers, only the declarative rules tried | |
822 // to modify the request and we can respond synchronously. | |
823 return ExecuteDeltas(browser_context, request->identifier(), | |
824 false /* call_callback*/); | |
825 } else { | |
826 return net::ERR_IO_PENDING; | |
827 } | |
828 } | |
829 | |
830 void ExtensionWebRequestEventRouter::OnSendHeaders( | |
831 void* browser_context, | |
832 InfoMap* extension_info_map, | |
833 net::URLRequest* request, | |
834 const net::HttpRequestHeaders& headers) { | |
835 // We hide events from the system context as well as sensitive requests. | |
836 if (!browser_context || | |
837 WebRequestPermissions::HideRequest(extension_info_map, request)) | |
838 return; | |
839 | |
840 if (GetAndSetSignaled(request->identifier(), kOnSendHeaders)) | |
841 return; | |
842 | |
843 ClearSignaled(request->identifier(), kOnBeforeRedirect); | |
844 | |
845 int extra_info_spec = 0; | |
846 std::vector<const EventListener*> listeners = | |
847 GetMatchingListeners(browser_context, extension_info_map, | |
848 keys::kOnSendHeadersEvent, request, | |
849 &extra_info_spec); | |
850 if (listeners.empty()) | |
851 return; | |
852 | |
853 base::ListValue args; | |
854 base::DictionaryValue* dict = new base::DictionaryValue(); | |
855 ExtractRequestInfo(request, dict); | |
856 if (extra_info_spec & ExtraInfoSpec::REQUEST_HEADERS) | |
857 dict->Set(keys::kRequestHeadersKey, GetRequestHeadersList(headers)); | |
858 args.Append(dict); | |
859 | |
860 DispatchEvent(browser_context, request, listeners, args); | |
861 } | |
862 | |
863 int ExtensionWebRequestEventRouter::OnHeadersReceived( | |
864 void* browser_context, | |
865 InfoMap* extension_info_map, | |
866 net::URLRequest* request, | |
867 const net::CompletionCallback& callback, | |
868 const net::HttpResponseHeaders* original_response_headers, | |
869 scoped_refptr<net::HttpResponseHeaders>* override_response_headers, | |
870 GURL* allowed_unsafe_redirect_url) { | |
871 // We hide events from the system context as well as sensitive requests. | |
872 if (!browser_context || | |
873 WebRequestPermissions::HideRequest(extension_info_map, request)) | |
874 return net::OK; | |
875 | |
876 bool initialize_blocked_requests = false; | |
877 | |
878 initialize_blocked_requests |= | |
879 ProcessDeclarativeRules(browser_context, extension_info_map, | |
880 keys::kOnHeadersReceivedEvent, request, | |
881 extensions::ON_HEADERS_RECEIVED, | |
882 original_response_headers); | |
883 | |
884 int extra_info_spec = 0; | |
885 std::vector<const EventListener*> listeners = | |
886 GetMatchingListeners(browser_context, extension_info_map, | |
887 keys::kOnHeadersReceivedEvent, request, | |
888 &extra_info_spec); | |
889 | |
890 if (!listeners.empty() && | |
891 !GetAndSetSignaled(request->identifier(), kOnHeadersReceived)) { | |
892 base::ListValue args; | |
893 base::DictionaryValue* dict = new base::DictionaryValue(); | |
894 ExtractRequestInfo(request, dict); | |
895 dict->SetString(keys::kStatusLineKey, | |
896 original_response_headers->GetStatusLine()); | |
897 if (extra_info_spec & ExtraInfoSpec::RESPONSE_HEADERS) { | |
898 dict->Set(keys::kResponseHeadersKey, | |
899 GetResponseHeadersList(original_response_headers)); | |
900 } | |
901 args.Append(dict); | |
902 | |
903 initialize_blocked_requests |= | |
904 DispatchEvent(browser_context, request, listeners, args); | |
905 } | |
906 | |
907 if (!initialize_blocked_requests) | |
908 return net::OK; // Nobody saw a reason for modifying the request. | |
909 | |
910 blocked_requests_[request->identifier()].event = kOnHeadersReceived; | |
911 blocked_requests_[request->identifier()].is_incognito |= | |
912 IsIncognitoBrowserContext(browser_context); | |
913 blocked_requests_[request->identifier()].request = request; | |
914 blocked_requests_[request->identifier()].callback = callback; | |
915 blocked_requests_[request->identifier()].net_log = &request->net_log(); | |
916 blocked_requests_[request->identifier()].override_response_headers = | |
917 override_response_headers; | |
918 blocked_requests_[request->identifier()].original_response_headers = | |
919 original_response_headers; | |
920 blocked_requests_[request->identifier()].new_url = | |
921 allowed_unsafe_redirect_url; | |
922 | |
923 if (blocked_requests_[request->identifier()].num_handlers_blocking == 0) { | |
924 // If there are no blocking handlers, only the declarative rules tried | |
925 // to modify the request and we can respond synchronously. | |
926 return ExecuteDeltas(browser_context, request->identifier(), | |
927 false /* call_callback*/); | |
928 } else { | |
929 return net::ERR_IO_PENDING; | |
930 } | |
931 } | |
932 | |
933 net::NetworkDelegate::AuthRequiredResponse | |
934 ExtensionWebRequestEventRouter::OnAuthRequired( | |
935 void* browser_context, | |
936 InfoMap* extension_info_map, | |
937 net::URLRequest* request, | |
938 const net::AuthChallengeInfo& auth_info, | |
939 const net::NetworkDelegate::AuthCallback& callback, | |
940 net::AuthCredentials* credentials) { | |
941 // No browser_context means that this is for authentication challenges in the | |
942 // system context. Skip in that case. Also skip sensitive requests. | |
943 if (!browser_context || | |
944 WebRequestPermissions::HideRequest(extension_info_map, request)) | |
945 return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION; | |
946 | |
947 int extra_info_spec = 0; | |
948 std::vector<const EventListener*> listeners = | |
949 GetMatchingListeners(browser_context, extension_info_map, | |
950 keys::kOnAuthRequiredEvent, request, | |
951 &extra_info_spec); | |
952 if (listeners.empty()) | |
953 return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION; | |
954 | |
955 base::ListValue args; | |
956 base::DictionaryValue* dict = new base::DictionaryValue(); | |
957 ExtractRequestInfo(request, dict); | |
958 dict->SetBoolean(keys::kIsProxyKey, auth_info.is_proxy); | |
959 if (!auth_info.scheme.empty()) | |
960 dict->SetString(keys::kSchemeKey, auth_info.scheme); | |
961 if (!auth_info.realm.empty()) | |
962 dict->SetString(keys::kRealmKey, auth_info.realm); | |
963 base::DictionaryValue* challenger = new base::DictionaryValue(); | |
964 challenger->SetString(keys::kHostKey, auth_info.challenger.host()); | |
965 challenger->SetInteger(keys::kPortKey, auth_info.challenger.port()); | |
966 dict->Set(keys::kChallengerKey, challenger); | |
967 dict->Set(keys::kStatusLineKey, GetStatusLine(request->response_headers())); | |
968 if (extra_info_spec & ExtraInfoSpec::RESPONSE_HEADERS) { | |
969 dict->Set(keys::kResponseHeadersKey, | |
970 GetResponseHeadersList(request->response_headers())); | |
971 } | |
972 args.Append(dict); | |
973 | |
974 if (DispatchEvent(browser_context, request, listeners, args)) { | |
975 blocked_requests_[request->identifier()].event = kOnAuthRequired; | |
976 blocked_requests_[request->identifier()].is_incognito |= | |
977 IsIncognitoBrowserContext(browser_context); | |
978 blocked_requests_[request->identifier()].request = request; | |
979 blocked_requests_[request->identifier()].auth_callback = callback; | |
980 blocked_requests_[request->identifier()].auth_credentials = credentials; | |
981 blocked_requests_[request->identifier()].net_log = &request->net_log(); | |
982 return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_IO_PENDING; | |
983 } | |
984 return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION; | |
985 } | |
986 | |
987 void ExtensionWebRequestEventRouter::OnBeforeRedirect( | |
988 void* browser_context, | |
989 InfoMap* extension_info_map, | |
990 net::URLRequest* request, | |
991 const GURL& new_location) { | |
992 // We hide events from the system context as well as sensitive requests. | |
993 if (!browser_context || | |
994 WebRequestPermissions::HideRequest(extension_info_map, request)) | |
995 return; | |
996 | |
997 if (GetAndSetSignaled(request->identifier(), kOnBeforeRedirect)) | |
998 return; | |
999 | |
1000 ClearSignaled(request->identifier(), kOnBeforeRequest); | |
1001 ClearSignaled(request->identifier(), kOnBeforeSendHeaders); | |
1002 ClearSignaled(request->identifier(), kOnSendHeaders); | |
1003 ClearSignaled(request->identifier(), kOnHeadersReceived); | |
1004 | |
1005 int extra_info_spec = 0; | |
1006 std::vector<const EventListener*> listeners = | |
1007 GetMatchingListeners(browser_context, extension_info_map, | |
1008 keys::kOnBeforeRedirectEvent, request, | |
1009 &extra_info_spec); | |
1010 if (listeners.empty()) | |
1011 return; | |
1012 | |
1013 int http_status_code = request->GetResponseCode(); | |
1014 | |
1015 std::string response_ip = request->GetSocketAddress().host(); | |
1016 | |
1017 base::ListValue args; | |
1018 base::DictionaryValue* dict = new base::DictionaryValue(); | |
1019 ExtractRequestInfo(request, dict); | |
1020 dict->SetString(keys::kRedirectUrlKey, new_location.spec()); | |
1021 dict->SetInteger(keys::kStatusCodeKey, http_status_code); | |
1022 if (!response_ip.empty()) | |
1023 dict->SetString(keys::kIpKey, response_ip); | |
1024 dict->SetBoolean(keys::kFromCache, request->was_cached()); | |
1025 dict->Set(keys::kStatusLineKey, GetStatusLine(request->response_headers())); | |
1026 if (extra_info_spec & ExtraInfoSpec::RESPONSE_HEADERS) { | |
1027 dict->Set(keys::kResponseHeadersKey, | |
1028 GetResponseHeadersList(request->response_headers())); | |
1029 } | |
1030 args.Append(dict); | |
1031 | |
1032 DispatchEvent(browser_context, request, listeners, args); | |
1033 } | |
1034 | |
1035 void ExtensionWebRequestEventRouter::OnResponseStarted( | |
1036 void* browser_context, | |
1037 InfoMap* extension_info_map, | |
1038 net::URLRequest* request) { | |
1039 // We hide events from the system context as well as sensitive requests. | |
1040 if (!browser_context || | |
1041 WebRequestPermissions::HideRequest(extension_info_map, request)) | |
1042 return; | |
1043 | |
1044 // OnResponseStarted is even triggered, when the request was cancelled. | |
1045 if (request->status().status() != net::URLRequestStatus::SUCCESS) | |
1046 return; | |
1047 | |
1048 int extra_info_spec = 0; | |
1049 std::vector<const EventListener*> listeners = | |
1050 GetMatchingListeners(browser_context, extension_info_map, | |
1051 keys::kOnResponseStartedEvent, request, | |
1052 &extra_info_spec); | |
1053 if (listeners.empty()) | |
1054 return; | |
1055 | |
1056 // UrlRequestFileJobs do not send headers, so we simulate their behavior. | |
1057 int response_code = 200; | |
1058 if (request->response_headers()) | |
1059 response_code = request->response_headers()->response_code(); | |
1060 | |
1061 std::string response_ip = request->GetSocketAddress().host(); | |
1062 | |
1063 base::ListValue args; | |
1064 base::DictionaryValue* dict = new base::DictionaryValue(); | |
1065 ExtractRequestInfo(request, dict); | |
1066 if (!response_ip.empty()) | |
1067 dict->SetString(keys::kIpKey, response_ip); | |
1068 dict->SetBoolean(keys::kFromCache, request->was_cached()); | |
1069 dict->SetInteger(keys::kStatusCodeKey, response_code); | |
1070 dict->Set(keys::kStatusLineKey, GetStatusLine(request->response_headers())); | |
1071 if (extra_info_spec & ExtraInfoSpec::RESPONSE_HEADERS) { | |
1072 dict->Set(keys::kResponseHeadersKey, | |
1073 GetResponseHeadersList(request->response_headers())); | |
1074 } | |
1075 args.Append(dict); | |
1076 | |
1077 DispatchEvent(browser_context, request, listeners, args); | |
1078 } | |
1079 | |
1080 void ExtensionWebRequestEventRouter::OnCompleted(void* browser_context, | |
1081 InfoMap* extension_info_map, | |
1082 net::URLRequest* request) { | |
1083 // We hide events from the system context as well as sensitive requests. | |
1084 // However, if the request first became sensitive after redirecting we have | |
1085 // already signaled it and thus we have to signal the end of it. This is | |
1086 // risk-free because the handler cannot modify the request now. | |
1087 if (!browser_context || | |
1088 (WebRequestPermissions::HideRequest(extension_info_map, request) && | |
1089 !WasSignaled(*request))) | |
1090 return; | |
1091 | |
1092 request_time_tracker_->LogRequestEndTime(request->identifier(), | |
1093 base::Time::Now()); | |
1094 | |
1095 DCHECK(request->status().status() == net::URLRequestStatus::SUCCESS); | |
1096 | |
1097 DCHECK(!GetAndSetSignaled(request->identifier(), kOnCompleted)); | |
1098 | |
1099 ClearPendingCallbacks(request); | |
1100 | |
1101 int extra_info_spec = 0; | |
1102 std::vector<const EventListener*> listeners = | |
1103 GetMatchingListeners(browser_context, extension_info_map, | |
1104 keys::kOnCompletedEvent, request, &extra_info_spec); | |
1105 if (listeners.empty()) | |
1106 return; | |
1107 | |
1108 // UrlRequestFileJobs do not send headers, so we simulate their behavior. | |
1109 int response_code = 200; | |
1110 if (request->response_headers()) | |
1111 response_code = request->response_headers()->response_code(); | |
1112 | |
1113 std::string response_ip = request->GetSocketAddress().host(); | |
1114 | |
1115 base::ListValue args; | |
1116 base::DictionaryValue* dict = new base::DictionaryValue(); | |
1117 ExtractRequestInfo(request, dict); | |
1118 dict->SetInteger(keys::kStatusCodeKey, response_code); | |
1119 if (!response_ip.empty()) | |
1120 dict->SetString(keys::kIpKey, response_ip); | |
1121 dict->SetBoolean(keys::kFromCache, request->was_cached()); | |
1122 dict->Set(keys::kStatusLineKey, GetStatusLine(request->response_headers())); | |
1123 if (extra_info_spec & ExtraInfoSpec::RESPONSE_HEADERS) { | |
1124 dict->Set(keys::kResponseHeadersKey, | |
1125 GetResponseHeadersList(request->response_headers())); | |
1126 } | |
1127 args.Append(dict); | |
1128 | |
1129 DispatchEvent(browser_context, request, listeners, args); | |
1130 } | |
1131 | |
1132 void ExtensionWebRequestEventRouter::OnErrorOccurred( | |
1133 void* browser_context, | |
1134 InfoMap* extension_info_map, | |
1135 net::URLRequest* request, | |
1136 bool started) { | |
1137 // We hide events from the system context as well as sensitive requests. | |
1138 // However, if the request first became sensitive after redirecting we have | |
1139 // already signaled it and thus we have to signal the end of it. This is | |
1140 // risk-free because the handler cannot modify the request now. | |
1141 if (!browser_context || | |
1142 (WebRequestPermissions::HideRequest(extension_info_map, request) && | |
1143 !WasSignaled(*request))) | |
1144 return; | |
1145 | |
1146 request_time_tracker_->LogRequestEndTime(request->identifier(), | |
1147 base::Time::Now()); | |
1148 | |
1149 DCHECK(request->status().status() == net::URLRequestStatus::FAILED || | |
1150 request->status().status() == net::URLRequestStatus::CANCELED); | |
1151 | |
1152 DCHECK(!GetAndSetSignaled(request->identifier(), kOnErrorOccurred)); | |
1153 | |
1154 ClearPendingCallbacks(request); | |
1155 | |
1156 int extra_info_spec = 0; | |
1157 std::vector<const EventListener*> listeners = | |
1158 GetMatchingListeners(browser_context, extension_info_map, | |
1159 web_request::OnErrorOccurred::kEventName, request, | |
1160 &extra_info_spec); | |
1161 if (listeners.empty()) | |
1162 return; | |
1163 | |
1164 base::ListValue args; | |
1165 base::DictionaryValue* dict = new base::DictionaryValue(); | |
1166 ExtractRequestInfo(request, dict); | |
1167 if (started) { | |
1168 std::string response_ip = request->GetSocketAddress().host(); | |
1169 if (!response_ip.empty()) | |
1170 dict->SetString(keys::kIpKey, response_ip); | |
1171 } | |
1172 dict->SetBoolean(keys::kFromCache, request->was_cached()); | |
1173 dict->SetString(keys::kErrorKey, | |
1174 net::ErrorToString(request->status().error())); | |
1175 args.Append(dict); | |
1176 | |
1177 DispatchEvent(browser_context, request, listeners, args); | |
1178 } | |
1179 | |
1180 void ExtensionWebRequestEventRouter::OnURLRequestDestroyed( | |
1181 void* browser_context, net::URLRequest* request) { | |
1182 ClearPendingCallbacks(request); | |
1183 | |
1184 signaled_requests_.erase(request->identifier()); | |
1185 | |
1186 request_time_tracker_->LogRequestEndTime(request->identifier(), | |
1187 base::Time::Now()); | |
1188 } | |
1189 | |
1190 void ExtensionWebRequestEventRouter::ClearPendingCallbacks( | |
1191 net::URLRequest* request) { | |
1192 blocked_requests_.erase(request->identifier()); | |
1193 } | |
1194 | |
1195 bool ExtensionWebRequestEventRouter::DispatchEvent( | |
1196 void* browser_context, | |
1197 net::URLRequest* request, | |
1198 const std::vector<const EventListener*>& listeners, | |
1199 const base::ListValue& args) { | |
1200 // TODO(mpcomplete): Consider consolidating common (extension_id,json_args) | |
1201 // pairs into a single message sent to a list of sub_event_names. | |
1202 int num_handlers_blocking = 0; | |
1203 for (std::vector<const EventListener*>::const_iterator it = listeners.begin(); | |
1204 it != listeners.end(); ++it) { | |
1205 // Filter out the optional keys that this listener didn't request. | |
1206 scoped_ptr<base::ListValue> args_filtered(args.DeepCopy()); | |
1207 base::DictionaryValue* dict = NULL; | |
1208 CHECK(args_filtered->GetDictionary(0, &dict) && dict); | |
1209 if (!((*it)->extra_info_spec & ExtraInfoSpec::REQUEST_HEADERS)) | |
1210 dict->Remove(keys::kRequestHeadersKey, NULL); | |
1211 if (!((*it)->extra_info_spec & ExtraInfoSpec::RESPONSE_HEADERS)) | |
1212 dict->Remove(keys::kResponseHeadersKey, NULL); | |
1213 | |
1214 extensions::EventRouter::DispatchEvent( | |
1215 (*it)->ipc_sender.get(), browser_context, | |
1216 (*it)->extension_id, (*it)->sub_event_name, | |
1217 args_filtered.Pass(), | |
1218 extensions::EventRouter::USER_GESTURE_UNKNOWN, | |
1219 extensions::EventFilteringInfo()); | |
1220 if ((*it)->extra_info_spec & | |
1221 (ExtraInfoSpec::BLOCKING | ExtraInfoSpec::ASYNC_BLOCKING)) { | |
1222 (*it)->blocked_requests.insert(request->identifier()); | |
1223 // If this is the first delegate blocking the request, go ahead and log | |
1224 // it. | |
1225 if (num_handlers_blocking == 0) { | |
1226 std::string delegate_info = | |
1227 l10n_util::GetStringFUTF8(IDS_LOAD_STATE_PARAMETER_EXTENSION, | |
1228 base::UTF8ToUTF16((*it)->extension_name)); | |
1229 // LobAndReport allows extensions that block requests to be displayed in | |
1230 // the load status bar. | |
1231 request->LogAndReportBlockedBy(delegate_info.c_str()); | |
1232 } | |
1233 ++num_handlers_blocking; | |
1234 } | |
1235 } | |
1236 | |
1237 if (num_handlers_blocking > 0) { | |
1238 blocked_requests_[request->identifier()].request = request; | |
1239 blocked_requests_[request->identifier()].is_incognito |= | |
1240 IsIncognitoBrowserContext(browser_context); | |
1241 blocked_requests_[request->identifier()].num_handlers_blocking += | |
1242 num_handlers_blocking; | |
1243 blocked_requests_[request->identifier()].blocking_time = base::Time::Now(); | |
1244 | |
1245 return true; | |
1246 } | |
1247 | |
1248 return false; | |
1249 } | |
1250 | |
1251 void ExtensionWebRequestEventRouter::OnEventHandled( | |
1252 void* browser_context, | |
1253 const std::string& extension_id, | |
1254 const std::string& event_name, | |
1255 const std::string& sub_event_name, | |
1256 uint64 request_id, | |
1257 EventResponse* response) { | |
1258 EventListener listener; | |
1259 listener.extension_id = extension_id; | |
1260 listener.sub_event_name = sub_event_name; | |
1261 | |
1262 // The listener may have been removed (e.g. due to the process going away) | |
1263 // before we got here. | |
1264 std::set<EventListener>::iterator found = | |
1265 listeners_[browser_context][event_name].find(listener); | |
1266 if (found != listeners_[browser_context][event_name].end()) | |
1267 found->blocked_requests.erase(request_id); | |
1268 | |
1269 DecrementBlockCount( | |
1270 browser_context, extension_id, event_name, request_id, response); | |
1271 } | |
1272 | |
1273 bool ExtensionWebRequestEventRouter::AddEventListener( | |
1274 void* browser_context, | |
1275 const std::string& extension_id, | |
1276 const std::string& extension_name, | |
1277 const std::string& event_name, | |
1278 const std::string& sub_event_name, | |
1279 const RequestFilter& filter, | |
1280 int extra_info_spec, | |
1281 int embedder_process_id, | |
1282 int webview_instance_id, | |
1283 base::WeakPtr<IPC::Sender> ipc_sender) { | |
1284 if (!IsWebRequestEvent(event_name)) | |
1285 return false; | |
1286 | |
1287 EventListener listener; | |
1288 listener.extension_id = extension_id; | |
1289 listener.extension_name = extension_name; | |
1290 listener.sub_event_name = sub_event_name; | |
1291 listener.filter = filter; | |
1292 listener.extra_info_spec = extra_info_spec; | |
1293 listener.ipc_sender = ipc_sender; | |
1294 listener.embedder_process_id = embedder_process_id; | |
1295 listener.webview_instance_id = webview_instance_id; | |
1296 if (listener.webview_instance_id) { | |
1297 content::RecordAction( | |
1298 base::UserMetricsAction("WebView.WebRequest.AddListener")); | |
1299 } | |
1300 | |
1301 if (listeners_[browser_context][event_name].count(listener) != 0u) { | |
1302 // This is likely an abuse of the API by a malicious extension. | |
1303 return false; | |
1304 } | |
1305 listeners_[browser_context][event_name].insert(listener); | |
1306 return true; | |
1307 } | |
1308 | |
1309 void ExtensionWebRequestEventRouter::RemoveEventListener( | |
1310 void* browser_context, | |
1311 const std::string& extension_id, | |
1312 const std::string& sub_event_name) { | |
1313 std::string event_name = | |
1314 extensions::EventRouter::GetBaseEventName(sub_event_name); | |
1315 DCHECK(IsWebRequestEvent(event_name)); | |
1316 | |
1317 EventListener listener; | |
1318 listener.extension_id = extension_id; | |
1319 listener.sub_event_name = sub_event_name; | |
1320 | |
1321 // It's possible for AddEventListener to fail asynchronously. In that case, | |
1322 // the renderer believes the listener exists, while the browser does not. | |
1323 // Ignore a RemoveEventListener in that case. | |
1324 std::set<EventListener>::iterator found = | |
1325 listeners_[browser_context][event_name].find(listener); | |
1326 if (found == listeners_[browser_context][event_name].end()) | |
1327 return; | |
1328 | |
1329 CHECK_EQ(listeners_[browser_context][event_name].count(listener), 1u) << | |
1330 "extension=" << extension_id << " event=" << event_name; | |
1331 | |
1332 // Unblock any request that this event listener may have been blocking. | |
1333 for (std::set<uint64>::iterator it = found->blocked_requests.begin(); | |
1334 it != found->blocked_requests.end(); ++it) { | |
1335 DecrementBlockCount(browser_context, extension_id, event_name, *it, NULL); | |
1336 } | |
1337 | |
1338 listeners_[browser_context][event_name].erase(listener); | |
1339 | |
1340 helpers::ClearCacheOnNavigation(); | |
1341 } | |
1342 | |
1343 void ExtensionWebRequestEventRouter::RemoveWebViewEventListeners( | |
1344 void* browser_context, | |
1345 const std::string& extension_id, | |
1346 int embedder_process_id, | |
1347 int webview_instance_id) { | |
1348 // Iterate over all listeners of all WebRequest events to delete | |
1349 // any listeners that belong to the provided <webview>. | |
1350 ListenerMapForBrowserContext& map_for_browser_context = | |
1351 listeners_[browser_context]; | |
1352 for (ListenerMapForBrowserContext::iterator event_iter = | |
1353 map_for_browser_context.begin(); | |
1354 event_iter != map_for_browser_context.end(); ++event_iter) { | |
1355 std::vector<EventListener> listeners_to_delete; | |
1356 std::set<EventListener>& listeners = event_iter->second; | |
1357 for (std::set<EventListener>::iterator listener_iter = listeners.begin(); | |
1358 listener_iter != listeners.end(); ++listener_iter) { | |
1359 const EventListener& listener = *listener_iter; | |
1360 if (listener.embedder_process_id == embedder_process_id && | |
1361 listener.webview_instance_id == webview_instance_id) | |
1362 listeners_to_delete.push_back(listener); | |
1363 } | |
1364 for (size_t i = 0; i < listeners_to_delete.size(); ++i) { | |
1365 EventListener& listener = listeners_to_delete[i]; | |
1366 content::BrowserThread::PostTask( | |
1367 content::BrowserThread::UI, | |
1368 FROM_HERE, | |
1369 base::Bind(&RemoveEventListenerOnUI, | |
1370 browser_context, | |
1371 listener.sub_event_name, | |
1372 embedder_process_id, | |
1373 extension_id)); | |
1374 } | |
1375 } | |
1376 } | |
1377 | |
1378 void ExtensionWebRequestEventRouter::OnOTRBrowserContextCreated( | |
1379 void* original_browser_context, void* otr_browser_context) { | |
1380 cross_browser_context_map_[original_browser_context] = | |
1381 std::make_pair(false, otr_browser_context); | |
1382 cross_browser_context_map_[otr_browser_context] = | |
1383 std::make_pair(true, original_browser_context); | |
1384 } | |
1385 | |
1386 void ExtensionWebRequestEventRouter::OnOTRBrowserContextDestroyed( | |
1387 void* original_browser_context, void* otr_browser_context) { | |
1388 cross_browser_context_map_.erase(otr_browser_context); | |
1389 cross_browser_context_map_.erase(original_browser_context); | |
1390 } | |
1391 | |
1392 void ExtensionWebRequestEventRouter::AddCallbackForPageLoad( | |
1393 const base::Closure& callback) { | |
1394 callbacks_for_page_load_.push_back(callback); | |
1395 } | |
1396 | |
1397 bool ExtensionWebRequestEventRouter::IsPageLoad( | |
1398 net::URLRequest* request) const { | |
1399 bool is_main_frame = false; | |
1400 int frame_id = -1; | |
1401 bool parent_is_main_frame = false; | |
1402 int parent_frame_id = -1; | |
1403 int render_process_host_id = -1; | |
1404 int routing_id = -1; | |
1405 ResourceType resource_type = content::RESOURCE_TYPE_LAST_TYPE; | |
1406 | |
1407 ExtractRequestInfoDetails(request, &is_main_frame, &frame_id, | |
1408 &parent_is_main_frame, &parent_frame_id, | |
1409 &render_process_host_id, | |
1410 &routing_id, &resource_type); | |
1411 | |
1412 return resource_type == content::RESOURCE_TYPE_MAIN_FRAME; | |
1413 } | |
1414 | |
1415 void ExtensionWebRequestEventRouter::NotifyPageLoad() { | |
1416 for (CallbacksForPageLoad::const_iterator i = | |
1417 callbacks_for_page_load_.begin(); | |
1418 i != callbacks_for_page_load_.end(); ++i) { | |
1419 i->Run(); | |
1420 } | |
1421 callbacks_for_page_load_.clear(); | |
1422 } | |
1423 | |
1424 void* ExtensionWebRequestEventRouter::GetCrossBrowserContext( | |
1425 void* browser_context) const { | |
1426 CrossBrowserContextMap::const_iterator cross_browser_context = | |
1427 cross_browser_context_map_.find(browser_context); | |
1428 if (cross_browser_context == cross_browser_context_map_.end()) | |
1429 return NULL; | |
1430 return cross_browser_context->second.second; | |
1431 } | |
1432 | |
1433 bool ExtensionWebRequestEventRouter::IsIncognitoBrowserContext( | |
1434 void* browser_context) const { | |
1435 CrossBrowserContextMap::const_iterator cross_browser_context = | |
1436 cross_browser_context_map_.find(browser_context); | |
1437 if (cross_browser_context == cross_browser_context_map_.end()) | |
1438 return false; | |
1439 return cross_browser_context->second.first; | |
1440 } | |
1441 | |
1442 bool ExtensionWebRequestEventRouter::WasSignaled( | |
1443 const net::URLRequest& request) const { | |
1444 SignaledRequestMap::const_iterator flag = | |
1445 signaled_requests_.find(request.identifier()); | |
1446 return (flag != signaled_requests_.end()) && (flag->second != 0); | |
1447 } | |
1448 | |
1449 void ExtensionWebRequestEventRouter::GetMatchingListenersImpl( | |
1450 void* browser_context, | |
1451 net::URLRequest* request, | |
1452 InfoMap* extension_info_map, | |
1453 bool crosses_incognito, | |
1454 const std::string& event_name, | |
1455 const GURL& url, | |
1456 int render_process_host_id, | |
1457 int routing_id, | |
1458 ResourceType resource_type, | |
1459 bool is_async_request, | |
1460 bool is_request_from_extension, | |
1461 int* extra_info_spec, | |
1462 std::vector<const ExtensionWebRequestEventRouter::EventListener*>* | |
1463 matching_listeners) { | |
1464 std::string web_request_event_name(event_name); | |
1465 extensions::WebViewRendererState::WebViewInfo web_view_info; | |
1466 bool is_web_view_guest = extensions::WebViewRendererState::GetInstance()-> | |
1467 GetInfo(render_process_host_id, routing_id, &web_view_info); | |
1468 if (is_web_view_guest) { | |
1469 web_request_event_name.replace( | |
1470 0, sizeof(kWebRequestEventPrefix) - 1, webview::kWebViewEventPrefix); | |
1471 } | |
1472 | |
1473 std::set<EventListener>& listeners = | |
1474 listeners_[browser_context][web_request_event_name]; | |
1475 for (std::set<EventListener>::iterator it = listeners.begin(); | |
1476 it != listeners.end(); ++it) { | |
1477 if (!it->ipc_sender.get()) { | |
1478 // The IPC sender has been deleted. This listener will be removed soon | |
1479 // via a call to RemoveEventListener. For now, just skip it. | |
1480 continue; | |
1481 } | |
1482 | |
1483 if (is_web_view_guest && | |
1484 (it->embedder_process_id != web_view_info.embedder_process_id || | |
1485 it->webview_instance_id != web_view_info.instance_id)) | |
1486 continue; | |
1487 | |
1488 if (!it->filter.urls.is_empty() && !it->filter.urls.MatchesURL(url)) | |
1489 continue; | |
1490 if (web_request_event_router_delegate_ && | |
1491 web_request_event_router_delegate_->OnGetMatchingListenersImplCheck( | |
1492 it->filter.tab_id, it->filter.window_id, request)) | |
1493 continue; | |
1494 if (!it->filter.types.empty() && | |
1495 std::find(it->filter.types.begin(), it->filter.types.end(), | |
1496 resource_type) == it->filter.types.end()) | |
1497 continue; | |
1498 | |
1499 if (!is_web_view_guest && !WebRequestPermissions::CanExtensionAccessURL( | |
1500 extension_info_map, it->extension_id, url, crosses_incognito, | |
1501 WebRequestPermissions::REQUIRE_HOST_PERMISSION)) | |
1502 continue; | |
1503 | |
1504 bool blocking_listener = | |
1505 (it->extra_info_spec & | |
1506 (ExtraInfoSpec::BLOCKING | ExtraInfoSpec::ASYNC_BLOCKING)) != 0; | |
1507 | |
1508 // We do not want to notify extensions about XHR requests that are | |
1509 // triggered by themselves. This is a workaround to prevent deadlocks | |
1510 // in case of synchronous XHR requests that block the extension renderer | |
1511 // and therefore prevent the extension from processing the request | |
1512 // handler. This is only a problem for blocking listeners. | |
1513 // http://crbug.com/105656 | |
1514 bool synchronous_xhr_from_extension = | |
1515 !is_async_request && is_request_from_extension && | |
1516 resource_type == content::RESOURCE_TYPE_XHR; | |
1517 | |
1518 // Only send webRequest events for URLs the extension has access to. | |
1519 if (blocking_listener && synchronous_xhr_from_extension) | |
1520 continue; | |
1521 | |
1522 matching_listeners->push_back(&(*it)); | |
1523 *extra_info_spec |= it->extra_info_spec; | |
1524 } | |
1525 } | |
1526 | |
1527 std::vector<const ExtensionWebRequestEventRouter::EventListener*> | |
1528 ExtensionWebRequestEventRouter::GetMatchingListeners( | |
1529 void* browser_context, | |
1530 InfoMap* extension_info_map, | |
1531 const std::string& event_name, | |
1532 net::URLRequest* request, | |
1533 int* extra_info_spec) { | |
1534 // TODO(mpcomplete): handle browser_context == NULL (should collect all | |
1535 // listeners). | |
1536 *extra_info_spec = 0; | |
1537 | |
1538 bool is_main_frame = false; | |
1539 int frame_id = -1; | |
1540 bool parent_is_main_frame = false; | |
1541 int parent_frame_id = -1; | |
1542 int render_process_host_id = -1; | |
1543 int routing_id = -1; | |
1544 ResourceType resource_type = content::RESOURCE_TYPE_LAST_TYPE; | |
1545 const GURL& url = request->url(); | |
1546 | |
1547 ExtractRequestInfoDetails(request, &is_main_frame, &frame_id, | |
1548 &parent_is_main_frame, &parent_frame_id, | |
1549 &render_process_host_id, | |
1550 &routing_id, &resource_type); | |
1551 | |
1552 std::vector<const ExtensionWebRequestEventRouter::EventListener*> | |
1553 matching_listeners; | |
1554 | |
1555 bool is_request_from_extension = | |
1556 IsRequestFromExtension(request, extension_info_map); | |
1557 | |
1558 const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request); | |
1559 // We are conservative here and assume requests are asynchronous in case | |
1560 // we don't have an info object. We don't want to risk a deadlock. | |
1561 bool is_async_request = !info || info->IsAsync(); | |
1562 | |
1563 GetMatchingListenersImpl( | |
1564 browser_context, request, extension_info_map, false, event_name, | |
1565 url, render_process_host_id, routing_id, resource_type, | |
1566 is_async_request, is_request_from_extension, extra_info_spec, | |
1567 &matching_listeners); | |
1568 void* cross_browser_context = GetCrossBrowserContext(browser_context); | |
1569 if (cross_browser_context) { | |
1570 GetMatchingListenersImpl( | |
1571 cross_browser_context, request, extension_info_map, true, event_name, | |
1572 url, render_process_host_id, routing_id, resource_type, | |
1573 is_async_request, is_request_from_extension, extra_info_spec, | |
1574 &matching_listeners); | |
1575 } | |
1576 | |
1577 return matching_listeners; | |
1578 } | |
1579 | |
1580 namespace { | |
1581 | |
1582 helpers::EventResponseDelta* CalculateDelta( | |
1583 ExtensionWebRequestEventRouter::BlockedRequest* blocked_request, | |
1584 ExtensionWebRequestEventRouter::EventResponse* response) { | |
1585 switch (blocked_request->event) { | |
1586 case ExtensionWebRequestEventRouter::kOnBeforeRequest: | |
1587 return helpers::CalculateOnBeforeRequestDelta( | |
1588 response->extension_id, response->extension_install_time, | |
1589 response->cancel, response->new_url); | |
1590 case ExtensionWebRequestEventRouter::kOnBeforeSendHeaders: { | |
1591 net::HttpRequestHeaders* old_headers = blocked_request->request_headers; | |
1592 net::HttpRequestHeaders* new_headers = response->request_headers.get(); | |
1593 return helpers::CalculateOnBeforeSendHeadersDelta( | |
1594 response->extension_id, response->extension_install_time, | |
1595 response->cancel, old_headers, new_headers); | |
1596 } | |
1597 case ExtensionWebRequestEventRouter::kOnHeadersReceived: { | |
1598 const net::HttpResponseHeaders* old_headers = | |
1599 blocked_request->original_response_headers.get(); | |
1600 helpers::ResponseHeaders* new_headers = | |
1601 response->response_headers.get(); | |
1602 return helpers::CalculateOnHeadersReceivedDelta( | |
1603 response->extension_id, | |
1604 response->extension_install_time, | |
1605 response->cancel, | |
1606 response->new_url, | |
1607 old_headers, | |
1608 new_headers); | |
1609 } | |
1610 case ExtensionWebRequestEventRouter::kOnAuthRequired: | |
1611 return helpers::CalculateOnAuthRequiredDelta( | |
1612 response->extension_id, response->extension_install_time, | |
1613 response->cancel, &response->auth_credentials); | |
1614 default: | |
1615 NOTREACHED(); | |
1616 break; | |
1617 } | |
1618 return NULL; | |
1619 } | |
1620 | |
1621 base::Value* SerializeResponseHeaders(const helpers::ResponseHeaders& headers) { | |
1622 scoped_ptr<base::ListValue> serialized_headers(new base::ListValue()); | |
1623 for (helpers::ResponseHeaders::const_iterator i = headers.begin(); | |
1624 i != headers.end(); ++i) { | |
1625 serialized_headers->Append( | |
1626 helpers::CreateHeaderDictionary(i->first, i->second)); | |
1627 } | |
1628 return serialized_headers.release(); | |
1629 } | |
1630 | |
1631 // Convert a RequestCookieModifications/ResponseCookieModifications object to a | |
1632 // base::ListValue which summarizes the changes made. This is templated since | |
1633 // the two types (request/response) are different but contain essentially the | |
1634 // same fields. | |
1635 template<typename CookieType> | |
1636 base::ListValue* SummarizeCookieModifications( | |
1637 const std::vector<linked_ptr<CookieType> >& modifications) { | |
1638 scoped_ptr<base::ListValue> cookie_modifications(new base::ListValue()); | |
1639 for (typename std::vector<linked_ptr<CookieType> >::const_iterator i = | |
1640 modifications.begin(); | |
1641 i != modifications.end(); ++i) { | |
1642 scoped_ptr<base::DictionaryValue> summary(new base::DictionaryValue()); | |
1643 const CookieType& mod = *i->get(); | |
1644 switch (mod.type) { | |
1645 case helpers::ADD: | |
1646 summary->SetString(activitylog::kCookieModificationTypeKey, | |
1647 activitylog::kCookieModificationAdd); | |
1648 break; | |
1649 case helpers::EDIT: | |
1650 summary->SetString(activitylog::kCookieModificationTypeKey, | |
1651 activitylog::kCookieModificationEdit); | |
1652 break; | |
1653 case helpers::REMOVE: | |
1654 summary->SetString(activitylog::kCookieModificationTypeKey, | |
1655 activitylog::kCookieModificationRemove); | |
1656 break; | |
1657 } | |
1658 if (mod.filter) { | |
1659 if (mod.filter->name) | |
1660 summary->SetString(activitylog::kCookieFilterNameKey, | |
1661 *mod.modification->name); | |
1662 if (mod.filter->domain) | |
1663 summary->SetString(activitylog::kCookieFilterDomainKey, | |
1664 *mod.modification->name); | |
1665 } | |
1666 if (mod.modification) { | |
1667 if (mod.modification->name) | |
1668 summary->SetString(activitylog::kCookieModDomainKey, | |
1669 *mod.modification->name); | |
1670 if (mod.modification->domain) | |
1671 summary->SetString(activitylog::kCookieModDomainKey, | |
1672 *mod.modification->name); | |
1673 } | |
1674 cookie_modifications->Append(summary.release()); | |
1675 } | |
1676 return cookie_modifications.release(); | |
1677 } | |
1678 | |
1679 // Converts an EventResponseDelta object to a dictionary value suitable for the | |
1680 // activity log. | |
1681 scoped_ptr<base::DictionaryValue> SummarizeResponseDelta( | |
1682 const std::string& event_name, | |
1683 const helpers::EventResponseDelta& delta) { | |
1684 scoped_ptr<base::DictionaryValue> details(new base::DictionaryValue()); | |
1685 if (delta.cancel) { | |
1686 details->SetBoolean(activitylog::kCancelKey, true); | |
1687 } | |
1688 if (!delta.new_url.is_empty()) { | |
1689 details->SetString(activitylog::kNewUrlKey, delta.new_url.spec()); | |
1690 } | |
1691 | |
1692 scoped_ptr<base::ListValue> modified_headers(new base::ListValue()); | |
1693 net::HttpRequestHeaders::Iterator iter(delta.modified_request_headers); | |
1694 while (iter.GetNext()) { | |
1695 modified_headers->Append( | |
1696 helpers::CreateHeaderDictionary(iter.name(), iter.value())); | |
1697 } | |
1698 if (!modified_headers->empty()) { | |
1699 details->Set(activitylog::kModifiedRequestHeadersKey, | |
1700 modified_headers.release()); | |
1701 } | |
1702 | |
1703 scoped_ptr<base::ListValue> deleted_headers(new base::ListValue()); | |
1704 deleted_headers->AppendStrings(delta.deleted_request_headers); | |
1705 if (!deleted_headers->empty()) { | |
1706 details->Set(activitylog::kDeletedRequestHeadersKey, | |
1707 deleted_headers.release()); | |
1708 } | |
1709 | |
1710 if (!delta.added_response_headers.empty()) { | |
1711 details->Set(activitylog::kAddedRequestHeadersKey, | |
1712 SerializeResponseHeaders(delta.added_response_headers)); | |
1713 } | |
1714 if (!delta.deleted_response_headers.empty()) { | |
1715 details->Set(activitylog::kDeletedResponseHeadersKey, | |
1716 SerializeResponseHeaders(delta.deleted_response_headers)); | |
1717 } | |
1718 if (delta.auth_credentials) { | |
1719 details->SetString(activitylog::kAuthCredentialsKey, | |
1720 base::UTF16ToUTF8( | |
1721 delta.auth_credentials->username()) + ":*"); | |
1722 } | |
1723 | |
1724 if (!delta.response_cookie_modifications.empty()) { | |
1725 details->Set( | |
1726 activitylog::kResponseCookieModificationsKey, | |
1727 SummarizeCookieModifications(delta.response_cookie_modifications)); | |
1728 } | |
1729 | |
1730 return details.Pass(); | |
1731 } | |
1732 | |
1733 } // namespace | |
1734 | |
1735 void ExtensionWebRequestEventRouter::LogExtensionActivity( | |
1736 void* browser_context_id, | |
1737 bool is_incognito, | |
1738 const std::string& extension_id, | |
1739 const GURL& url, | |
1740 const std::string& api_call, | |
1741 scoped_ptr<base::DictionaryValue> details) { | |
1742 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { | |
1743 BrowserThread::PostTask( | |
1744 BrowserThread::UI, | |
1745 FROM_HERE, | |
1746 base::Bind(&ExtensionWebRequestEventRouter::LogExtensionActivity, | |
1747 base::Unretained(this), | |
1748 browser_context_id, | |
1749 is_incognito, | |
1750 extension_id, | |
1751 url, | |
1752 api_call, | |
1753 base::Passed(&details))); | |
1754 } else { | |
1755 if (web_request_event_router_delegate_) { | |
1756 web_request_event_router_delegate_->LogExtensionActivity( | |
1757 reinterpret_cast<content::BrowserContext*>(browser_context_id), | |
1758 is_incognito, extension_id, url, api_call, details.Pass()); | |
1759 } | |
1760 } | |
1761 } | |
1762 | |
1763 void ExtensionWebRequestEventRouter::DecrementBlockCount( | |
1764 void* browser_context, | |
1765 const std::string& extension_id, | |
1766 const std::string& event_name, | |
1767 uint64 request_id, | |
1768 EventResponse* response) { | |
1769 scoped_ptr<EventResponse> response_scoped(response); | |
1770 | |
1771 // It's possible that this request was deleted, or cancelled by a previous | |
1772 // event handler. If so, ignore this response. | |
1773 if (blocked_requests_.find(request_id) == blocked_requests_.end()) | |
1774 return; | |
1775 | |
1776 BlockedRequest& blocked_request = blocked_requests_[request_id]; | |
1777 int num_handlers_blocking = --blocked_request.num_handlers_blocking; | |
1778 CHECK_GE(num_handlers_blocking, 0); | |
1779 | |
1780 if (response) { | |
1781 helpers::EventResponseDelta* delta = | |
1782 CalculateDelta(&blocked_request, response); | |
1783 | |
1784 LogExtensionActivity(browser_context, | |
1785 blocked_request.is_incognito, | |
1786 extension_id, | |
1787 blocked_request.request->url(), | |
1788 event_name, | |
1789 SummarizeResponseDelta(event_name, *delta)); | |
1790 | |
1791 blocked_request.response_deltas.push_back( | |
1792 linked_ptr<helpers::EventResponseDelta>(delta)); | |
1793 } | |
1794 | |
1795 base::TimeDelta block_time = | |
1796 base::Time::Now() - blocked_request.blocking_time; | |
1797 if (!extension_id.empty()) { | |
1798 request_time_tracker_->IncrementExtensionBlockTime( | |
1799 extension_id, request_id, block_time); | |
1800 } else { | |
1801 // |extension_id| is empty for requests blocked on startup waiting for the | |
1802 // declarative rules to be read from disk. | |
1803 UMA_HISTOGRAM_TIMES("Extensions.NetworkDelayStartup", block_time); | |
1804 } | |
1805 | |
1806 if (num_handlers_blocking == 0) { | |
1807 blocked_request.request->LogUnblocked(); | |
1808 ExecuteDeltas(browser_context, request_id, true); | |
1809 } else { | |
1810 // Update the URLRequest to make sure it's tagged with an extension that's | |
1811 // still blocking it. This may end up being the same extension as before. | |
1812 std::set<EventListener>& listeners = | |
1813 listeners_[browser_context][event_name]; | |
1814 | |
1815 for (std::set<EventListener>::iterator it = listeners.begin(); | |
1816 it != listeners.end(); ++it) { | |
1817 if (it->blocked_requests.count(request_id) == 0) | |
1818 continue; | |
1819 std::string delegate_info = | |
1820 l10n_util::GetStringFUTF8(IDS_LOAD_STATE_PARAMETER_EXTENSION, | |
1821 base::UTF8ToUTF16(it->extension_name)); | |
1822 blocked_request.request->LogAndReportBlockedBy(delegate_info.c_str()); | |
1823 break; | |
1824 } | |
1825 } | |
1826 } | |
1827 | |
1828 void ExtensionWebRequestEventRouter::SendMessages( | |
1829 void* browser_context, | |
1830 const BlockedRequest& blocked_request) { | |
1831 const helpers::EventResponseDeltas& deltas = blocked_request.response_deltas; | |
1832 for (helpers::EventResponseDeltas::const_iterator delta = deltas.begin(); | |
1833 delta != deltas.end(); ++delta) { | |
1834 const std::set<std::string>& messages = (*delta)->messages_to_extension; | |
1835 for (std::set<std::string>::const_iterator message = messages.begin(); | |
1836 message != messages.end(); ++message) { | |
1837 scoped_ptr<base::DictionaryValue> argument(new base::DictionaryValue); | |
1838 ExtractRequestInfo(blocked_request.request, argument.get()); | |
1839 extensions::WebViewRendererState::WebViewInfo web_view_info; | |
1840 bool is_web_view_guest = GetWebViewInfo(blocked_request.request, | |
1841 &web_view_info); | |
1842 argument->SetString(keys::kMessageKey, *message); | |
1843 argument->SetString(keys::kStageKey, | |
1844 GetRequestStageAsString(blocked_request.event)); | |
1845 | |
1846 BrowserThread::PostTask( | |
1847 BrowserThread::UI, | |
1848 FROM_HERE, | |
1849 base::Bind(&SendOnMessageEventOnUI, | |
1850 browser_context, | |
1851 (*delta)->extension_id, | |
1852 is_web_view_guest, | |
1853 web_view_info, | |
1854 base::Passed(&argument))); | |
1855 } | |
1856 } | |
1857 } | |
1858 | |
1859 int ExtensionWebRequestEventRouter::ExecuteDeltas( | |
1860 void* browser_context, | |
1861 uint64 request_id, | |
1862 bool call_callback) { | |
1863 BlockedRequest& blocked_request = blocked_requests_[request_id]; | |
1864 CHECK(blocked_request.num_handlers_blocking == 0); | |
1865 helpers::EventResponseDeltas& deltas = blocked_request.response_deltas; | |
1866 base::TimeDelta block_time = | |
1867 base::Time::Now() - blocked_request.blocking_time; | |
1868 request_time_tracker_->IncrementTotalBlockTime(request_id, block_time); | |
1869 | |
1870 bool credentials_set = false; | |
1871 | |
1872 deltas.sort(&helpers::InDecreasingExtensionInstallationTimeOrder); | |
1873 WarningSet warnings; | |
1874 | |
1875 bool canceled = false; | |
1876 helpers::MergeCancelOfResponses( | |
1877 blocked_request.response_deltas, | |
1878 &canceled, | |
1879 blocked_request.net_log); | |
1880 | |
1881 if (blocked_request.event == kOnBeforeRequest) { | |
1882 CHECK(!blocked_request.callback.is_null()); | |
1883 helpers::MergeOnBeforeRequestResponses( | |
1884 blocked_request.response_deltas, | |
1885 blocked_request.new_url, | |
1886 &warnings, | |
1887 blocked_request.net_log); | |
1888 } else if (blocked_request.event == kOnBeforeSendHeaders) { | |
1889 CHECK(!blocked_request.callback.is_null()); | |
1890 helpers::MergeOnBeforeSendHeadersResponses( | |
1891 blocked_request.response_deltas, | |
1892 blocked_request.request_headers, | |
1893 &warnings, | |
1894 blocked_request.net_log); | |
1895 } else if (blocked_request.event == kOnHeadersReceived) { | |
1896 CHECK(!blocked_request.callback.is_null()); | |
1897 helpers::MergeOnHeadersReceivedResponses( | |
1898 blocked_request.response_deltas, | |
1899 blocked_request.original_response_headers.get(), | |
1900 blocked_request.override_response_headers, | |
1901 blocked_request.new_url, | |
1902 &warnings, | |
1903 blocked_request.net_log); | |
1904 } else if (blocked_request.event == kOnAuthRequired) { | |
1905 CHECK(blocked_request.callback.is_null()); | |
1906 CHECK(!blocked_request.auth_callback.is_null()); | |
1907 credentials_set = helpers::MergeOnAuthRequiredResponses( | |
1908 blocked_request.response_deltas, | |
1909 blocked_request.auth_credentials, | |
1910 &warnings, | |
1911 blocked_request.net_log); | |
1912 } else { | |
1913 NOTREACHED(); | |
1914 } | |
1915 | |
1916 SendMessages(browser_context, blocked_request); | |
1917 | |
1918 if (!warnings.empty()) { | |
1919 BrowserThread::PostTask( | |
1920 BrowserThread::UI, | |
1921 FROM_HERE, | |
1922 base::Bind(&WarningService::NotifyWarningsOnUI, | |
1923 browser_context, warnings)); | |
1924 } | |
1925 | |
1926 if (canceled) { | |
1927 request_time_tracker_->SetRequestCanceled(request_id); | |
1928 } else if (blocked_request.new_url && | |
1929 !blocked_request.new_url->is_empty()) { | |
1930 request_time_tracker_->SetRequestRedirected(request_id); | |
1931 } | |
1932 | |
1933 // This triggers onErrorOccurred if canceled is true. | |
1934 int rv = canceled ? net::ERR_BLOCKED_BY_CLIENT : net::OK; | |
1935 | |
1936 if (!blocked_request.callback.is_null()) { | |
1937 net::CompletionCallback callback = blocked_request.callback; | |
1938 // Ensure that request is removed before callback because the callback | |
1939 // might trigger the next event. | |
1940 blocked_requests_.erase(request_id); | |
1941 if (call_callback) | |
1942 callback.Run(rv); | |
1943 } else if (!blocked_request.auth_callback.is_null()) { | |
1944 net::NetworkDelegate::AuthRequiredResponse response = | |
1945 net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION; | |
1946 if (canceled) { | |
1947 response = net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_CANCEL_AUTH; | |
1948 } else if (credentials_set) { | |
1949 response = net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_SET_AUTH; | |
1950 } | |
1951 net::NetworkDelegate::AuthCallback callback = blocked_request.auth_callback; | |
1952 blocked_requests_.erase(request_id); | |
1953 if (call_callback) | |
1954 callback.Run(response); | |
1955 } else { | |
1956 blocked_requests_.erase(request_id); | |
1957 } | |
1958 return rv; | |
1959 } | |
1960 | |
1961 bool ExtensionWebRequestEventRouter::ProcessDeclarativeRules( | |
1962 void* browser_context, | |
1963 InfoMap* extension_info_map, | |
1964 const std::string& event_name, | |
1965 net::URLRequest* request, | |
1966 extensions::RequestStage request_stage, | |
1967 const net::HttpResponseHeaders* original_response_headers) { | |
1968 extensions::WebViewRendererState::WebViewInfo web_view_info; | |
1969 bool is_web_view_guest = GetWebViewInfo(request, &web_view_info); | |
1970 | |
1971 extensions::RulesRegistry::WebViewKey webview_key( | |
1972 is_web_view_guest ? web_view_info.embedder_process_id : 0, | |
1973 is_web_view_guest ? web_view_info.instance_id : 0); | |
1974 RulesRegistryKey rules_key(browser_context, webview_key); | |
1975 // If this check fails, check that the active stages are up-to-date in | |
1976 // extensions/browser/api/declarative_webrequest/request_stage.h . | |
1977 DCHECK(request_stage & extensions::kActiveStages); | |
1978 | |
1979 // Rules of the current |browser_context| may apply but we need to check also | |
1980 // whether there are applicable rules from extensions whose background page | |
1981 // spans from regular to incognito mode. | |
1982 | |
1983 // First parameter identifies the registry, the second indicates whether the | |
1984 // registry belongs to the cross browser_context. | |
1985 typedef std::pair<extensions::WebRequestRulesRegistry*, bool> | |
1986 RelevantRegistry; | |
1987 typedef std::vector<RelevantRegistry> RelevantRegistries; | |
1988 RelevantRegistries relevant_registries; | |
1989 | |
1990 if (rules_registries_.find(rules_key) != rules_registries_.end()) { | |
1991 relevant_registries.push_back( | |
1992 std::make_pair(rules_registries_[rules_key].get(), false)); | |
1993 } | |
1994 | |
1995 void* cross_browser_context = GetCrossBrowserContext(browser_context); | |
1996 RulesRegistryKey cross_browser_context_rules_key( | |
1997 cross_browser_context, webview_key); | |
1998 if (cross_browser_context && | |
1999 rules_registries_.find(cross_browser_context_rules_key) != | |
2000 rules_registries_.end()) { | |
2001 relevant_registries.push_back( | |
2002 std::make_pair( | |
2003 rules_registries_[cross_browser_context_rules_key].get(), true)); | |
2004 } | |
2005 | |
2006 // The following block is experimentally enabled and its impact on load time | |
2007 // logged with UMA Extensions.NetworkDelayRegistryLoad. crbug.com/175961 | |
2008 for (RelevantRegistries::iterator i = relevant_registries.begin(); | |
2009 i != relevant_registries.end(); ++i) { | |
2010 extensions::WebRequestRulesRegistry* rules_registry = i->first; | |
2011 if (!rules_registry->ready().is_signaled()) { | |
2012 // The rules registry is still loading. Block this request until it | |
2013 // finishes. | |
2014 rules_registry->ready().Post( | |
2015 FROM_HERE, | |
2016 base::Bind(&ExtensionWebRequestEventRouter::OnRulesRegistryReady, | |
2017 AsWeakPtr(), | |
2018 browser_context, | |
2019 event_name, | |
2020 request->identifier(), | |
2021 request_stage)); | |
2022 blocked_requests_[request->identifier()].num_handlers_blocking++; | |
2023 blocked_requests_[request->identifier()].request = request; | |
2024 blocked_requests_[request->identifier()].is_incognito |= | |
2025 IsIncognitoBrowserContext(browser_context); | |
2026 blocked_requests_[request->identifier()].blocking_time = | |
2027 base::Time::Now(); | |
2028 blocked_requests_[request->identifier()].original_response_headers = | |
2029 original_response_headers; | |
2030 blocked_requests_[request->identifier()].extension_info_map = | |
2031 extension_info_map; | |
2032 return true; | |
2033 } | |
2034 } | |
2035 | |
2036 base::Time start = base::Time::Now(); | |
2037 | |
2038 bool deltas_created = false; | |
2039 for (RelevantRegistries::iterator i = relevant_registries.begin(); | |
2040 i != relevant_registries.end(); ++i) { | |
2041 extensions::WebRequestRulesRegistry* rules_registry = | |
2042 i->first; | |
2043 helpers::EventResponseDeltas result = | |
2044 rules_registry->CreateDeltas( | |
2045 extension_info_map, | |
2046 extensions::WebRequestData( | |
2047 request, request_stage, original_response_headers), | |
2048 i->second); | |
2049 | |
2050 if (!result.empty()) { | |
2051 helpers::EventResponseDeltas& deltas = | |
2052 blocked_requests_[request->identifier()].response_deltas; | |
2053 deltas.insert(deltas.end(), result.begin(), result.end()); | |
2054 deltas_created = true; | |
2055 } | |
2056 } | |
2057 | |
2058 base::TimeDelta elapsed_time = start - base::Time::Now(); | |
2059 UMA_HISTOGRAM_TIMES("Extensions.DeclarativeWebRequestNetworkDelay", | |
2060 elapsed_time); | |
2061 | |
2062 return deltas_created; | |
2063 } | |
2064 | |
2065 void ExtensionWebRequestEventRouter::OnRulesRegistryReady( | |
2066 void* browser_context, | |
2067 const std::string& event_name, | |
2068 uint64 request_id, | |
2069 extensions::RequestStage request_stage) { | |
2070 // It's possible that this request was deleted, or cancelled by a previous | |
2071 // event handler. If so, ignore this response. | |
2072 if (blocked_requests_.find(request_id) == blocked_requests_.end()) | |
2073 return; | |
2074 | |
2075 BlockedRequest& blocked_request = blocked_requests_[request_id]; | |
2076 base::TimeDelta block_time = | |
2077 base::Time::Now() - blocked_request.blocking_time; | |
2078 UMA_HISTOGRAM_TIMES("Extensions.NetworkDelayRegistryLoad", block_time); | |
2079 | |
2080 ProcessDeclarativeRules(browser_context, | |
2081 blocked_request.extension_info_map, | |
2082 event_name, | |
2083 blocked_request.request, | |
2084 request_stage, | |
2085 blocked_request.original_response_headers.get()); | |
2086 // Reset to NULL so that nobody relies on this being set. | |
2087 blocked_request.extension_info_map = NULL; | |
2088 DecrementBlockCount( | |
2089 browser_context, std::string(), event_name, request_id, NULL); | |
2090 } | |
2091 | |
2092 bool ExtensionWebRequestEventRouter::GetAndSetSignaled(uint64 request_id, | |
2093 EventTypes event_type) { | |
2094 SignaledRequestMap::iterator iter = signaled_requests_.find(request_id); | |
2095 if (iter == signaled_requests_.end()) { | |
2096 signaled_requests_[request_id] = event_type; | |
2097 return false; | |
2098 } | |
2099 bool was_signaled_before = (iter->second & event_type) != 0; | |
2100 iter->second |= event_type; | |
2101 return was_signaled_before; | |
2102 } | |
2103 | |
2104 void ExtensionWebRequestEventRouter::ClearSignaled(uint64 request_id, | |
2105 EventTypes event_type) { | |
2106 SignaledRequestMap::iterator iter = signaled_requests_.find(request_id); | |
2107 if (iter == signaled_requests_.end()) | |
2108 return; | |
2109 iter->second &= ~event_type; | |
2110 } | |
2111 | |
2112 // Special QuotaLimitHeuristic for WebRequestHandlerBehaviorChangedFunction. | |
2113 // | |
2114 // Each call of webRequest.handlerBehaviorChanged() clears the in-memory cache | |
2115 // of WebKit at the time of the next page load (top level navigation event). | |
2116 // This quota heuristic is intended to limit the number of times the cache is | |
2117 // cleared by an extension. | |
2118 // | |
2119 // As we want to account for the number of times the cache is really cleared | |
2120 // (opposed to the number of times webRequest.handlerBehaviorChanged() is | |
2121 // called), we cannot decide whether a call of | |
2122 // webRequest.handlerBehaviorChanged() should trigger a quota violation at the | |
2123 // time it is called. Instead we only decrement the bucket counter at the time | |
2124 // when the cache is cleared (when page loads happen). | |
2125 class ClearCacheQuotaHeuristic : public extensions::QuotaLimitHeuristic { | |
2126 public: | |
2127 ClearCacheQuotaHeuristic(const Config& config, BucketMapper* map) | |
2128 : QuotaLimitHeuristic( | |
2129 config, | |
2130 map, | |
2131 "MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES"), | |
2132 callback_registered_(false), | |
2133 weak_ptr_factory_(this) {} | |
2134 virtual ~ClearCacheQuotaHeuristic() {} | |
2135 virtual bool Apply(Bucket* bucket, | |
2136 const base::TimeTicks& event_time) OVERRIDE; | |
2137 | |
2138 private: | |
2139 // Callback that is triggered by the ExtensionWebRequestEventRouter on a page | |
2140 // load. | |
2141 // | |
2142 // We don't need to take care of the life time of |bucket|: It is owned by the | |
2143 // BucketMapper of our base class in |QuotaLimitHeuristic::bucket_mapper_|. As | |
2144 // long as |this| exists, the respective BucketMapper and its bucket will | |
2145 // exist as well. | |
2146 void OnPageLoad(Bucket* bucket); | |
2147 | |
2148 // Flag to prevent that we register more than one call back in-between | |
2149 // clearing the cache. | |
2150 bool callback_registered_; | |
2151 | |
2152 base::WeakPtrFactory<ClearCacheQuotaHeuristic> weak_ptr_factory_; | |
2153 | |
2154 DISALLOW_COPY_AND_ASSIGN(ClearCacheQuotaHeuristic); | |
2155 }; | |
2156 | |
2157 bool ClearCacheQuotaHeuristic::Apply(Bucket* bucket, | |
2158 const base::TimeTicks& event_time) { | |
2159 if (event_time > bucket->expiration()) | |
2160 bucket->Reset(config(), event_time); | |
2161 | |
2162 // Call bucket->DeductToken() on a new page load, this is when | |
2163 // webRequest.handlerBehaviorChanged() clears the cache. | |
2164 if (!callback_registered_) { | |
2165 ExtensionWebRequestEventRouter::GetInstance()->AddCallbackForPageLoad( | |
2166 base::Bind(&ClearCacheQuotaHeuristic::OnPageLoad, | |
2167 weak_ptr_factory_.GetWeakPtr(), | |
2168 bucket)); | |
2169 callback_registered_ = true; | |
2170 } | |
2171 | |
2172 // We only check whether tokens are left here. Deducting a token happens in | |
2173 // OnPageLoad(). | |
2174 return bucket->has_tokens(); | |
2175 } | |
2176 | |
2177 void ClearCacheQuotaHeuristic::OnPageLoad(Bucket* bucket) { | |
2178 callback_registered_ = false; | |
2179 bucket->DeductToken(); | |
2180 } | |
2181 | |
2182 bool WebRequestInternalAddEventListenerFunction::RunSync() { | |
2183 // Argument 0 is the callback, which we don't use here. | |
2184 ExtensionWebRequestEventRouter::RequestFilter filter; | |
2185 base::DictionaryValue* value = NULL; | |
2186 error_.clear(); | |
2187 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &value)); | |
2188 // Failure + an empty error string means a fatal error. | |
2189 EXTENSION_FUNCTION_VALIDATE(filter.InitFromValue(*value, &error_) || | |
2190 !error_.empty()); | |
2191 if (!error_.empty()) | |
2192 return false; | |
2193 | |
2194 int extra_info_spec = 0; | |
2195 if (HasOptionalArgument(2)) { | |
2196 base::ListValue* value = NULL; | |
2197 EXTENSION_FUNCTION_VALIDATE(args_->GetList(2, &value)); | |
2198 EXTENSION_FUNCTION_VALIDATE( | |
2199 ExtensionWebRequestEventRouter::ExtraInfoSpec::InitFromValue( | |
2200 *value, &extra_info_spec)); | |
2201 } | |
2202 | |
2203 std::string event_name; | |
2204 EXTENSION_FUNCTION_VALIDATE(args_->GetString(3, &event_name)); | |
2205 | |
2206 std::string sub_event_name; | |
2207 EXTENSION_FUNCTION_VALIDATE(args_->GetString(4, &sub_event_name)); | |
2208 | |
2209 int webview_instance_id = 0; | |
2210 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(5, &webview_instance_id)); | |
2211 | |
2212 base::WeakPtr<extensions::ExtensionMessageFilter> ipc_sender = | |
2213 ipc_sender_weak(); | |
2214 int embedder_process_id = | |
2215 ipc_sender.get() ? ipc_sender->render_process_id() : -1; | |
2216 | |
2217 const Extension* extension = | |
2218 extension_info_map()->extensions().GetByID(extension_id()); | |
2219 std::string extension_name = extension ? extension->name() : extension_id(); | |
2220 | |
2221 bool is_web_view_guest = webview_instance_id != 0; | |
2222 // We check automatically whether the extension has the 'webRequest' | |
2223 // permission. For blocking calls we require the additional permission | |
2224 // 'webRequestBlocking'. | |
2225 if ((!is_web_view_guest && | |
2226 extra_info_spec & | |
2227 (ExtensionWebRequestEventRouter::ExtraInfoSpec::BLOCKING | | |
2228 ExtensionWebRequestEventRouter::ExtraInfoSpec::ASYNC_BLOCKING)) && | |
2229 !extension->permissions_data()->HasAPIPermission( | |
2230 extensions::APIPermission::kWebRequestBlocking)) { | |
2231 error_ = keys::kBlockingPermissionRequired; | |
2232 return false; | |
2233 } | |
2234 | |
2235 // We allow to subscribe to patterns that are broader than the host | |
2236 // permissions. E.g., we could subscribe to http://www.example.com/* | |
2237 // while having host permissions for http://www.example.com/foo/* and | |
2238 // http://www.example.com/bar/*. | |
2239 // For this reason we do only a coarse check here to warn the extension | |
2240 // developer if he does something obviously wrong. | |
2241 if (!is_web_view_guest && | |
2242 extension->permissions_data()->GetEffectiveHostPermissions().is_empty()) { | |
2243 error_ = keys::kHostPermissionsRequired; | |
2244 return false; | |
2245 } | |
2246 | |
2247 bool success = | |
2248 ExtensionWebRequestEventRouter::GetInstance()->AddEventListener( | |
2249 profile_id(), extension_id(), extension_name, | |
2250 event_name, sub_event_name, filter, extra_info_spec, | |
2251 embedder_process_id, webview_instance_id, ipc_sender_weak()); | |
2252 EXTENSION_FUNCTION_VALIDATE(success); | |
2253 | |
2254 helpers::ClearCacheOnNavigation(); | |
2255 | |
2256 BrowserThread::PostTask(BrowserThread::UI, | |
2257 FROM_HERE, | |
2258 base::Bind(&helpers::NotifyWebRequestAPIUsed, | |
2259 profile_id(), | |
2260 make_scoped_refptr(extension))); | |
2261 | |
2262 return true; | |
2263 } | |
2264 | |
2265 void WebRequestInternalEventHandledFunction::RespondWithError( | |
2266 const std::string& event_name, | |
2267 const std::string& sub_event_name, | |
2268 uint64 request_id, | |
2269 scoped_ptr<ExtensionWebRequestEventRouter::EventResponse> response, | |
2270 const std::string& error) { | |
2271 error_ = error; | |
2272 ExtensionWebRequestEventRouter::GetInstance()->OnEventHandled( | |
2273 profile_id(), | |
2274 extension_id(), | |
2275 event_name, | |
2276 sub_event_name, | |
2277 request_id, | |
2278 response.release()); | |
2279 } | |
2280 | |
2281 bool WebRequestInternalEventHandledFunction::RunSync() { | |
2282 std::string event_name; | |
2283 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &event_name)); | |
2284 | |
2285 std::string sub_event_name; | |
2286 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &sub_event_name)); | |
2287 | |
2288 std::string request_id_str; | |
2289 EXTENSION_FUNCTION_VALIDATE(args_->GetString(2, &request_id_str)); | |
2290 uint64 request_id; | |
2291 EXTENSION_FUNCTION_VALIDATE(base::StringToUint64(request_id_str, | |
2292 &request_id)); | |
2293 | |
2294 scoped_ptr<ExtensionWebRequestEventRouter::EventResponse> response; | |
2295 if (HasOptionalArgument(3)) { | |
2296 base::DictionaryValue* value = NULL; | |
2297 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(3, &value)); | |
2298 | |
2299 if (!value->empty()) { | |
2300 base::Time install_time = | |
2301 extension_info_map()->GetInstallTime(extension_id()); | |
2302 response.reset(new ExtensionWebRequestEventRouter::EventResponse( | |
2303 extension_id(), install_time)); | |
2304 } | |
2305 | |
2306 if (value->HasKey("cancel")) { | |
2307 // Don't allow cancel mixed with other keys. | |
2308 if (value->size() != 1) { | |
2309 RespondWithError(event_name, | |
2310 sub_event_name, | |
2311 request_id, | |
2312 response.Pass(), | |
2313 keys::kInvalidBlockingResponse); | |
2314 return false; | |
2315 } | |
2316 | |
2317 bool cancel = false; | |
2318 EXTENSION_FUNCTION_VALIDATE(value->GetBoolean("cancel", &cancel)); | |
2319 response->cancel = cancel; | |
2320 } | |
2321 | |
2322 if (value->HasKey("redirectUrl")) { | |
2323 std::string new_url_str; | |
2324 EXTENSION_FUNCTION_VALIDATE(value->GetString("redirectUrl", | |
2325 &new_url_str)); | |
2326 response->new_url = GURL(new_url_str); | |
2327 if (!response->new_url.is_valid()) { | |
2328 RespondWithError(event_name, | |
2329 sub_event_name, | |
2330 request_id, | |
2331 response.Pass(), | |
2332 ErrorUtils::FormatErrorMessage( | |
2333 keys::kInvalidRedirectUrl, new_url_str)); | |
2334 return false; | |
2335 } | |
2336 } | |
2337 | |
2338 const bool hasRequestHeaders = value->HasKey("requestHeaders"); | |
2339 const bool hasResponseHeaders = value->HasKey("responseHeaders"); | |
2340 if (hasRequestHeaders || hasResponseHeaders) { | |
2341 if (hasRequestHeaders && hasResponseHeaders) { | |
2342 // Allow only one of the keys, not both. | |
2343 RespondWithError(event_name, | |
2344 sub_event_name, | |
2345 request_id, | |
2346 response.Pass(), | |
2347 keys::kInvalidHeaderKeyCombination); | |
2348 return false; | |
2349 } | |
2350 | |
2351 base::ListValue* headers_value = NULL; | |
2352 scoped_ptr<net::HttpRequestHeaders> request_headers; | |
2353 scoped_ptr<helpers::ResponseHeaders> response_headers; | |
2354 if (hasRequestHeaders) { | |
2355 request_headers.reset(new net::HttpRequestHeaders()); | |
2356 EXTENSION_FUNCTION_VALIDATE(value->GetList(keys::kRequestHeadersKey, | |
2357 &headers_value)); | |
2358 } else { | |
2359 response_headers.reset(new helpers::ResponseHeaders()); | |
2360 EXTENSION_FUNCTION_VALIDATE(value->GetList(keys::kResponseHeadersKey, | |
2361 &headers_value)); | |
2362 } | |
2363 | |
2364 for (size_t i = 0; i < headers_value->GetSize(); ++i) { | |
2365 base::DictionaryValue* header_value = NULL; | |
2366 std::string name; | |
2367 std::string value; | |
2368 EXTENSION_FUNCTION_VALIDATE( | |
2369 headers_value->GetDictionary(i, &header_value)); | |
2370 if (!FromHeaderDictionary(header_value, &name, &value)) { | |
2371 std::string serialized_header; | |
2372 base::JSONWriter::Write(header_value, &serialized_header); | |
2373 RespondWithError(event_name, | |
2374 sub_event_name, | |
2375 request_id, | |
2376 response.Pass(), | |
2377 ErrorUtils::FormatErrorMessage(keys::kInvalidHeader, | |
2378 serialized_header)); | |
2379 return false; | |
2380 } | |
2381 if (!net::HttpUtil::IsValidHeaderName(name)) { | |
2382 RespondWithError(event_name, | |
2383 sub_event_name, | |
2384 request_id, | |
2385 response.Pass(), | |
2386 keys::kInvalidHeaderName); | |
2387 return false; | |
2388 } | |
2389 if (!net::HttpUtil::IsValidHeaderValue(value)) { | |
2390 RespondWithError(event_name, | |
2391 sub_event_name, | |
2392 request_id, | |
2393 response.Pass(), | |
2394 ErrorUtils::FormatErrorMessage( | |
2395 keys::kInvalidHeaderValue, name)); | |
2396 return false; | |
2397 } | |
2398 if (hasRequestHeaders) | |
2399 request_headers->SetHeader(name, value); | |
2400 else | |
2401 response_headers->push_back(helpers::ResponseHeader(name, value)); | |
2402 } | |
2403 if (hasRequestHeaders) | |
2404 response->request_headers.reset(request_headers.release()); | |
2405 else | |
2406 response->response_headers.reset(response_headers.release()); | |
2407 } | |
2408 | |
2409 if (value->HasKey(keys::kAuthCredentialsKey)) { | |
2410 base::DictionaryValue* credentials_value = NULL; | |
2411 EXTENSION_FUNCTION_VALIDATE(value->GetDictionary( | |
2412 keys::kAuthCredentialsKey, | |
2413 &credentials_value)); | |
2414 base::string16 username; | |
2415 base::string16 password; | |
2416 EXTENSION_FUNCTION_VALIDATE( | |
2417 credentials_value->GetString(keys::kUsernameKey, &username)); | |
2418 EXTENSION_FUNCTION_VALIDATE( | |
2419 credentials_value->GetString(keys::kPasswordKey, &password)); | |
2420 response->auth_credentials.reset( | |
2421 new net::AuthCredentials(username, password)); | |
2422 } | |
2423 } | |
2424 | |
2425 ExtensionWebRequestEventRouter::GetInstance()->OnEventHandled( | |
2426 profile_id(), extension_id(), event_name, sub_event_name, request_id, | |
2427 response.release()); | |
2428 | |
2429 return true; | |
2430 } | |
2431 | |
2432 void WebRequestHandlerBehaviorChangedFunction::GetQuotaLimitHeuristics( | |
2433 extensions::QuotaLimitHeuristics* heuristics) const { | |
2434 extensions::QuotaLimitHeuristic::Config config = { | |
2435 // See web_request.json for current value. | |
2436 web_request::MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES, | |
2437 base::TimeDelta::FromMinutes(10)}; | |
2438 extensions::QuotaLimitHeuristic::BucketMapper* bucket_mapper = | |
2439 new extensions::QuotaLimitHeuristic::SingletonBucketMapper(); | |
2440 ClearCacheQuotaHeuristic* heuristic = | |
2441 new ClearCacheQuotaHeuristic(config, bucket_mapper); | |
2442 heuristics->push_back(heuristic); | |
2443 } | |
2444 | |
2445 void WebRequestHandlerBehaviorChangedFunction::OnQuotaExceeded( | |
2446 const std::string& violation_error) { | |
2447 // Post warning message. | |
2448 WarningSet warnings; | |
2449 warnings.insert( | |
2450 Warning::CreateRepeatedCacheFlushesWarning(extension_id())); | |
2451 BrowserThread::PostTask( | |
2452 BrowserThread::UI, | |
2453 FROM_HERE, | |
2454 base::Bind(&WarningService::NotifyWarningsOnUI, profile_id(), warnings)); | |
2455 | |
2456 // Continue gracefully. | |
2457 RunSync(); | |
2458 } | |
2459 | |
2460 bool WebRequestHandlerBehaviorChangedFunction::RunSync() { | |
2461 helpers::ClearCacheOnNavigation(); | |
2462 return true; | |
2463 } | |
OLD | NEW |