diff options
Diffstat (limited to 'chrome/browser/renderer_host/resource_dispatcher_host.cc')
-rw-r--r-- | chrome/browser/renderer_host/resource_dispatcher_host.cc | 1217 |
1 files changed, 0 insertions, 1217 deletions
diff --git a/chrome/browser/renderer_host/resource_dispatcher_host.cc b/chrome/browser/renderer_host/resource_dispatcher_host.cc deleted file mode 100644 index f746429..0000000 --- a/chrome/browser/renderer_host/resource_dispatcher_host.cc +++ /dev/null @@ -1,1217 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// See http://dev.chromium.org/developers/design-documents/multi-process-resource-loading - -#include <vector> - -#include "chrome/browser/renderer_host/resource_dispatcher_host.h" - -#include "base/message_loop.h" -#include "base/scoped_ptr.h" -#include "base/time.h" -#include "chrome/browser/cert_store.h" -#include "chrome/browser/cross_site_request_manager.h" -#include "chrome/browser/download/download_file.h" -#include "chrome/browser/download/download_manager.h" -#include "chrome/browser/download/download_request_manager.h" -#include "chrome/browser/download/save_file_manager.h" -#include "chrome/browser/external_protocol_handler.h" -#include "chrome/browser/login_prompt.h" -#include "chrome/browser/plugin_service.h" -#include "chrome/browser/render_view_host.h" -#include "chrome/browser/render_view_host_delegate.h" -#include "chrome/browser/renderer_host/async_resource_handler.h" -#include "chrome/browser/renderer_host/buffered_resource_handler.h" -#include "chrome/browser/renderer_host/cross_site_resource_handler.h" -#include "chrome/browser/renderer_host/download_resource_handler.h" -#include "chrome/browser/renderer_host/safe_browsing_resource_handler.h" -#include "chrome/browser/renderer_host/save_file_resource_handler.h" -#include "chrome/browser/renderer_host/sync_resource_handler.h" -#include "chrome/browser/renderer_security_policy.h" -#include "chrome/browser/resource_request_details.h" -#include "chrome/browser/safe_browsing/safe_browsing_service.h" -#include "chrome/browser/tab_util.h" -#include "chrome/browser/web_contents.h" -#include "chrome/common/notification_source.h" -#include "chrome/common/notification_types.h" -#include "chrome/common/render_messages.h" -#include "chrome/common/stl_util-inl.h" -#include "net/base/auth.h" -#include "net/base/cert_status_flags.h" -#include "net/base/load_flags.h" -#include "net/base/mime_util.h" -#include "net/base/net_errors.h" -#include "net/url_request/url_request.h" - -// Uncomment to enable logging of request traffic. -//#define LOG_RESOURCE_DISPATCHER_REQUESTS - -#ifdef LOG_RESOURCE_DISPATCHER_REQUESTS -# define RESOURCE_LOG(stuff) LOG(INFO) << stuff -#else -# define RESOURCE_LOG(stuff) -#endif - -using base::Time; -using base::TimeDelta; -using base::TimeTicks; - -// ---------------------------------------------------------------------------- - -// The interval for calls to ResourceDispatcherHost::UpdateLoadStates -static const int kUpdateLoadStatesIntervalMsec = 100; - -// Maximum number of pending data messages sent to the renderer at any -// given time for a given request. -static const int kMaxPendingDataMessages = 20; - -// A ShutdownTask proxies a shutdown task from the UI thread to the IO thread. -// It should be constructed on the UI thread and run in the IO thread. -class ResourceDispatcherHost::ShutdownTask : public Task { - public: - explicit ShutdownTask(ResourceDispatcherHost* resource_dispatcher_host) - : rdh_(resource_dispatcher_host) { } - - void Run() { - rdh_->OnShutdown(); - } - - private: - ResourceDispatcherHost* rdh_; -}; - -namespace { - -// Consults the RendererSecurity policy to determine whether the -// ResourceDispatcherHost should service this request. A request might be -// disallowed if the renderer is not authorized to restrive the request URL or -// if the renderer is attempting to upload an unauthorized file. -bool ShouldServiceRequest(int render_process_host_id, - const ViewHostMsg_Resource_Request& request_data) { - // TODO(mpcomplete): remove this when http://b/viewIssue?id=1080959 is fixed. - if (render_process_host_id == -1) - return true; - - RendererSecurityPolicy* policy = RendererSecurityPolicy::GetInstance(); - - // Check if the renderer is permitted to request the requested URL. - if (!policy->CanRequestURL(render_process_host_id, request_data.url)) { - LOG(INFO) << "Denied unauthorized request for " << - request_data.url.possibly_invalid_spec(); - return false; - } - - // Check if the renderer is permitted to upload the requested files. - const std::vector<net::UploadData::Element>& uploads = - request_data.upload_content; - std::vector<net::UploadData::Element>::const_iterator iter; - for (iter = uploads.begin(); iter != uploads.end(); ++iter) { - if (iter->type() == net::UploadData::TYPE_FILE && - !policy->CanUploadFile(render_process_host_id, iter->file_path())) { - NOTREACHED() << "Denied unauthorized upload of " << iter->file_path(); - return false; - } - } - - return true; -} - -} // namespace - -ResourceDispatcherHost::ResourceDispatcherHost(MessageLoop* io_loop) - : ui_loop_(MessageLoop::current()), - io_loop_(io_loop), - download_file_manager_(new DownloadFileManager(ui_loop_, this)), - download_request_manager_(new DownloadRequestManager(io_loop, ui_loop_)), - save_file_manager_(new SaveFileManager(ui_loop_, io_loop, this)), - safe_browsing_(new SafeBrowsingService), - request_id_(-1), - plugin_service_(PluginService::GetInstance()), - method_runner_(this), - is_shutdown_(false) { -} - -ResourceDispatcherHost::~ResourceDispatcherHost() { - AsyncResourceHandler::GlobalCleanup(); - STLDeleteValues(&pending_requests_); -} - -void ResourceDispatcherHost::Initialize() { - DCHECK(MessageLoop::current() == ui_loop_); - download_file_manager_->Initialize(); - safe_browsing_->Initialize(io_loop_); -} - -void ResourceDispatcherHost::Shutdown() { - DCHECK(MessageLoop::current() == ui_loop_); - io_loop_->PostTask(FROM_HERE, new ShutdownTask(this)); -} - -void ResourceDispatcherHost::OnShutdown() { - DCHECK(MessageLoop::current() == io_loop_); - is_shutdown_ = true; - STLDeleteValues(&pending_requests_); - // Make sure we shutdown the timer now, otherwise by the time our destructor - // runs if the timer is still running the Task is deleted twice (once by - // the MessageLoop and the second time by RepeatingTimer). - update_load_states_timer_.Stop(); -} - -bool ResourceDispatcherHost::HandleExternalProtocol(int request_id, - int render_process_host_id, - int tab_contents_id, - const GURL& url, - ResourceType::Type type, - ResourceHandler* handler) { - if (!ResourceType::IsFrame(type) || URLRequest::IsHandledURL(url)) - return false; - - ui_loop_->PostTask(FROM_HERE, NewRunnableFunction( - &ExternalProtocolHandler::LaunchUrl, url, render_process_host_id, - tab_contents_id)); - - handler->OnResponseCompleted(request_id, URLRequestStatus( - URLRequestStatus::FAILED, - net::ERR_ABORTED)); - return true; -} - -void ResourceDispatcherHost::BeginRequest( - Receiver* receiver, - HANDLE render_process_handle, - int render_process_host_id, - int render_view_id, - int request_id, - const ViewHostMsg_Resource_Request& request_data, - URLRequestContext* request_context, - IPC::Message* sync_result) { - if (is_shutdown_ || - !ShouldServiceRequest(render_process_host_id, request_data)) { - // Tell the renderer that this request was disallowed. - receiver->Send(new ViewMsg_Resource_RequestComplete( - render_view_id, - request_id, - URLRequestStatus(URLRequestStatus::FAILED, net::ERR_ABORTED))); - return; - } - - // Ensure the Chrome plugins are loaded, as they may intercept network - // requests. Does nothing if they are already loaded. - // TODO(mpcomplete): This takes 200 ms! Investigate parallelizing this by - // starting the load earlier in a BG thread. - plugin_service_->LoadChromePlugins(this); - - // Construct the event handler. - scoped_refptr<ResourceHandler> handler; - if (sync_result) { - handler = new SyncResourceHandler(receiver, request_data.url, sync_result); - } else { - handler = new AsyncResourceHandler(receiver, - render_process_host_id, - render_view_id, - render_process_handle, - request_data.url, - this); - } - - if (HandleExternalProtocol(request_id, render_process_host_id, render_view_id, - request_data.url, request_data.resource_type, - handler)) { - return; - } - - // Construct the request. - URLRequest* request = new URLRequest(request_data.url, this); - request->set_method(request_data.method); - request->set_policy_url(request_data.policy_url); - request->set_referrer(request_data.referrer.spec()); - request->SetExtraRequestHeaders(request_data.headers); - request->set_load_flags(request_data.load_flags); - request->set_context(request_context); - request->set_origin_pid(request_data.origin_pid); - - // Set upload data. - uint64 upload_size = 0; - if (!request_data.upload_content.empty()) { - scoped_refptr<net::UploadData> upload = new net::UploadData(); - upload->set_elements(request_data.upload_content); // Deep copy. - request->set_upload(upload); - upload_size = upload->GetContentLength(); - } - - // Install a CrossSiteResourceHandler if this request is coming from a - // RenderViewHost with a pending cross-site request. We only check this for - // MAIN_FRAME requests. - // TODO(mpcomplete): remove "render_process_host_id != -1" - // when http://b/viewIssue?id=1080959 is fixed. - if (request_data.resource_type == ResourceType::MAIN_FRAME && - render_process_host_id != -1 && - Singleton<CrossSiteRequestManager>::get()-> - HasPendingCrossSiteRequest(render_process_host_id, render_view_id)) { - // Wrap the event handler to be sure the current page's onunload handler - // has a chance to run before we render the new page. - handler = new CrossSiteResourceHandler(handler, - render_process_host_id, - render_view_id, - this); - } - - if (safe_browsing_->enabled() && - safe_browsing_->CanCheckUrl(request_data.url)) { - handler = new SafeBrowsingResourceHandler(handler, - render_process_host_id, - render_view_id, - request_data.url, - request_data.resource_type, - safe_browsing_, - this); - } - - // Insert a buffered event handler before the actual one. - handler = new BufferedResourceHandler(handler, this, request); - - // Make extra info and read footer (contains request ID). - ExtraRequestInfo* extra_info = - new ExtraRequestInfo(handler, - request_id, - render_process_host_id, - render_view_id, - request_data.mixed_content, - request_data.resource_type, - upload_size); - extra_info->allow_download = - ResourceType::IsFrame(request_data.resource_type); - request->set_user_data(extra_info); // takes pointer ownership - - BeginRequestInternal(request, request_data.mixed_content); -} - -// We are explicitly forcing the download of 'url'. -void ResourceDispatcherHost::BeginDownload(const GURL& url, - const GURL& referrer, - int render_process_host_id, - int render_view_id, - URLRequestContext* request_context) { - if (is_shutdown_) - return; - - // Check if the renderer is permitted to request the requested URL. - // - // TODO(mpcomplete): remove "render_process_host_id != -1" - // when http://b/viewIssue?id=1080959 is fixed. - if (render_process_host_id != -1 && - !RendererSecurityPolicy::GetInstance()-> - CanRequestURL(render_process_host_id, url)) { - LOG(INFO) << "Denied unauthorized download request for " << - url.possibly_invalid_spec(); - return; - } - - // Ensure the Chrome plugins are loaded, as they may intercept network - // requests. Does nothing if they are already loaded. - plugin_service_->LoadChromePlugins(this); - URLRequest* request = new URLRequest(url, this); - - request_id_--; - - scoped_refptr<ResourceHandler> handler = - new DownloadResourceHandler(this, - render_process_host_id, - render_view_id, - request_id_, - url.spec(), - download_file_manager_.get(), - request, - true); - - - if (safe_browsing_->enabled() && safe_browsing_->CanCheckUrl(url)) { - handler = new SafeBrowsingResourceHandler(handler, - render_process_host_id, - render_view_id, - url, - ResourceType::MAIN_FRAME, - safe_browsing_, - this); - } - - bool known_proto = URLRequest::IsHandledURL(url); - if (!known_proto) { - CHECK(false); - } - - request->set_method("GET"); - request->set_referrer(referrer.spec()); - request->set_context(request_context); - - ExtraRequestInfo* extra_info = - new ExtraRequestInfo(handler, - request_id_, - render_process_host_id, - render_view_id, - false, // Downloads are not considered mixed-content - ResourceType::SUB_RESOURCE, - 0 /* upload_size */ ); - extra_info->allow_download = true; - extra_info->is_download = true; - request->set_user_data(extra_info); // Takes pointer ownership. - - BeginRequestInternal(request, false); -} - -// This function is only used for saving feature. -void ResourceDispatcherHost::BeginSaveFile(const GURL& url, - const GURL& referrer, - int render_process_host_id, - int render_view_id, - URLRequestContext* request_context) { - if (is_shutdown_) - return; - - // Ensure the Chrome plugins are loaded, as they may intercept network - // requests. Does nothing if they are already loaded. - plugin_service_->LoadChromePlugins(this); - - scoped_refptr<ResourceHandler> handler = - new SaveFileResourceHandler(render_process_host_id, - render_view_id, - url.spec(), - save_file_manager_.get()); - request_id_--; - - bool known_proto = URLRequest::IsHandledURL(url); - if (!known_proto) { - // Since any URLs which have non-standard scheme have been filtered - // by save manager(see GURL::SchemeIsStandard). This situation - // should not happen. - NOTREACHED(); - return; - } - - URLRequest* request = new URLRequest(url, this); - request->set_method("GET"); - request->set_referrer(referrer.spec()); - // So far, for saving page, we need fetch content from cache, in the - // future, maybe we can use a configuration to configure this behavior. - request->set_load_flags(net::LOAD_ONLY_FROM_CACHE); - request->set_context(request_context); - - ExtraRequestInfo* extra_info = - new ExtraRequestInfo(handler, - request_id_, - render_process_host_id, - render_view_id, - false, - ResourceType::SUB_RESOURCE, - 0 /* upload_size */); - // Just saving some resources we need, disallow downloading. - extra_info->allow_download = false; - extra_info->is_download = false; - request->set_user_data(extra_info); // Takes pointer ownership. - - BeginRequestInternal(request, false); -} - -void ResourceDispatcherHost::CancelRequest(int render_process_host_id, - int request_id, - bool from_renderer) { - CancelRequest(render_process_host_id, request_id, from_renderer, true); -} - -void ResourceDispatcherHost::CancelRequest(int render_process_host_id, - int request_id, - bool from_renderer, - bool allow_delete) { - PendingRequestList::iterator i = pending_requests_.find( - GlobalRequestID(render_process_host_id, request_id)); - if (i == pending_requests_.end()) { - // We probably want to remove this warning eventually, but I wanted to be - // able to notice when this happens during initial development since it - // should be rare and may indicate a bug. - DLOG(WARNING) << "Canceling a request that wasn't found"; - return; - } - - // WebKit will send us a cancel for downloads since it no longer handles them. - // In this case, ignore the cancel since we handle downloads in the browser. - ExtraRequestInfo* info = ExtraInfoForRequest(i->second); - if (!from_renderer || !info->is_download) { - if (info->login_handler) { - info->login_handler->OnRequestCancelled(); - info->login_handler = NULL; - } - if (!i->second->is_pending() && allow_delete) { - // No io is pending, canceling the request won't notify us of anything, - // so we explicitly remove it. - // TODO: removing the request in this manner means we're not notifying - // anyone. We need make sure the event handlers and others are notified - // so that everything is cleaned up properly. - RemovePendingRequest(info->render_process_host_id, info->request_id); - } else { - i->second->Cancel(); - } - } - - // Do not remove from the pending requests, as the request will still - // call AllDataReceived, and may even have more data before it does - // that. -} - -void ResourceDispatcherHost::OnDataReceivedACK(int render_process_host_id, - int request_id) { - PendingRequestList::iterator i = pending_requests_.find( - GlobalRequestID(render_process_host_id, request_id)); - if (i == pending_requests_.end()) - return; - - ExtraRequestInfo* info = ExtraInfoForRequest(i->second); - - // Decrement the number of pending data messages. - info->pending_data_count--; - - // If the pending data count was higher than the max, resume the request. - if (info->pending_data_count == kMaxPendingDataMessages) { - // Decrement the pending data count one more time because we also - // incremented it before pausing the request. - info->pending_data_count--; - - // Resume the request. - PauseRequest(render_process_host_id, request_id, false); - } -} - -void ResourceDispatcherHost::OnUploadProgressACK(int render_process_host_id, - int request_id) { - PendingRequestList::iterator i = pending_requests_.find( - GlobalRequestID(render_process_host_id, request_id)); - if (i == pending_requests_.end()) - return; - - ExtraRequestInfo* info = ExtraInfoForRequest(i->second); - info->waiting_for_upload_progress_ack = false; -} - -bool ResourceDispatcherHost::WillSendData(int render_process_host_id, - int request_id) { - PendingRequestList::iterator i = pending_requests_.find( - GlobalRequestID(render_process_host_id, request_id)); - if (i == pending_requests_.end()) { - NOTREACHED() << L"WillSendData for invalid request"; - return false; - } - - ExtraRequestInfo* info = ExtraInfoForRequest(i->second); - - info->pending_data_count++; - if (info->pending_data_count > kMaxPendingDataMessages) { - // We reached the max number of data messages that can be sent to - // the renderer for a given request. Pause the request and wait for - // the renderer to start processing them before resuming it. - PauseRequest(render_process_host_id, request_id, true); - return false; - } - - return true; -} - -void ResourceDispatcherHost::PauseRequest(int render_process_host_id, - int request_id, - bool pause) { - GlobalRequestID global_id(render_process_host_id, request_id); - PendingRequestList::iterator i = pending_requests_.find(global_id); - if (i == pending_requests_.end()) { - DLOG(WARNING) << "Pausing a request that wasn't found"; - return; - } - - ExtraRequestInfo* info = ExtraInfoForRequest(i->second); - - int pause_count = info->pause_count + (pause ? 1 : -1); - if (pause_count < 0) { - NOTREACHED(); // Unbalanced call to pause. - return; - } - info->pause_count = pause_count; - - RESOURCE_LOG("To pause (" << pause << "): " << i->second->url().spec()); - - // If we're resuming, kick the request to start reading again. Run the read - // asynchronously to avoid recursion problems. - if (info->pause_count == 0) { - MessageLoop::current()->PostTask(FROM_HERE, - method_runner_.NewRunnableMethod( - &ResourceDispatcherHost::ResumeRequest, global_id)); - } -} - -void ResourceDispatcherHost::OnClosePageACK(int render_process_host_id, - int request_id) { - GlobalRequestID global_id(render_process_host_id, request_id); - PendingRequestList::iterator i = pending_requests_.find(global_id); - if (i == pending_requests_.end()) { - // If there are no matching pending requests, then this is not a - // cross-site navigation and we are just closing the tab/browser. - ui_loop_->PostTask(FROM_HERE, NewRunnableFunction( - &RenderViewHost::ClosePageIgnoringUnloadEvents, - render_process_host_id, - request_id)); - return; - } - - ExtraRequestInfo* info = ExtraInfoForRequest(i->second); - if (info->cross_site_handler) { - info->cross_site_handler->ResumeResponse(); - } -} - -// The object died, so cancel and detach all requests associated with it except -// for downloads, which belong to the browser process even if initiated via a -// renderer. -void ResourceDispatcherHost::CancelRequestsForProcess( - int render_process_host_id) { - CancelRequestsForRenderView(render_process_host_id, -1 /* cancel all */); -} - -void ResourceDispatcherHost::CancelRequestsForRenderView( - int render_process_host_id, - int render_view_id) { - // Since pending_requests_ is a map, we first build up a list of all of the - // matching requests to be cancelled, and then we cancel them. Since there - // may be more than one request to cancel, we cannot simply hold onto the map - // iterators found in the first loop. - - // Find the global ID of all matching elements. - std::vector<GlobalRequestID> matching_requests; - for (PendingRequestList::const_iterator i = pending_requests_.begin(); - i != pending_requests_.end(); ++i) { - if (i->first.render_process_host_id == render_process_host_id) { - ExtraRequestInfo* info = ExtraInfoForRequest(i->second); - if (!info->is_download && (render_view_id == -1 || - render_view_id == info->render_view_id)) { - matching_requests.push_back( - GlobalRequestID(render_process_host_id, i->first.request_id)); - } - } - } - - // Remove matches. - for (size_t i = 0; i < matching_requests.size(); ++i) { - PendingRequestList::iterator iter = - pending_requests_.find(matching_requests[i]); - // Although every matching request was in pending_requests_ when we built - // matching_requests, it is normal for a matching request to be not found - // in pending_requests_ after we have removed some matching requests from - // pending_requests_. For example, deleting a URLRequest that has - // exclusive (write) access to an HTTP cache entry may unblock another - // URLRequest that needs exclusive access to the same cache entry, and - // that URLRequest may complete and remove itself from pending_requests_. - // So we need to check that iter is not equal to pending_requests_.end(). - if (iter != pending_requests_.end()) - RemovePendingRequest(iter); - } -} - -// Cancels the request and removes it from the list. -void ResourceDispatcherHost::RemovePendingRequest(int render_process_host_id, - int request_id) { - PendingRequestList::iterator i = pending_requests_.find( - GlobalRequestID(render_process_host_id, request_id)); - if (i == pending_requests_.end()) { - NOTREACHED() << "Trying to remove a request that's not here"; - return; - } - RemovePendingRequest(i); -} - -void ResourceDispatcherHost::RemovePendingRequest( - const PendingRequestList::iterator& iter) { - // Notify the login handler that this request object is going away. - ExtraRequestInfo* info = ExtraInfoForRequest(iter->second); - if (info && info->login_handler) - info->login_handler->OnRequestCancelled(); - - delete iter->second; - pending_requests_.erase(iter); - - // If we have no more pending requests, then stop the load state monitor - if (pending_requests_.empty()) - update_load_states_timer_.Stop(); -} - -// URLRequest::Delegate ------------------------------------------------------- - -void ResourceDispatcherHost::OnReceivedRedirect(URLRequest* request, - const GURL& new_url) { - RESOURCE_LOG("OnReceivedRedirect: " << request->url().spec()); - ExtraRequestInfo* info = ExtraInfoForRequest(request); - - DCHECK(request->status().is_success()); - - // TODO(mpcomplete): remove this when http://b/viewIssue?id=1080959 is fixed. - if (info->render_process_host_id != -1 && - !RendererSecurityPolicy::GetInstance()-> - CanRequestURL(info->render_process_host_id, new_url)) { - LOG(INFO) << "Denied unauthorized request for " << - new_url.possibly_invalid_spec(); - - // Tell the renderer that this request was disallowed. - CancelRequest(info->render_process_host_id, info->request_id, false); - return; - } - - NofityReceivedRedirect(request, info->render_process_host_id, new_url); - - if (HandleExternalProtocol(info->request_id, info->render_process_host_id, - info->render_view_id, new_url, - info->resource_type, info->resource_handler)) { - // The request is complete so we can remove it. - RemovePendingRequest(info->render_process_host_id, info->request_id); - return; - } - - if (!info->resource_handler->OnRequestRedirected(info->request_id, new_url)) - CancelRequest(info->render_process_host_id, info->request_id, false); -} - -void ResourceDispatcherHost::OnAuthRequired( - URLRequest* request, - net::AuthChallengeInfo* auth_info) { - // Create a login dialog on the UI thread to get authentication data, - // or pull from cache and continue on the IO thread. - // TODO(mpcomplete): We should block the parent tab while waiting for - // authentication. - // That would also solve the problem of the URLRequest being cancelled - // before we receive authentication. - ExtraRequestInfo* info = ExtraInfoForRequest(request); - DCHECK(!info->login_handler) << - "OnAuthRequired called with login_handler pending"; - info->login_handler = CreateLoginPrompt(auth_info, request, ui_loop_); -} - -void ResourceDispatcherHost::OnSSLCertificateError( - URLRequest* request, - int cert_error, - net::X509Certificate* cert) { - DCHECK(request); - SSLManager::OnSSLCertificateError(this, request, cert_error, cert, ui_loop_); -} - -void ResourceDispatcherHost::OnResponseStarted(URLRequest* request) { - RESOURCE_LOG("OnResponseStarted: " << request->url().spec()); - ExtraRequestInfo* info = ExtraInfoForRequest(request); - if (PauseRequestIfNeeded(info)) { - RESOURCE_LOG("OnResponseStarted pausing: " << request->url().spec()); - return; - } - - if (request->status().is_success()) { - // We want to send a final upload progress message prior to sending - // the response complete message even if we're waiting for an ack to - // to a previous upload progress message. - info->waiting_for_upload_progress_ack = false; - MaybeUpdateUploadProgress(info, request); - - if (!CompleteResponseStarted(request)) { - CancelRequest(info->render_process_host_id, info->request_id, false); - } else { - // Start reading. - int bytes_read = 0; - if (Read(request, &bytes_read)) { - OnReadCompleted(request, bytes_read); - } else if (!request->status().is_io_pending()) { - DCHECK(!info->is_paused); - // If the error is not an IO pending, then we're done reading. - OnResponseCompleted(request); - } - } - } else { - OnResponseCompleted(request); - } -} - -bool ResourceDispatcherHost::CompleteResponseStarted(URLRequest* request) { - ExtraRequestInfo* info = ExtraInfoForRequest(request); - - scoped_refptr<ResourceResponse> response(new ResourceResponse); - - response->response_head.status = request->status(); - response->response_head.request_time = request->request_time(); - response->response_head.response_time = request->response_time(); - response->response_head.headers = request->response_headers(); - request->GetCharset(&response->response_head.charset); - response->response_head.filter_policy = info->filter_policy; - response->response_head.content_length = request->GetExpectedContentSize(); - request->GetMimeType(&response->response_head.mime_type); - - if (request->ssl_info().cert) { - int cert_id = - CertStore::GetSharedInstance()->StoreCert( - request->ssl_info().cert, - info->render_process_host_id); - int cert_status = request->ssl_info().cert_status; - // EV certificate verification could be expensive. We don't want to spend - // time performing EV certificate verification on all resources because - // EV status is irrelevant to sub-frames and sub-resources. So we call - // IsEV here rather than in the network layer because the network layer - // doesn't know the resource type. - if (info->resource_type == ResourceType::MAIN_FRAME && - request->ssl_info().cert->IsEV(cert_status)) - cert_status |= net::CERT_STATUS_IS_EV; - - response->response_head.security_info = - SSLManager::SerializeSecurityInfo(cert_id, - cert_status, - request->ssl_info().security_bits); - } else { - // We should not have any SSL state. - DCHECK(!request->ssl_info().cert_status && - (request->ssl_info().security_bits == -1 || - request->ssl_info().security_bits == 0)); - } - - NotifyResponseStarted(request, info->render_process_host_id); - return info->resource_handler->OnResponseStarted(info->request_id, - response.get()); -} - -void ResourceDispatcherHost::BeginRequestInternal(URLRequest* request, - bool mixed_content) { - ExtraRequestInfo* info = ExtraInfoForRequest(request); - GlobalRequestID global_id(info->render_process_host_id, info->request_id); - pending_requests_[global_id] = request; - if (mixed_content) { - // We don't start the request in that case. The SSLManager will potentially - // change the request (potentially to indicate its content should be - // filtered) and start it itself. - SSLManager::OnMixedContentRequest(this, request, ui_loop_); - return; - } - request->Start(); - - // Make sure we have the load state monitor running - if (!update_load_states_timer_.IsRunning()) { - update_load_states_timer_.Start( - TimeDelta::FromMilliseconds(kUpdateLoadStatesIntervalMsec), - this, &ResourceDispatcherHost::UpdateLoadStates); - } -} - -// This test mirrors the decision that WebKit makes in -// WebFrameLoaderClient::dispatchDecidePolicyForMIMEType. -// static. -bool ResourceDispatcherHost::ShouldDownload( - const std::string& mime_type, const std::string& content_disposition) { - std::string type = StringToLowerASCII(mime_type); - std::string disposition = StringToLowerASCII(content_disposition); - - // First, examine content-disposition. - if (!disposition.empty()) { - bool should_download = true; - - // Some broken sites just send ... - // Content-Disposition: ; filename="file" - // ... screen those out here. - if (disposition[0] == ';') - should_download = false; - - if (disposition.compare(0, 6, "inline") == 0) - should_download = false; - - // Some broken sites just send ... - // Content-Disposition: filename="file" - // ... without a disposition token... Screen those out. - if (disposition.compare(0, 8, "filename") == 0) - should_download = false; - - // Also in use is Content-Disposition: name="file" - if (disposition.compare(0, 4, "name") == 0) - should_download = false; - - // We have a content-disposition of "attachment" or unknown. - // RFC 2183, section 2.8 says that an unknown disposition - // value should be treated as "attachment". - if (should_download) - return true; - } - - // MIME type checking. - if (net::IsSupportedMimeType(type)) - return false; - - // Finally, check the plugin service. - bool allow_wildcard = false; - return !plugin_service_->HavePluginFor(type, allow_wildcard); -} - -bool ResourceDispatcherHost::PauseRequestIfNeeded(ExtraRequestInfo* info) { - if (info->pause_count > 0) - info->is_paused = true; - - return info->is_paused; -} - -void ResourceDispatcherHost::ResumeRequest(const GlobalRequestID& request_id) { - PendingRequestList::iterator i = pending_requests_.find(request_id); - if (i == pending_requests_.end()) // The request may have been destroyed - return; - - URLRequest* request = i->second; - ExtraRequestInfo* info = ExtraInfoForRequest(request); - if (!info->is_paused) - return; - - RESOURCE_LOG("Resuming: " << i->second->url().spec()); - - info->is_paused = false; - - if (info->has_started_reading) - OnReadCompleted(i->second, info->paused_read_bytes); - else - OnResponseStarted(i->second); -} - -bool ResourceDispatcherHost::Read(URLRequest* request, int* bytes_read) { - ExtraRequestInfo* info = ExtraInfoForRequest(request); - DCHECK(!info->is_paused); - - char* buf; - int buf_size; - if (!info->resource_handler->OnWillRead(info->request_id, - &buf, &buf_size, -1)) { - return false; - } - - DCHECK(buf); - DCHECK(buf_size > 0); - - info->has_started_reading = true; - return request->Read(buf, buf_size, bytes_read); -} - -void ResourceDispatcherHost::OnReadCompleted(URLRequest* request, - int bytes_read) { - DCHECK(request); - RESOURCE_LOG("OnReadCompleted: " << request->url().spec()); - ExtraRequestInfo* info = ExtraInfoForRequest(request); - if (PauseRequestIfNeeded(info)) { - info->paused_read_bytes = bytes_read; - RESOURCE_LOG("OnReadCompleted pausing: " << request->url().spec()); - return; - } - - if (request->status().is_success() && CompleteRead(request, &bytes_read)) { - // The request can be paused if we realize that the renderer is not - // servicing messages fast enough. - if (info->pause_count == 0 && - Read(request, &bytes_read) && - request->status().is_success()) { - if (bytes_read == 0) { - CompleteRead(request, &bytes_read); - } else { - // Force the next CompleteRead / Read pair to run as a separate task. - // This avoids a fast, large network request from monopolizing the IO - // thread and starving other IO operations from running. - info->paused_read_bytes = bytes_read; - info->is_paused = true; - GlobalRequestID id(info->render_process_host_id, info->request_id); - MessageLoop::current()->PostTask( - FROM_HERE, - method_runner_.NewRunnableMethod( - &ResourceDispatcherHost::ResumeRequest, id)); - return; - } - } - } - - if (PauseRequestIfNeeded(info)) { - info->paused_read_bytes = bytes_read; - RESOURCE_LOG("OnReadCompleted (CompleteRead) pausing: " << - request->url().spec()); - return; - } - - // If the status is not IO pending then we've either finished (success) or we - // had an error. Either way, we're done! - if (!request->status().is_io_pending()) - OnResponseCompleted(request); -} - -bool ResourceDispatcherHost::CompleteRead(URLRequest* request, - int* bytes_read) { - if (!request->status().is_success()) { - NOTREACHED(); - return false; - } - - ExtraRequestInfo* info = ExtraInfoForRequest(request); - - if (!info->resource_handler->OnReadCompleted(info->request_id, bytes_read)) { - // Pass in false as the last arg to indicate we don't want |request| - // deleted. We do this as callers of us assume |request| is valid after we - // return. - CancelRequest(info->render_process_host_id, info->request_id, false, false); - return false; - } - - return *bytes_read != 0; -} - -void ResourceDispatcherHost::OnResponseCompleted(URLRequest* request) { - RESOURCE_LOG("OnResponseCompleted: " << request->url().spec()); - ExtraRequestInfo* info = ExtraInfoForRequest(request); - - if (info->resource_handler->OnResponseCompleted(info->request_id, - request->status())) { - NotifyResponseCompleted(request, info->render_process_host_id); - - // The request is complete so we can remove it. - RemovePendingRequest(info->render_process_host_id, info->request_id); - } - // If the handler's OnResponseCompleted returns false, we are deferring the - // call until later. We will notify the world and clean up when we resume. -} - -void ResourceDispatcherHost::AddObserver(Observer* obs) { - observer_list_.AddObserver(obs); -} - -void ResourceDispatcherHost::RemoveObserver(Observer* obs) { - observer_list_.RemoveObserver(obs); -} - -URLRequest* ResourceDispatcherHost::GetURLRequest( - GlobalRequestID request_id) const { - // This should be running in the IO loop. io_loop_ can be NULL during the - // unit_tests. - DCHECK(MessageLoop::current() == io_loop_ && io_loop_); - - PendingRequestList::const_iterator i = pending_requests_.find(request_id); - if (i == pending_requests_.end()) - return NULL; - - return i->second; -} - -// A NotificationTask proxies a resource dispatcher notification from the IO -// thread to the UI thread. It should be constructed on the IO thread and run -// in the UI thread. Takes ownership of |details|. -class NotificationTask : public Task { - public: - NotificationTask(NotificationType type, - URLRequest* request, - ResourceRequestDetails* details) - : type_(type), - details_(details) { - if (!tab_util::GetTabContentsID(request, - &render_process_host_id_, - &tab_contents_id_)) - NOTREACHED(); - } - - void Run() { - // Find the tab associated with this request. - TabContents* tab_contents = - tab_util::GetWebContentsByID(render_process_host_id_, tab_contents_id_); - - if (tab_contents) { - // Issue the notification. - NotificationService::current()-> - Notify(type_, - Source<NavigationController>(tab_contents->controller()), - Details<ResourceRequestDetails>(details_.get())); - } - } - - private: - // These IDs let us find the correct tab on the UI thread. - int render_process_host_id_; - int tab_contents_id_; - - // The type and details of the notification. - NotificationType type_; - scoped_ptr<ResourceRequestDetails> details_; -}; - -static int GetCertID(URLRequest* request, int render_process_host_id) { - if (request->ssl_info().cert) { - return CertStore::GetSharedInstance()->StoreCert(request->ssl_info().cert, - render_process_host_id); - } - // If there is no SSL info attached to this request, we must either be a non - // secure request, or the request has been canceled or failed (before the SSL - // info was populated), or the response is an error (we have seen 403, 404, - // and 501) made up by the proxy. - DCHECK(!request->url().SchemeIsSecure() || - (request->status().status() == URLRequestStatus::CANCELED) || - (request->status().status() == URLRequestStatus::FAILED) || - ((request->response_headers()->response_code() >= 400) && - (request->response_headers()->response_code() <= 599))); - return 0; -} - -void ResourceDispatcherHost::NotifyResponseStarted(URLRequest* request, - int render_process_host_id) { - // Notify the observers on the IO thread. - FOR_EACH_OBSERVER(Observer, observer_list_, OnRequestStarted(this, request)); - - // Notify the observers on the UI thread. - ui_loop_->PostTask(FROM_HERE, - new NotificationTask(NOTIFY_RESOURCE_RESPONSE_STARTED, request, - new ResourceRequestDetails(request, - GetCertID(request, render_process_host_id)))); -} - -void ResourceDispatcherHost::NotifyResponseCompleted( - URLRequest* request, - int render_process_host_id) { - // Notify the observers on the IO thread. - FOR_EACH_OBSERVER(Observer, observer_list_, - OnResponseCompleted(this, request)); - - // Notify the observers on the UI thread. - ui_loop_->PostTask(FROM_HERE, - new NotificationTask(NOTIFY_RESOURCE_RESPONSE_COMPLETED, request, - new ResourceRequestDetails(request, - GetCertID(request, render_process_host_id)))); -} - -void ResourceDispatcherHost::NofityReceivedRedirect(URLRequest* request, - int render_process_host_id, - const GURL& new_url) { - // Notify the observers on the IO thread. - FOR_EACH_OBSERVER(Observer, observer_list_, - OnReceivedRedirect(this, request, new_url)); - - int cert_id = GetCertID(request, render_process_host_id); - - // Notify the observers on the UI thread. - ui_loop_->PostTask(FROM_HERE, - new NotificationTask(NOTIFY_RESOURCE_RECEIVED_REDIRECT, request, - new ResourceRedirectDetails(request, - cert_id, - new_url))); -} - -namespace { - -// This function attempts to return the "more interesting" load state of |a| -// and |b|. We don't have temporal information about these load states -// (meaning we don't know when we transitioned into these states), so we just -// rank them according to how "interesting" the states are. -// -// We take advantage of the fact that the load states are an enumeration listed -// in the order in which they occur during the lifetime of a request, so we can -// regard states with larger numeric values as being further along toward -// completion. We regard those states as more interesting to report since they -// represent progress. -// -// For example, by this measure "tranferring data" is a more interesting state -// than "resolving host" because when we are transferring data we are actually -// doing something that corresponds to changes that the user might observe, -// whereas waiting for a host name to resolve implies being stuck. -// -net::LoadState MoreInterestingLoadState(net::LoadState a, net::LoadState b) { - return (a < b) ? b : a; -} - -// Carries information about a load state change. -struct LoadInfo { - GURL url; - net::LoadState load_state; -}; - -// Map from ProcessID+ViewID pair to LoadState -typedef std::map<std::pair<int, int>, LoadInfo> LoadInfoMap; - -// Used to marshall calls to LoadStateChanged from the IO to UI threads. We do -// them all as a single task to avoid spamming the UI thread. -class LoadInfoUpdateTask : public Task { - public: - virtual void Run() { - LoadInfoMap::const_iterator i; - for (i = info_map.begin(); i != info_map.end(); ++i) { - RenderViewHost* view = - RenderViewHost::FromID(i->first.first, i->first.second); - if (view) // The view could be gone at this point. - view->LoadStateChanged(i->second.url, i->second.load_state); - } - } - LoadInfoMap info_map; -}; - -} // namespace - -void ResourceDispatcherHost::UpdateLoadStates() { - // Populate this map with load state changes, and then send them on to the UI - // thread where they can be passed along to the respective RVHs. - LoadInfoMap info_map; - - PendingRequestList::const_iterator i; - for (i = pending_requests_.begin(); i != pending_requests_.end(); ++i) { - URLRequest* request = i->second; - net::LoadState load_state = request->GetLoadState(); - ExtraRequestInfo* info = ExtraInfoForRequest(request); - - // We also poll for upload progress on this timer and send upload - // progress ipc messages to the plugin process. - MaybeUpdateUploadProgress(info, request); - - if (info->last_load_state != load_state) { - info->last_load_state = load_state; - - std::pair<int, int> key(info->render_process_host_id, - info->render_view_id); - net::LoadState to_insert; - LoadInfoMap::iterator existing = info_map.find(key); - if (existing == info_map.end()) { - to_insert = load_state; - } else { - to_insert = - MoreInterestingLoadState(existing->second.load_state, load_state); - if (to_insert == existing->second.load_state) - continue; - } - LoadInfo& load_info = info_map[key]; - load_info.url = request->url(); - load_info.load_state = to_insert; - } - } - - if (info_map.empty()) - return; - - LoadInfoUpdateTask* task = new LoadInfoUpdateTask; - task->info_map.swap(info_map); - ui_loop_->PostTask(FROM_HERE, task); -} - -void ResourceDispatcherHost::MaybeUpdateUploadProgress(ExtraRequestInfo *info, - URLRequest *request) { - if (!info->upload_size || info->waiting_for_upload_progress_ack || - !(request->load_flags() & net::LOAD_ENABLE_UPLOAD_PROGRESS)) - return; - - uint64 size = info->upload_size; - uint64 position = request->GetUploadProgress(); - if (position == info->last_upload_position) - return; // no progress made since last time - - const uint64 kHalfPercentIncrements = 200; - const TimeDelta kOneSecond = TimeDelta::FromMilliseconds(1000); - - uint64 amt_since_last = position - info->last_upload_position; - TimeDelta time_since_last = TimeTicks::Now() - info->last_upload_ticks; - - bool is_finished = (size == position); - bool enough_new_progress = (amt_since_last > (size / kHalfPercentIncrements)); - bool too_much_time_passed = time_since_last > kOneSecond; - - if (is_finished || enough_new_progress || too_much_time_passed) { - info->resource_handler->OnUploadProgress(info->request_id, position, size); - info->waiting_for_upload_progress_ack = true; - info->last_upload_ticks = TimeTicks::Now(); - info->last_upload_position = position; - } -} |