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

Side by Side Diff: chrome/browser/icon_url_data_manager.cc

Issue 3324009: Work in progress implementation of Icon URI scheme. (Closed) Base URL: git://git.chromium.org/chromium.git
Patch Set: Created 10 years, 3 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 (c) 2010 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 #include <string>
6
7 #include "base/file_path.h"
8 #include "base/logging.h"
9 #include "base/message_loop.h"
10 #include "base/string_util.h"
11 #include "base/task.h"
12 #include "base/utf_string_conversions.h"
13 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/chrome_thread.h"
15 #include "chrome/browser/icon_manager.h"
16 #include "chrome/browser/icon_url_data_manager.h"
17 #include "chrome/common/url_constants.h"
18 #include "gfx/codec/png_codec.h"
19 #include "net/base/escape.h"
20 #include "net/base/net_errors.h"
21 #include "net/base/io_buffer.h"
22 #include "net/url_request/url_request.h"
23 #include "net/url_request/url_request_job.h"
24
25 // Definition of IconURI.
26
27 const std::string IconURI::DIRECTORY = "directory";
28 const std::string IconURI::PARENT_DIRECTORY = "parentdir";
29 const std::string IconURI::UNKNOWN = "unknown";
30
31 const std::string IconURI::SMALL_ID = "small";
32 const std::string IconURI::MEDIUM_ID = "medium";
33 const std::string IconURI::LARGE_ID = "large";
34
35 IconURI::IconURI() : size_(SMALL) {}
36
37 IconURI::~IconURI() {}
38
39 bool IconURI::ParseIconURI(const GURL& url) {
40 url_ = url;
41
42 if (!IsValid())
43 return false;
44
45 std::string path;
46 TrimString(url.path(), "/", &path);
47 StringToLowerASCII(&path);
48
49 size_t semi_colon_index = path.find(';');
50 std::string size_str;
51
52 if (semi_colon_index != std::string::npos) {
53 size_str = path.substr(semi_colon_index + 1);
54 SetSize(size_str);
55 }
56
57 std::string identifier = path.substr(0, semi_colon_index);
58 SetIdentifier(identifier);
59
60 return true;
61 }
62
63 bool IconURI::IsValid() {
64 return url_.SchemeIs("icon");
65 }
66
67 std::string IconURI::extension() const { return extension_; }
68
69 std::string IconURI::media_type() const { return media_type_; }
70
71 std::string IconURI::keyword() const {
72 if (keyword_.empty() && extension_.empty() && media_type_.empty())
73 return UNKNOWN;
74
75 return keyword_;
76 }
77
78 IconLoader::IconSize IconURI::GetIconLoaderSize() const {
79 // TODO(plafayette): Support more sizes. Scale when necessary.
80 if (size_ >= LARGE)
81 return IconLoader::LARGE;
82 else if (size_ >= MEDIUM)
83 return IconLoader::NORMAL;
84
85 return IconLoader::SMALL;
86 }
87
88 int IconURI::size() const { return size_; }
89
90 void IconURI::SetIdentifier(const std::string& identifier) {
91 if (!identifier.empty() && identifier[0] == '.') {
92 SetExtension(identifier);
93 return;
94 }
95
96 if (identifier.find(':') != std::string::npos) {
97 SetMediaType(identifier);
98 return;
99 }
100
101 if (identifier.compare(DIRECTORY) == 0)
102 keyword_ = DIRECTORY;
103 else if (identifier.compare(PARENT_DIRECTORY) == 0)
104 keyword_ = PARENT_DIRECTORY;
105 else if (identifier.compare(UNKNOWN) == 0)
106 keyword_ = UNKNOWN; // Use default "unknown" icon image.
107 }
108
109 void IconURI::SetExtension(const std::string& ext) {
110 if (!ext.empty() && ext[0] != '.')
111 return;
112
113 extension_ = ext;
114 }
115
116 void IconURI::SetMediaType(const std::string& media_type) {
117 size_t colon_index = media_type.find(':');
118 if (colon_index == std::string::npos || colon_index == 0)
119 return;
120
121 media_type_ = media_type;
122 media_type_[colon_index] = '/'; // Use the normal media type separator.
123 }
124
125 void IconURI::SetSize(const std::string& size_str) {
126 int size = atoi(size_str.c_str());
127 if (size > 0) {
128 size_ = size;
129 return;
130 }
131
132 if (size_str.compare(LARGE_ID) == 0)
133 size_ = LARGE;
134 else if (size_str.compare(MEDIUM_ID) == 0)
135 size_ = MEDIUM;
136 else
137 size_ = SMALL; // Default to a 16x16 icon.
138 }
139
140
141 class IconJobSource : public base::RefCountedThreadSafe<IconJobSource> {
142 public:
143 typedef int RequestID;
144
145 IconJobSource() {}
146
147 ~IconJobSource() {
148 cancelable_consumer_.CancelAllRequests();
149 }
150
151 void StartDataRequest(const IconURI& icon_uri, RequestID request_id) {
152 IconManager* im = g_browser_process->icon_manager();
153
154 // TODO(plafayette): Resolve icons by content type and keyword.
155 std::string extension = icon_uri.extension();
156
157 // On Windows, the IconLoader resolves icons by file extension. To get the
158 // generic file icon, we provide an unknown file extension.
159 extension = extension.empty() ? ".unknown" : extension;
160
161 FilePath file_path(UTF8ToWide(extension));
162 IconLoader::IconSize size = icon_uri.GetIconLoaderSize();
163
164 SkBitmap* icon = im->LookupIcon(file_path, size);
165
166 if (icon) {
167 scoped_refptr<RefCountedBytes> icon_data = new RefCountedBytes;
168 gfx::PNGCodec::EncodeBGRASkBitmap(*icon, false, &icon_data->data);
169
170 SendResponse(icon_data, request_id);
171 } else {
172 // Icon was not in cache, go fetch it slowly.
173 IconManager::Handle h = im->LoadIcon(file_path,
174 size,
175 &cancelable_consumer_,
176 NewCallback(this, &IconJobSource::OnFileIconDataAvailable));
177
178 cancelable_consumer_.SetClientData(im, h, request_id);
179 }
180 }
181
182 void OnFileIconDataAvailable(IconManager::Handle handle,
183 SkBitmap* icon) {
184 IconManager* im = g_browser_process->icon_manager();
185 int request_id = cancelable_consumer_.GetClientData(im, handle);
186
187 if (icon) {
188 scoped_refptr<RefCountedBytes> icon_data = new RefCountedBytes;
189 gfx::PNGCodec::EncodeBGRASkBitmap(*icon, false, &icon_data->data);
190
191 SendResponse(icon_data, request_id);
192 } else {
193 SendResponse(NULL, request_id);
194 }
195 }
196
197 void SendResponse(RefCountedMemory* bytes, RequestID request_id) {
198 ChromeThread::PostTask(
199 ChromeThread::IO, FROM_HERE,
200 NewRunnableMethod(Singleton<IconURLDataManager>::get(),
201 &IconURLDataManager::DataAvailable,
202 request_id,
203 scoped_refptr<RefCountedMemory>(bytes)));
204 }
205
206 private:
207 CancelableRequestConsumerT<int, 0> cancelable_consumer_;
208 };
209
210
211 // Definition of IconURLDataManager
212
213 IconURLDataManager::IconURLDataManager()
214 : data_source_(new IconJobSource()),
215 next_request_id_(0) {
216 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
217 }
218
219 IconURLDataManager::~IconURLDataManager() {}
220
221 URLRequestJob* IconURLDataManager::Factory(URLRequest* request,
222 const std::string& scheme) {
223 return new URLRequestIconJob(request);
224 }
225
226 void IconURLDataManager::StartDataRequest(const IconURI& icon_uri,
227 URLRequestIconJob* job) {
228 RequestID request_id = next_request_id_++;
229 // Save this request so we know where to send the data.
230 pending_requests_.insert(std::make_pair(request_id, job));
231
232 data_source_->StartDataRequest(icon_uri, request_id);
233 }
234
235 void IconURLDataManager::DataAvailable(RequestID request_id,
236 scoped_refptr<RefCountedMemory> bytes) {
237 PendingRequestMap::iterator i = pending_requests_.find(request_id);
238 if (i != pending_requests_.end()) {
239 // We acquire a reference to the job so that it doesn't disappear under the
240 // feet of any method invoked here (we could trigger a callback).
241 scoped_refptr<URLRequestIconJob> job = i->second;
242 pending_requests_.erase(i);
243 job->DataAvailable(bytes);
244 }
245 }
246
247 void IconURLDataManager::RemoveRequest(URLRequestIconJob* job) {
248 // Remove the request from our list of pending requests.
249 // If/when the source sends the data that was requested, the data will just
250 // be thrown away.
251 for (PendingRequestMap::iterator i = pending_requests_.begin();
252 i != pending_requests_.end(); ++i) {
253 if (i->second == job) {
254 pending_requests_.erase(i);
255 return;
256 }
257 }
258 }
259
260 bool IconURLDataManager::HasPendingJob(URLRequestIconJob* job) const {
261 for (PendingRequestMap::const_iterator i = pending_requests_.begin();
262 i != pending_requests_.end(); ++i) {
263 if (i->second == job)
264 return true;
265 }
266
267 return false;
268 }
269
270 // URLRequestJob definition.
271
272 URLRequestIconJob::URLRequestIconJob(URLRequest* request)
273 : URLRequestJob(request),
274 data_offset_(0),
275 pending_buf_size_(0) {}
276
277 void URLRequestIconJob::Start() {
278 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
279 this, &URLRequestIconJob::StartAsync));
280 }
281
282 void URLRequestIconJob::Kill() {
283 Singleton<IconURLDataManager>()->RemoveRequest(this);
284 }
285
286 bool URLRequestIconJob::ReadRawData(net::IOBuffer* buf,
287 int buf_size,
288 int *bytes_read) {
289 if (!data_.get()) {
290 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
291 DCHECK(!pending_buf_.get());
292 CHECK(buf->data());
293 pending_buf_ = buf;
294 pending_buf_size_ = buf_size;
295 return false; // Tell the caller we're still waiting for data.
296 }
297
298 // Otherwise, the data is available.
299 CompleteRead(buf, buf_size, bytes_read);
300 return true;
301 }
302
303 bool URLRequestIconJob::GetMimeType(std::string* mime_type) const {
304 // Rely on image decoder inferring the correct type.
305 return true;
306 }
307
308 void URLRequestIconJob::SetMimeType(const std::string& mime_type) {
309 mime_type_ = mime_type;
310 }
311
312 URLRequestIconJob::~URLRequestIconJob() {
313 CHECK(!Singleton<IconURLDataManager>()->HasPendingJob(this));
314 }
315
316 void URLRequestIconJob::StartAsync() {
317 if (!request_)
318 return;
319
320 if (StartRequest()) {
321 NotifyHeadersComplete();
322 } else {
323 NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED,
324 net::ERR_INVALID_URL));
325 }
326 }
327
328 bool URLRequestIconJob::StartRequest() {
329 IconURI icon_uri;
330 icon_uri.ParseIconURI(request_->url());
331
332 ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, NewRunnableMethod(
333 Singleton<IconURLDataManager>::get(),
334 &IconURLDataManager::StartDataRequest,
335 icon_uri,
336 this));
337
338 return true;
339 }
340
341 void URLRequestIconJob::DataAvailable(RefCountedMemory* bytes) {
342 if (bytes) {
343 // The request completed, and we have all the data.
344 // Clear any IO pending status.
345 SetStatus(URLRequestStatus());
346
347 data_ = bytes;
348 int bytes_read;
349 if (pending_buf_.get()) {
350 CHECK(pending_buf_->data());
351 CompleteRead(pending_buf_, pending_buf_size_, &bytes_read);
352 pending_buf_ = NULL;
353 NotifyReadComplete(bytes_read);
354 }
355 } else {
356 // The request failed.
357 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, 0));
358 }
359 }
360
361 void URLRequestIconJob::CompleteRead(net::IOBuffer* buf,
362 int buf_size,
363 int* bytes_read) {
364 int remaining = static_cast<int>(data_->size()) - data_offset_;
365 if (buf_size > remaining)
366 buf_size = remaining;
367 if (buf_size > 0) {
368 memcpy(buf->data(), data_->front() + data_offset_, buf_size);
369 data_offset_ += buf_size;
370 }
371 *bytes_read = buf_size;
372 }
373
374 void RegisterURLRequestIconJob() {
375 // Initialize.
376 Singleton<IconURLDataManager>::get();
377
378 URLRequest::RegisterProtocolFactory(chrome::kIconURLScheme,
379 &IconURLDataManager::Factory);
380 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698