Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(207)

Side by Side Diff: components/webp_transcode/webp_network_client.mm

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

Powered by Google App Engine
This is Rietveld 408576698