mirror of
				https://bitbucket.org/chromiumembedded/cef
				synced 2025-06-05 21:39:12 +02:00 
			
		
		
		
	Add more robust download handler implementation (issue #156).
git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@155 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
This commit is contained in:
		| @@ -4,14 +4,14 @@ | |||||||
| // found in the LICENSE file. | // found in the LICENSE file. | ||||||
| // | // | ||||||
| // This file contains an implementation of the ResourceLoaderBridge class. | // This file contains an implementation of the ResourceLoaderBridge class. | ||||||
| // The class is implemented using net::URLRequest, meaning it is a "simple" version | // The class is implemented using net::URLRequest, meaning it is a "simple" | ||||||
| // that directly issues requests. The more complicated one used in the | // version that directly issues requests. The more complicated one used in the | ||||||
| // browser uses IPC. | // browser uses IPC. | ||||||
| // | // | ||||||
| // Because net::URLRequest only provides an asynchronous resource loading API, this | // Because net::URLRequest only provides an asynchronous resource loading API, | ||||||
| // file makes use of net::URLRequest from a background IO thread.  Requests for | // this file makes use of net::URLRequest from a background IO thread.  Requests | ||||||
| // cookies and synchronously loaded resources result in the main thread of the | // for cookies and synchronously loaded resources result in the main thread of | ||||||
| // application blocking until the IO thread completes the operation.  (See | // the application blocking until the IO thread completes the operation.  (See | ||||||
| // GetCookies and SyncLoad) | // GetCookies and SyncLoad) | ||||||
| // | // | ||||||
| // Main thread                          IO thread | // Main thread                          IO thread | ||||||
| @@ -100,9 +100,26 @@ struct RequestParams { | |||||||
| // The interval for calls to RequestProxy::MaybeUpdateUploadProgress | // The interval for calls to RequestProxy::MaybeUpdateUploadProgress | ||||||
| static const int kUpdateUploadProgressIntervalMsec = 100; | static const int kUpdateUploadProgressIntervalMsec = 100; | ||||||
|  |  | ||||||
|  | class ExtraRequestInfo : public net::URLRequest::UserData { | ||||||
|  | public: | ||||||
|  |   ExtraRequestInfo(ResourceType::Type resource_type) | ||||||
|  |     : resource_type_(resource_type), | ||||||
|  |       allow_download_(resource_type == ResourceType::MAIN_FRAME ||  | ||||||
|  |                       resource_type == ResourceType::SUB_FRAME) | ||||||
|  |   { } | ||||||
|  |  | ||||||
|  |   // Identifies the type of resource, such as subframe, media, etc. | ||||||
|  |   ResourceType::Type resource_type() const { return resource_type_; } | ||||||
|  |   bool allow_download() const { return allow_download_; } | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |   ResourceType::Type resource_type_; | ||||||
|  |   bool allow_download_; | ||||||
|  | }; | ||||||
|  |  | ||||||
| // The RequestProxy does most of its work on the IO thread.  The Start and | // The RequestProxy does most of its work on the IO thread.  The Start and | ||||||
| // Cancel methods are proxied over to the IO thread, where an net::URLRequest object | // Cancel methods are proxied over to the IO thread, where an net::URLRequest | ||||||
| // is instantiated. | // object is instantiated. | ||||||
| class RequestProxy : public net::URLRequest::Delegate, | class RequestProxy : public net::URLRequest::Delegate, | ||||||
|                      public base::RefCountedThreadSafe<RequestProxy> { |                      public base::RefCountedThreadSafe<RequestProxy> { | ||||||
|  public: |  public: | ||||||
| @@ -168,18 +185,26 @@ class RequestProxy : public net::URLRequest::Delegate, | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   void NotifyReceivedResponse(const ResourceResponseInfo& info, |   void NotifyReceivedResponse(const ResourceResponseInfo& info, | ||||||
|                               bool content_filtered) { |                               bool content_filtered, | ||||||
|     std::string cd_header, filename; |                               const GURL& url, bool allow_download) { | ||||||
|     if (info.headers && browser_.get() && |  | ||||||
|         info.headers->GetNormalizedHeader("Content-Disposition", &cd_header) && |     if (browser_.get() && info.headers.get()) { | ||||||
|         webkit_glue::IsContentDispositionAttachment(cd_header, filename)) { |  | ||||||
|       // The response represents a download request. |  | ||||||
|       CefRefPtr<CefHandler> handler = browser_->GetHandler(); |       CefRefPtr<CefHandler> handler = browser_->GetHandler(); | ||||||
|       if (handler.get()) { |       if (handler.get()) { | ||||||
|         CefRefPtr<CefDownloadHandler> dl_handler; |         std::string content_disposition; | ||||||
|         if (handler->HandleDownloadResponse(browser_, info.mime_type, filename, |         info.headers->GetNormalizedHeader("Content-Disposition", | ||||||
|                 info.content_length, dl_handler) == RV_CONTINUE) { |             &content_disposition); | ||||||
|           download_handler_ = dl_handler; |  | ||||||
|  |         if (allow_download && | ||||||
|  |             webkit_glue::ShouldDownload(content_disposition, info.mime_type)) { | ||||||
|  |           FilePath path(net::GetSuggestedFilename(url, content_disposition, | ||||||
|  |               info.charset, FilePath(L"download"))); | ||||||
|  |           CefRefPtr<CefDownloadHandler> dl_handler; | ||||||
|  |           if (handler->HandleDownloadResponse(browser_, info.mime_type, | ||||||
|  |                   path.value(), info.content_length, dl_handler) == | ||||||
|  |                   RV_CONTINUE) { | ||||||
|  |             download_handler_ = dl_handler; | ||||||
|  |           } | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| @@ -259,7 +284,8 @@ class RequestProxy : public net::URLRequest::Delegate, | |||||||
|       { |       { | ||||||
|         // Build the request object for passing to the handler |         // Build the request object for passing to the handler | ||||||
|         CefRefPtr<CefRequest> request(new CefRequestImpl()); |         CefRefPtr<CefRequest> request(new CefRequestImpl()); | ||||||
|         CefRequestImpl* requestimpl = static_cast<CefRequestImpl*>(request.get()); |         CefRequestImpl* requestimpl = | ||||||
|  |             static_cast<CefRequestImpl*>(request.get()); | ||||||
|  |  | ||||||
|         std::string originalUrl(params->url.spec()); |         std::string originalUrl(params->url.spec()); | ||||||
|         requestimpl->SetURL(originalUrl); |         requestimpl->SetURL(originalUrl); | ||||||
| @@ -370,6 +396,7 @@ class RequestProxy : public net::URLRequest::Delegate, | |||||||
|       request_->set_load_flags(params->load_flags); |       request_->set_load_flags(params->load_flags); | ||||||
|       request_->set_upload(params->upload.get()); |       request_->set_upload(params->upload.get()); | ||||||
|       request_->set_context(_Context->request_context()); |       request_->set_context(_Context->request_context()); | ||||||
|  |       request_->SetUserData(NULL, new ExtraRequestInfo(params->request_type)); | ||||||
|       BrowserAppCacheSystem::SetExtraRequestInfo( |       BrowserAppCacheSystem::SetExtraRequestInfo( | ||||||
|           request_.get(), params->appcache_host_id, params->request_type); |           request_.get(), params->appcache_host_id, params->request_type); | ||||||
|  |  | ||||||
| @@ -389,7 +416,8 @@ class RequestProxy : public net::URLRequest::Delegate, | |||||||
|       if (request_->has_upload() && |       if (request_->has_upload() && | ||||||
|           params->load_flags & net::LOAD_ENABLE_UPLOAD_PROGRESS) { |           params->load_flags & net::LOAD_ENABLE_UPLOAD_PROGRESS) { | ||||||
|         upload_progress_timer_.Start( |         upload_progress_timer_.Start( | ||||||
|             base::TimeDelta::FromMilliseconds(kUpdateUploadProgressIntervalMsec), |             base::TimeDelta::FromMilliseconds( | ||||||
|  |                 kUpdateUploadProgressIntervalMsec), | ||||||
|             this, &RequestProxy::MaybeUpdateUploadProgress); |             this, &RequestProxy::MaybeUpdateUploadProgress); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| @@ -463,8 +491,19 @@ class RequestProxy : public net::URLRequest::Delegate, | |||||||
|   virtual void OnReceivedResponse( |   virtual void OnReceivedResponse( | ||||||
|       const ResourceResponseInfo& info, |       const ResourceResponseInfo& info, | ||||||
|       bool content_filtered) { |       bool content_filtered) { | ||||||
|  |     GURL url; | ||||||
|  |     bool allow_download(false); | ||||||
|  |     if (request_.get()){ | ||||||
|  |       url = request_->url(); | ||||||
|  |       ExtraRequestInfo* info = | ||||||
|  |           static_cast<ExtraRequestInfo*>(request_->GetUserData(NULL)); | ||||||
|  |       if (info) | ||||||
|  |         allow_download = info->allow_download(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     owner_loop_->PostTask(FROM_HERE, NewRunnableMethod( |     owner_loop_->PostTask(FROM_HERE, NewRunnableMethod( | ||||||
|         this, &RequestProxy::NotifyReceivedResponse, info, content_filtered)); |         this, &RequestProxy::NotifyReceivedResponse, info, content_filtered, | ||||||
|  |         url, allow_download)); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   virtual void OnReceivedData(int bytes_read) { |   virtual void OnReceivedData(int bytes_read) { | ||||||
| @@ -550,8 +589,8 @@ class RequestProxy : public net::URLRequest::Delegate, | |||||||
|  |  | ||||||
|   // Called on the IO thread. |   // Called on the IO thread. | ||||||
|   void MaybeUpdateUploadProgress() { |   void MaybeUpdateUploadProgress() { | ||||||
|     // If a redirect is received upload is cancelled in net::URLRequest, we should |     // If a redirect is received upload is cancelled in net::URLRequest, we | ||||||
|     // try to stop the |upload_progress_timer_| timer and return. |     // should try to stop the |upload_progress_timer_| timer and return. | ||||||
|     if (!request_->has_upload()) { |     if (!request_->has_upload()) { | ||||||
|       if (upload_progress_timer_.IsRunning()) |       if (upload_progress_timer_.IsRunning()) | ||||||
|         upload_progress_timer_.Stop(); |         upload_progress_timer_.Stop(); | ||||||
|   | |||||||
| @@ -9,9 +9,12 @@ | |||||||
| MSVC_PUSH_WARNING_LEVEL(0); | MSVC_PUSH_WARNING_LEVEL(0); | ||||||
| #include "MemoryCache.h" | #include "MemoryCache.h" | ||||||
| #include "TextEncoding.h" | #include "TextEncoding.h" | ||||||
| #include "third_party/WebKit/WebCore/platform/network/HTTPParsers.h" |  | ||||||
| #include "third_party/WebKit/WebKit/chromium/src/WebFrameImpl.h" | #include "third_party/WebKit/WebKit/chromium/src/WebFrameImpl.h" | ||||||
| MSVC_POP_WARNING(); | MSVC_POP_WARNING(); | ||||||
|  | #undef LOG | ||||||
|  | #include "base/string_util.h" | ||||||
|  | #include "net/base/mime_util.h" | ||||||
|  | #include "webkit/glue/plugins/plugin_list.h" | ||||||
|  |  | ||||||
| #include "browser_webkit_glue.h" | #include "browser_webkit_glue.h" | ||||||
|  |  | ||||||
| @@ -136,20 +139,52 @@ void EnableSpdy(bool enable) { | |||||||
|   // Used in benchmarking,  Ignored for CEF. |   // Used in benchmarking,  Ignored for CEF. | ||||||
| } | } | ||||||
|  |  | ||||||
| bool IsContentDispositionAttachment(const std::string& cd_header, | // Adapted from Chromium's BufferedResourceHandler::ShouldDownload | ||||||
|                                     std::string& file_name) { | bool ShouldDownload(const std::string& content_disposition, | ||||||
|   WTF::String cd_str(cd_header.c_str(), cd_header.length()); |                     const std::string& mime_type) | ||||||
|   if (WebCore::contentDispositionType(cd_str) == | { | ||||||
|     WebCore::ContentDispositionAttachment) { |   std::string type = StringToLowerASCII(mime_type); | ||||||
|     WTF::String name_str = |   std::string disposition = StringToLowerASCII(content_disposition); | ||||||
|         WebCore::filenameFromHTTPContentDisposition(cd_str); |  | ||||||
|     if (!name_str.isEmpty()) { |   // First, examine content-disposition. | ||||||
|       WTF::CString cstr(name_str.utf8()); |   if (!disposition.empty()) { | ||||||
|       file_name = std::string(cstr.data(), cstr.length()); |     bool should_download = true; | ||||||
|     } |  | ||||||
|     return 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; | ||||||
|   } |   } | ||||||
|   return false; |  | ||||||
|  |   // Mirrors WebViewImpl::CanShowMIMEType() | ||||||
|  |   if (type.empty() || net::IsSupportedMimeType(type)) | ||||||
|  |     return false; | ||||||
|  |  | ||||||
|  |   //// Finally, check the plugin list. | ||||||
|  |   WebPluginInfo info; | ||||||
|  |   bool allow_wildcard = false; | ||||||
|  |   return !NPAPI::PluginList::Singleton()->GetPluginInfo( | ||||||
|  |     GURL(), type, allow_wildcard, &info, NULL) || !info.enabled; | ||||||
| } | } | ||||||
|  |  | ||||||
| }  // namespace webkit_glue | }  // namespace webkit_glue | ||||||
|   | |||||||
| @@ -42,9 +42,9 @@ v8::Handle<v8::Context> GetV8Context(WebKit::WebFrame* frame); | |||||||
| // Clear all cached data. | // Clear all cached data. | ||||||
| void ClearCache(); | void ClearCache(); | ||||||
|  |  | ||||||
| // Returns true if the specified 'Content-Disposition' header value represents | // Returns true if the request represents a download based on | ||||||
| // an attachment download. Also returns the file name. | // the supplied Content-Type and Content-Disposition headers. | ||||||
| bool IsContentDispositionAttachment(const std::string& cd_header, | bool ShouldDownload(const std::string& content_disposition, | ||||||
|                                     std::string& file_name); |                     const std::string& mime_type); | ||||||
|  |  | ||||||
| }  // namespace webkit_glue | }  // namespace webkit_glue | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user