summaryrefslogtreecommitdiffstats
path: root/ios/chrome/browser/favicon/favicon_loader.mm
blob: 5f22de03a1de2d32e73e0b006210fae753d49359 (plain)
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);
}