Index: ios/chrome/browser/net/image_fetcher.mm |
diff --git a/ios/chrome/browser/net/image_fetcher.mm b/ios/chrome/browser/net/image_fetcher.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e40dcdf69e1a6a982f9197ce34b6366ccaa63d32 |
--- /dev/null |
+++ b/ios/chrome/browser/net/image_fetcher.mm |
@@ -0,0 +1,176 @@ |
+// Copyright 2012 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. |
+ |
+#import "ios/chrome/browser/net/image_fetcher.h" |
+ |
+#import <Foundation/Foundation.h> |
+ |
+#include "base/bind.h" |
+#include "base/compiler_specific.h" |
+#include "base/location.h" |
+#include "base/logging.h" |
+#include "base/mac/scoped_block.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/task_runner_util.h" |
+#include "base/threading/sequenced_worker_pool.h" |
+#include "ios/web/public/webp_decoder.h" |
+#include "net/base/load_flags.h" |
+#include "net/http/http_response_headers.h" |
+#include "net/url_request/url_fetcher.h" |
+#include "url/gurl.h" |
+ |
+namespace { |
+ |
+class WebpDecoderDelegate : public web::WebpDecoder::Delegate { |
+ public: |
+ NSData* data() const { return decoded_image_; } |
+ |
+ // WebpDecoder::Delegate methods |
+ void OnFinishedDecoding(bool success) override { |
+ if (!success) |
+ decoded_image_.reset(); |
+ } |
+ void SetImageFeatures( |
+ size_t total_size, |
+ web::WebpDecoder::DecodedImageFormat format) override { |
+ decoded_image_.reset([[NSMutableData alloc] initWithCapacity:total_size]); |
+ } |
+ void OnDataDecoded(NSData* data) override { |
+ DCHECK(decoded_image_); |
+ [decoded_image_ appendData:data]; |
+ } |
+ private: |
+ ~WebpDecoderDelegate() override {} |
+ base::scoped_nsobject<NSMutableData> decoded_image_; |
+}; |
+ |
+// Returns a NSData object containing the decoded image. |
+// Returns nil in case of failure. |
+base::scoped_nsobject<NSData> DecodeWebpImage( |
+ const base::scoped_nsobject<NSData>& webp_image) { |
+ scoped_refptr<WebpDecoderDelegate> delegate(new WebpDecoderDelegate); |
+ scoped_refptr<web::WebpDecoder> decoder(new web::WebpDecoder(delegate.get())); |
+ decoder->OnDataReceived(webp_image); |
+ DLOG_IF(ERROR, !delegate->data()) << "WebP image decoding failed."; |
+ return base::scoped_nsobject<NSData>([delegate->data() retain]); |
+} |
+ |
+} // namespace |
+ |
+namespace image_fetcher { |
+ |
+ImageFetcher::ImageFetcher( |
+ const scoped_refptr<base::SequencedWorkerPool> decoding_pool) |
+ : request_context_getter_(nullptr), |
+ weak_factory_(this), |
+ decoding_pool_(decoding_pool) { |
+ DCHECK(decoding_pool_.get()); |
+} |
+ |
+ImageFetcher::~ImageFetcher() { |
+ // Delete all the entries in the |downloads_in_progress_| map. This will in |
+ // turn cancel all of the requests. |
+ for (std::map<const net::URLFetcher*, Callback>::iterator it = |
+ downloads_in_progress_.begin(); |
+ it != downloads_in_progress_.end(); ++it) { |
+ [it->second release]; |
+ delete it->first; |
+ } |
+} |
+ |
+void ImageFetcher::StartDownload( |
+ const GURL& url, |
+ Callback callback, |
+ const std::string& referrer, |
+ net::URLRequest::ReferrerPolicy referrer_policy) { |
+ DCHECK(request_context_getter_.get()); |
+ net::URLFetcher* fetcher = net::URLFetcher::Create(url, |
+ net::URLFetcher::GET, |
+ this); |
+ downloads_in_progress_[fetcher] = [callback copy]; |
+ fetcher->SetLoadFlags( |
+ net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES); |
+ fetcher->SetRequestContext(request_context_getter_.get()); |
+ fetcher->SetReferrer(referrer); |
+ fetcher->SetReferrerPolicy(referrer_policy); |
+ fetcher->Start(); |
+} |
+ |
+void ImageFetcher::StartDownload(const GURL& url, Callback callback) { |
+ ImageFetcher::StartDownload( |
+ url, callback, "", net::URLRequest::NEVER_CLEAR_REFERRER); |
+} |
+ |
+// Delegate callback that is called when URLFetcher completes. If the image |
+// was fetched successfully, creates a new NSData and returns it to the |
+// callback, otherwise returns nil to the callback. |
+void ImageFetcher::OnURLFetchComplete(const net::URLFetcher* fetcher) { |
+ if (downloads_in_progress_.find(fetcher) == downloads_in_progress_.end()) { |
+ LOG(ERROR) << "Received callback for unknown URLFetcher " << fetcher; |
+ return; |
+ } |
+ |
+ // Ensures that |fetcher| will be deleted even if we return early. |
+ scoped_ptr<const net::URLFetcher> fetcher_deleter(fetcher); |
+ |
+ // Retrieves the callback and ensures that it will be deleted even if we |
+ // return early. |
+ base::mac::ScopedBlock<Callback> callback(downloads_in_progress_[fetcher]); |
+ |
+ // Remove |fetcher| from the map. |
+ downloads_in_progress_.erase(fetcher); |
+ |
+ // Make sure the request was successful. For "data" requests, the response |
+ // code has no meaning, because there is no actual server (data is encoded |
+ // directly in the URL). In that case, we set the response code to 200. |
+ const GURL& original_url = fetcher->GetOriginalURL(); |
+ const int http_response_code = original_url.SchemeIs("data") ? |
+ 200 : fetcher->GetResponseCode(); |
+ if (http_response_code != 200) { |
+ (callback.get())(original_url, http_response_code, nil); |
+ return; |
+ } |
+ |
+ std::string response; |
+ if (!fetcher->GetResponseAsString(&response)) { |
+ (callback.get())(original_url, http_response_code, nil); |
+ return; |
+ } |
+ |
+ // Create a NSData from the returned data and notify the callback. |
+ base::scoped_nsobject<NSData> data([[NSData alloc] |
+ initWithBytes:reinterpret_cast<const unsigned char*>(response.data()) |
+ length:response.size()]); |
+ |
+ if (fetcher->GetResponseHeaders()) { |
+ std::string mime_type; |
+ fetcher->GetResponseHeaders()->GetMimeType(&mime_type); |
+ if (mime_type == "image/webp") { |
+ base::PostTaskAndReplyWithResult(decoding_pool_.get(), |
+ FROM_HERE, |
+ base::Bind(&DecodeWebpImage, data), |
+ base::Bind(&ImageFetcher::RunCallback, |
+ weak_factory_.GetWeakPtr(), |
+ callback, |
+ original_url, |
+ http_response_code)); |
+ return; |
+ } |
+ } |
+ (callback.get())(original_url, http_response_code, data); |
+} |
+ |
+void ImageFetcher::RunCallback(const base::mac::ScopedBlock<Callback>& callback, |
+ const GURL& url, |
+ int http_response_code, |
+ NSData* data) { |
+ (callback.get())(url, http_response_code, data); |
+} |
+ |
+void ImageFetcher::SetRequestContextGetter( |
+ net::URLRequestContextGetter* request_context_getter) { |
+ request_context_getter_ = request_context_getter; |
+} |
+ |
+} // namespace image_fetcher |