| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 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 #import "components/webp_transcode/webp_network_client.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/compiler_specific.h" | |
| 11 #include "base/location.h" | |
| 12 #include "base/logging.h" | |
| 13 #include "base/mac/bind_objc_block.h" | |
| 14 #include "base/mac/scoped_nsobject.h" | |
| 15 #include "base/sequenced_task_runner.h" | |
| 16 #include "base/single_thread_task_runner.h" | |
| 17 #include "base/strings/string_util.h" | |
| 18 #include "base/strings/sys_string_conversions.h" | |
| 19 #include "base/thread_task_runner_handle.h" | |
| 20 #include "components/webp_transcode/webp_decoder.h" | |
| 21 #include "net/base/net_errors.h" | |
| 22 #include "net/http/http_request_headers.h" | |
| 23 #include "net/url_request/url_request.h" | |
| 24 | |
| 25 namespace net { | |
| 26 class URLRequest; | |
| 27 } | |
| 28 | |
| 29 using namespace webp_transcode; | |
| 30 | |
| 31 namespace { | |
| 32 | |
| 33 // MIME type for WebP images. | |
| 34 const char kWebPMimeType[] = "image/webp"; | |
| 35 NSString* const kNSWebPMimeType = @"image/webp"; | |
| 36 | |
| 37 NSURLResponse* NewImageResponse(NSURLResponse* webp_response, | |
| 38 size_t content_length, | |
| 39 WebpDecoder::DecodedImageFormat format) { | |
| 40 DCHECK(webp_response); | |
| 41 | |
| 42 NSString* mime_type = nil; | |
| 43 switch (format) { | |
| 44 case WebpDecoder::JPEG: | |
| 45 mime_type = @"image/jpeg"; | |
| 46 break; | |
| 47 case WebpDecoder::PNG: | |
| 48 mime_type = @"image/png"; | |
| 49 break; | |
| 50 case WebpDecoder::TIFF: | |
| 51 mime_type = @"image/tiff"; | |
| 52 break; | |
| 53 case WebpDecoder::DECODED_FORMAT_COUNT: | |
| 54 NOTREACHED(); | |
| 55 break; | |
| 56 } | |
| 57 DCHECK(mime_type); | |
| 58 | |
| 59 if ([webp_response isKindOfClass:[NSHTTPURLResponse class]]) { | |
| 60 NSHTTPURLResponse* http_response = | |
| 61 static_cast<NSHTTPURLResponse*>(webp_response); | |
| 62 NSMutableDictionary* header_fields = [NSMutableDictionary | |
| 63 dictionaryWithDictionary:[http_response allHeaderFields]]; | |
| 64 [header_fields setObject:[NSString stringWithFormat:@"%zu", content_length] | |
| 65 forKey:@"Content-Length"]; | |
| 66 [header_fields setObject:mime_type forKey:@"Content-Type"]; | |
| 67 return [[NSHTTPURLResponse alloc] initWithURL:[http_response URL] | |
| 68 statusCode:[http_response statusCode] | |
| 69 HTTPVersion:@"HTTP/1.1" | |
| 70 headerFields:header_fields]; | |
| 71 } else { | |
| 72 return [[NSURLResponse alloc] initWithURL:[webp_response URL] | |
| 73 MIMEType:mime_type | |
| 74 expectedContentLength:content_length | |
| 75 textEncodingName:[webp_response textEncodingName]]; | |
| 76 } | |
| 77 } | |
| 78 | |
| 79 class WebpDecoderDelegate : public WebpDecoder::Delegate { | |
| 80 public: | |
| 81 WebpDecoderDelegate(id<CRNNetworkClientProtocol> client, | |
| 82 const base::Time& request_creation_time, | |
| 83 const scoped_refptr<base::TaskRunner>& callback_runner) | |
| 84 : underlying_client_([client retain]), | |
| 85 callback_task_runner_(callback_runner), | |
| 86 request_creation_time_(request_creation_time) { | |
| 87 DCHECK(underlying_client_.get()); | |
| 88 } | |
| 89 | |
| 90 void SetOriginalResponse( | |
| 91 const base::scoped_nsobject<NSURLResponse>& response) { | |
| 92 original_response_.reset([response retain]); | |
| 93 } | |
| 94 | |
| 95 // WebpDecoder::Delegate methods. | |
| 96 void OnFinishedDecoding(bool success) override { | |
| 97 base::scoped_nsprotocol<id<CRNNetworkClientProtocol>> block_client( | |
| 98 [underlying_client_ retain]); | |
| 99 if (success) { | |
| 100 callback_task_runner_->PostTask(FROM_HERE, base::BindBlock(^{ | |
| 101 [block_client didFinishLoading]; | |
| 102 })); | |
| 103 } else { | |
| 104 DLOG(WARNING) << "WebP decoding failed " | |
| 105 << base::SysNSStringToUTF8( | |
| 106 [[original_response_ URL] absoluteString]); | |
| 107 void (^errorBlock)(void) = ^{ | |
| 108 [block_client didFailWithNSErrorCode:NSURLErrorCannotDecodeContentData | |
| 109 netErrorCode:net::ERR_CONTENT_DECODING_FAILED]; | |
| 110 }; | |
| 111 callback_task_runner_->PostTask(FROM_HERE, base::BindBlock(errorBlock)); | |
| 112 } | |
| 113 } | |
| 114 | |
| 115 void SetImageFeatures(size_t total_size, | |
| 116 WebpDecoder::DecodedImageFormat format) override { | |
| 117 base::scoped_nsobject<NSURLResponse> imageResponse( | |
| 118 NewImageResponse(original_response_, total_size, format)); | |
| 119 DCHECK(imageResponse); | |
| 120 base::scoped_nsprotocol<id<CRNNetworkClientProtocol>> block_client( | |
| 121 [underlying_client_ retain]); | |
| 122 callback_task_runner_->PostTask(FROM_HERE, base::BindBlock(^{ | |
| 123 [block_client didReceiveResponse:imageResponse]; | |
| 124 })); | |
| 125 } | |
| 126 | |
| 127 void OnDataDecoded(NSData* data) override { | |
| 128 base::scoped_nsprotocol<id<CRNNetworkClientProtocol>> block_client( | |
| 129 [underlying_client_ retain]); | |
| 130 callback_task_runner_->PostTask(FROM_HERE, base::BindBlock(^{ | |
| 131 [block_client didLoadData:data]; | |
| 132 })); | |
| 133 } | |
| 134 | |
| 135 private: | |
| 136 ~WebpDecoderDelegate() override {} | |
| 137 | |
| 138 base::scoped_nsprotocol<id<CRNNetworkClientProtocol>> underlying_client_; | |
| 139 base::scoped_nsobject<NSURLResponse> original_response_; | |
| 140 scoped_refptr<base::TaskRunner> callback_task_runner_; | |
| 141 base::Time request_creation_time_; | |
| 142 }; | |
| 143 | |
| 144 } // namespace | |
| 145 | |
| 146 @interface WebPNetworkClient () { | |
| 147 scoped_refptr<webp_transcode::WebpDecoder> _webpDecoder; | |
| 148 scoped_refptr<WebpDecoderDelegate> _webpDecoderDelegate; | |
| 149 scoped_refptr<base::SequencedTaskRunner> _taskRunner; | |
| 150 base::Time _requestCreationTime; | |
| 151 } | |
| 152 @end | |
| 153 | |
| 154 @implementation WebPNetworkClient | |
| 155 | |
| 156 - (instancetype)init { | |
| 157 NOTREACHED() << "Use |-initWithTaskRunner:| instead"; | |
| 158 return nil; | |
| 159 } | |
| 160 | |
| 161 - (instancetype)initWithTaskRunner: | |
| 162 (const scoped_refptr<base::SequencedTaskRunner>&)runner { | |
| 163 if (self = [super init]) { | |
| 164 DCHECK(runner); | |
| 165 _taskRunner = runner; | |
| 166 } | |
| 167 return self; | |
| 168 } | |
| 169 | |
| 170 - (void)didCreateNativeRequest:(net::URLRequest*)nativeRequest { | |
| 171 // Append 'image/webp' to the outgoing 'Accept' header. | |
| 172 const net::HttpRequestHeaders& headers = | |
| 173 nativeRequest->extra_request_headers(); | |
| 174 std::string acceptHeader; | |
| 175 if (headers.GetHeader("Accept", &acceptHeader)) { | |
| 176 // Add 'image/webp' if it isn't in the Accept header yet. | |
| 177 if (acceptHeader.find(kWebPMimeType) == std::string::npos) { | |
| 178 acceptHeader += std::string(",") + kWebPMimeType; | |
| 179 nativeRequest->SetExtraRequestHeaderByName("Accept", acceptHeader, true); | |
| 180 } | |
| 181 } else { | |
| 182 // All requests should already have an Accept: header, so this case | |
| 183 // should never happen outside of unit tests. | |
| 184 nativeRequest->SetExtraRequestHeaderByName("Accept", kWebPMimeType, false); | |
| 185 } | |
| 186 [super didCreateNativeRequest:nativeRequest]; | |
| 187 } | |
| 188 | |
| 189 - (void)didLoadData:(NSData*)data { | |
| 190 if (_webpDecoder.get()) { | |
| 191 // |data| is assumed to be immutable. | |
| 192 base::scoped_nsobject<NSData> scopedData([data retain]); | |
| 193 _taskRunner->PostTask(FROM_HERE, base::Bind(&WebpDecoder::OnDataReceived, | |
| 194 _webpDecoder, scopedData)); | |
| 195 } else { | |
| 196 [super didLoadData:data]; | |
| 197 } | |
| 198 } | |
| 199 | |
| 200 - (void)didReceiveResponse:(NSURLResponse*)response { | |
| 201 DCHECK(self.underlyingClient); | |
| 202 NSString* responseMimeType = [response MIMEType]; | |
| 203 if (responseMimeType && | |
| 204 [responseMimeType caseInsensitiveCompare:kNSWebPMimeType] == | |
| 205 NSOrderedSame) { | |
| 206 _webpDecoderDelegate = | |
| 207 new WebpDecoderDelegate(self.underlyingClient, _requestCreationTime, | |
| 208 base::ThreadTaskRunnerHandle::Get()); | |
| 209 _webpDecoder = new webp_transcode::WebpDecoder(_webpDecoderDelegate.get()); | |
| 210 base::scoped_nsobject<NSURLResponse> scoped_response([response copy]); | |
| 211 _taskRunner->PostTask(FROM_HERE, | |
| 212 base::Bind(&WebpDecoderDelegate::SetOriginalResponse, | |
| 213 _webpDecoderDelegate, scoped_response)); | |
| 214 // Do not call super here, the WebpDecoderDelegate will update the mime type | |
| 215 // and call |-didReceiveResponse:|. | |
| 216 } else { | |
| 217 // If this isn't a WebP, pass the call up the chain. | |
| 218 [super didReceiveResponse:response]; | |
| 219 } | |
| 220 } | |
| 221 | |
| 222 - (void)didFinishLoading { | |
| 223 if (_webpDecoder.get()) { | |
| 224 _taskRunner->PostTask(FROM_HERE, | |
| 225 base::Bind(&WebpDecoder::Stop, _webpDecoder)); | |
| 226 } else { | |
| 227 [super didFinishLoading]; | |
| 228 } | |
| 229 } | |
| 230 | |
| 231 @end | |
| OLD | NEW |