1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
// Copyright 2015 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/favicon/favicon_loader.h"
#import <UIKit/UIKit.h>
#import "base/mac/foundation_util.h"
#import "base/mac/scoped_block.h"
#include "base/strings/sys_string_conversions.h"
#include "components/favicon/core/favicon_service.h"
#include "components/favicon_base/favicon_callback.h"
#include "ui/gfx/favicon_size.h"
#include "url/gurl.h"
struct FaviconLoader::RequestData {
RequestData() {}
RequestData(NSString* key, FaviconLoader::ImageCompletionBlock block)
: key([key copy]), block(block, base::scoped_policy::RETAIN) {}
~RequestData() {}
base::scoped_nsobject<NSString> key;
base::mac::ScopedBlock<FaviconLoader::ImageCompletionBlock> block;
};
FaviconLoader::FaviconLoader(favicon::FaviconService* favicon_service)
: favicon_service_(favicon_service),
favicon_cache_([[NSMutableDictionary dictionaryWithCapacity:10] retain]) {
}
FaviconLoader::~FaviconLoader() {}
// TODO(pinkerton): How do we update the favicon if it's changed on the web?
// We can possibly just rely on this class being purged or the app being killed
// to reset it, but then how do we ensure the FaviconService is updated?
UIImage* FaviconLoader::ImageForURL(const GURL& url,
int types,
ImageCompletionBlock block) {
DCHECK(thread_checker_.CalledOnValidThread());
NSString* key = base::SysUTF8ToNSString(url.spec());
id value = [favicon_cache_ objectForKey:key];
if (value) {
// [NSNull null] returns a singleton, so we can use it as a sentinel value
// and just compare pointers to validate whether the value is the sentinel
// or a valid UIImage.
if (value == [NSNull null])
return [UIImage imageNamed:@"default_favicon"];
return base::mac::ObjCCastStrict<UIImage>(value);
}
// Kick off an async request for the favicon.
if (favicon_service_) {
int size = gfx::kFaviconSize * [UIScreen mainScreen].scale;
scoped_ptr<RequestData> request_data(new RequestData(key, block));
favicon_base::FaviconResultsCallback callback =
base::Bind(&FaviconLoader::OnFaviconAvailable, base::Unretained(this),
base::Passed(&request_data));
favicon_service_->GetFaviconForPageURL(url, types, size, callback,
&cancelable_task_tracker_);
}
return [UIImage imageNamed:@"default_favicon"];
}
void FaviconLoader::PurgeCache() {
DCHECK(thread_checker_.CalledOnValidThread());
cancelable_task_tracker_.TryCancelAll();
favicon_cache_.reset(
[[NSMutableDictionary dictionaryWithCapacity:10] retain]);
}
void FaviconLoader::OnFaviconAvailable(
scoped_ptr<RequestData> request_data,
const std::vector<favicon_base::FaviconRawBitmapResult>&
favicon_bitmap_results) {
DCHECK(request_data);
DCHECK(thread_checker_.CalledOnValidThread());
if (favicon_bitmap_results.size() < 1 ||
!favicon_bitmap_results[0].is_valid()) {
// Return early if there were no results or if it is invalid, after adding a
// "no favicon" entry to the cache so that we don't keep trying to fetch a
// missing favicon over and over.
[favicon_cache_ setObject:[NSNull null] forKey:request_data->key];
return;
}
// The favicon code assumes favicons are PNG-encoded.
NSData* image_data =
[NSData dataWithBytes:favicon_bitmap_results[0].bitmap_data->front()
length:favicon_bitmap_results[0].bitmap_data->size()];
UIImage* favicon =
[UIImage imageWithData:image_data scale:[[UIScreen mainScreen] scale]];
[favicon_cache_ setObject:favicon forKey:request_data->key];
// Call the block to tell the caller this is complete.
if (request_data->block)
(request_data->block.get())(favicon);
}
|