summaryrefslogtreecommitdiff
path: root/chromium/components/favicon/core/favicon_handler.h
blob: 631d3bf584b939e307ec6a4c07e2f69380bc2a14 (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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
// Copyright (c) 2012 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.

#ifndef COMPONENTS_FAVICON_CORE_FAVICON_HANDLER_H_
#define COMPONENTS_FAVICON_CORE_FAVICON_HANDLER_H_

#include <stddef.h>

#include <vector>

#include "base/callback_forward.h"
#include "base/cancelable_callback.h"
#include "base/containers/flat_set.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/task/cancelable_task_tracker.h"
#include "components/favicon/core/favicon_driver_observer.h"
#include "components/favicon/core/favicon_url.h"
#include "components/favicon_base/favicon_callback.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/gfx/favicon_size.h"
#include "ui/gfx/image/image.h"
#include "url/gurl.h"

class SkBitmap;

namespace favicon {

class CoreFaviconService;

// FaviconHandler works with FaviconDriver to fetch the specific type of
// favicon.
//
// FetchFavicon() requests favicons from CoreFaviconService. CoreFaviconService
// is typically backed by a database (such as HistoryService) and
// asynchronously returns results. At this point FaviconHandler only knows the
// url of the page, and not necessarily the url of the favicon(s). To ensure
// FaviconHandler handles reloading stale favicons as well as reloading a
// favicon on page reload Faviconhandler always requests the favicon from the
// CoreFaviconService regardless of whether the active favicon is valid.
//
// After the navigation two types of events are delivered (which is
// first depends upon who is faster): notification from the CoreFaviconService
// of the request for the favicon
// (OnFaviconDataForInitialURLFromFaviconService), or the favicon urls from
// the page (OnUpdateCandidates()).
// . If CoreFaviconService has a valid up to date favicon for the page, use it.
// . When OnUpdateCandidates() is called, if it matches that of the current page
//   and the current page's favicon is set, do nothing (everything is up to
//   date).
// . On the other hand, if CoreFaviconService does not know the favicon for
//   url, or the favicon is out date, or the favicon urls do not match,
//   then download the favicon. Downloading only happens once
//   CoreFaviconService has completed the request *and* the set of urls is
//   known.
//
// DownloadCurrentCandidateOrAskFaviconService() does the following:
// . If the current favicon is valid but expired, download a new icon.
// . Otherwise ask CoreFaviconService to update the mapping from page url to
//   favicon url and call us back with the favicon. Remember, it is
//   possible for the CoreFaviconService to already have the favicon, just not
//   the mapping between page and favicon url. The callback for this is
//   OnFaviconData().
//
// OnFaviconData() either updates the favicon of the current page (if
// CoreFaviconService knew about the favicon), or requests the renderer to
// download the favicon.
//
// When the renderer downloads favicons, it considers the entire list of
// favicon candidates, if |download_largest_favicon_| is true, the largest
// favicon will be used, otherwise the one that best matches the preferred size
// is chosen (or the first one if there is no preferred  size). Once the
// matching favicon has been determined, SetFavicon is called which updates
// the page's favicon and notifies the database to save the favicon.

class FaviconHandler {
 public:
  class Delegate {
   public:
    // Mimics WebContents::ImageDownloadCallback.
    using ImageDownloadCallback = base::OnceCallback<void(
        int id,
        int status_code,
        const GURL& image_url,
        const std::vector<SkBitmap>& bitmaps,
        const std::vector<gfx::Size>& original_bitmap_sizes)>;

    using ManifestDownloadCallback = base::OnceCallback<void(
        const std::vector<favicon::FaviconURL>& favicons)>;

    // Starts the download for the given favicon. When finished, the callback
    // is called with the results. Returns the unique id of the download
    // request, which will also be passed to the callback. In case of error, 0
    // is returned and no callback will be called.
    // Bitmaps with pixel sizes larger than |max_bitmap_size| are filtered out
    // from the bitmap results. If there are no bitmap results <=
    // |max_bitmap_size|, the smallest bitmap is resized to |max_bitmap_size|
    // and is the only result. A |max_bitmap_size| of 0 means unlimited.
    virtual int DownloadImage(const GURL& url,
                              int max_image_size,
                              ImageDownloadCallback callback) = 0;

    // Downloads a WebManifest and returns the favicons listed there.
    virtual void DownloadManifest(const GURL& url,
                                  ManifestDownloadCallback callback) = 0;

    // Returns whether the user is operating in an off-the-record context.
    virtual bool IsOffTheRecord() = 0;

    // Notifies that the favicon image has been updated. Most delegates
    // propagate the notification to FaviconDriverObserver::OnFaviconUpdated().
    // See its documentation for details.
    virtual void OnFaviconUpdated(
        const GURL& page_url,
        FaviconDriverObserver::NotificationIconType notification_icon_type,
        const GURL& icon_url,
        bool icon_url_changed,
        const gfx::Image& image) = 0;

    // Notifies that a page that used to have a favicon (reported via
    // OnFaviconUpdated() above) stopped having it (e.g. it was removed via
    // javascript).
    virtual void OnFaviconDeleted(
        const GURL& page_url,
        FaviconDriverObserver::NotificationIconType notification_icon_type) = 0;
  };

  // |service| may be null (which means favicons are not saved). If |service|
  // is non-null it must outlive this class. |delegate| must not be nullptr and
  // must outlive this class.
  FaviconHandler(CoreFaviconService* service,
                 Delegate* delegate,
                 FaviconDriverObserver::NotificationIconType handler_type);
  ~FaviconHandler();

  // Initiates loading the favicon for the specified url. |is_same_document| is
  // true for fragment navigations and history pushState/replaceState, see
  // NavigationHandle::IsSameDocument().
  void FetchFavicon(const GURL& page_url, bool is_same_document);

  // Collects the candidate favicons as listed in the HTML head, as well as
  // the WebManifest URL if available (or empty URL otherwise).
  void OnUpdateCandidates(const GURL& page_url,
                          const std::vector<favicon::FaviconURL>& candidates,
                          const GURL& manifest_url);

  // Returns the supported icon types, inferred from the handler type as passed
  // in the constructor.
  const favicon_base::IconTypeSet& icon_types() const { return icon_types_; }

  // For testing.
  const std::vector<GURL> GetIconURLs() const;

  // Returns whether the handler is waiting for a download to complete or for
  // data from the CoreFaviconService. Reserved for testing.
  bool HasPendingTasksForTest();

  // Get the maximal icon size in pixels for a handler of type |handler_type|.
  // |candidates_from_web_manifest| represents whether the icons are coming
  // from a Web Manifest.
  static int GetMaximalIconSize(
      FaviconDriverObserver::NotificationIconType handler_type,
      bool candidates_from_web_manifest);

 private:
  // Used to track a candidate for the favicon.
  struct FaviconCandidate {
    // Builds a scored candidate by selecting the best bitmap sizes.
    static FaviconCandidate FromFaviconURL(
        const favicon::FaviconURL& favicon_url,
        const std::vector<int>& desired_pixel_sizes,
        bool want_largest_icon);

    // Compare function used for std::stable_sort to sort in descending order.
    static bool CompareScore(const FaviconCandidate& lhs,
                             const FaviconCandidate& rhs) {
      return lhs.score > rhs.score;
    }

    GURL icon_url;
    favicon_base::IconType icon_type = favicon_base::IconType::kInvalid;
    float score = 0;
  };

  struct DownloadedFavicon {
    FaviconCandidate candidate;
    gfx::Image image;
  };

  // Returns the set of relevant favicon_base::IconType values based on the
  // handler's type.
  static favicon_base::IconTypeSet GetIconTypesFromHandlerType(
      FaviconDriverObserver::NotificationIconType handler_type);

  // Called with the result of looking up cached icon data for the manifest's
  // URL, which is used as icon URL.
  void OnFaviconDataForManifestFromFaviconService(
      const std::vector<favicon_base::FaviconRawBitmapResult>&
          favicon_bitmap_results);

  // Called when the dowloading of a manifest completes.
  void OnDidDownloadManifest(const std::vector<FaviconURL>& candidates);

  // Called when the actual list of favicon candidates to be processed is
  // available, which can be either icon URLs listed in the HTML head instead
  // or, if a Web Manifest was provided, the list of icons there.
  void OnGotFinalIconURLCandidates(const std::vector<FaviconURL>& candidates);

  // Called when the history request for favicon data mapped to |url_| has
  // completed and the renderer has told us the icon URLs used by |url_|,
  // including the case where no relevant candidates exists.
  void OnGotInitialHistoryDataAndIconURLCandidates();

  // See description above class for details.
  void OnFaviconDataForInitialURLFromFaviconService(const std::vector<
      favicon_base::FaviconRawBitmapResult>& favicon_bitmap_results);

  // If the favicon currently mapped to |url_| has expired, downloads the
  // current candidate favicon from the renderer. Otherwise requests data for
  // the current favicon from history. If data is requested from history,
  // OnFaviconData() is called with the history data once it has been retrieved.
  void DownloadCurrentCandidateOrAskFaviconService();

  // Requests the favicon for |icon_url| from the favicon service. Unless in
  // incognito, it also updates the page URL (url_) to |icon_url| mappings.
  void GetFaviconAndUpdateMappingsUnlessIncognito(
      const GURL& icon_url,
      favicon_base::IconType icon_type,
      favicon_base::FaviconResultsCallback callback);

  // See description above class for details.
  void OnFaviconData(const std::vector<favicon_base::FaviconRawBitmapResult>&
                         favicon_bitmap_results);

  // Schedules a download for the specified entry. This adds the request to
  // image_download_requests_.
  void ScheduleImageDownload(const GURL& image_url,
                             favicon_base::IconType icon_type);

  // Triggered when a download of an image has finished. |bitmaps| and
  // |original_bitmap_sizes| must contain the same number of elements (i.e. same
  // vector size).
  void OnDidDownloadFavicon(
      favicon_base::IconType icon_type,
      int id,
      int http_status_code,
      const GURL& image_url,
      const std::vector<SkBitmap>& bitmaps,
      const std::vector<gfx::Size>& original_bitmap_sizes);

  // Returns true if the next candidate in |final_candidates_| should be
  // downloaded.
  bool ShouldDownloadNextCandidate() const;

  // Sets the image data for the favicon.
  void SetFavicon(const GURL& icon_url,
                  const gfx::Image& image,
                  favicon_base::IconType icon_type);

  // Deletes the favicon mappings for |page_urls_|, if:
  // - We're not in incognito.
  // - A mapping is known to exist (reflected by |notification_icon_type_|).
  // - All download attempts returned 404s OR no relevant candidate was
  //   provided (as per |icon_types_|).
  void MaybeDeleteFaviconMappings();

  // Notifies |driver_| that FaviconHandler found an icon which matches the
  // |handler_type_| criteria. NotifyFaviconUpdated() can be called multiple
  // times for the same page if:
  // - a better match is found for |handler_type_| (e.g. newer bitmap data)
  // - Javascript changes the page's icon URLs.
  void NotifyFaviconUpdated(
      const std::vector<favicon_base::FaviconRawBitmapResult>&
          favicon_bitmap_results);
  void NotifyFaviconUpdated(const GURL& icon_url,
                            favicon_base::IconType icon_type,
                            const gfx::Image& image);

  // Return the current candidate if any.
  const FaviconCandidate* current_candidate() const {
    DCHECK(final_candidates_);
    return current_candidate_index_ < final_candidates_->size()
               ? &final_candidates_.value()[current_candidate_index_]
               : nullptr;
  }

  // Returns the preferred size of the image. 0 means no preference (any size
  // will do).
  int preferred_icon_size() const {
    return download_largest_icon_ ? 0 : gfx::kFaviconSize;
  }

  // Used for the GetFaviconForPageURL() request looking up the page URL,
  // triggered in FetchFavicon().
  base::CancelableTaskTracker cancelable_task_tracker_for_page_url_;

  // Used for various CoreFaviconService methods triggered while processing
  // candidates.
  base::CancelableTaskTracker cancelable_task_tracker_for_candidates_;

  const FaviconDriverObserver::NotificationIconType handler_type_;

  // URL of the page(s) we're requesting the favicon for. They can be multiple
  // in case of in-page navigations (e.g. fragment navigations).
  base::flat_set<GURL> page_urls_;
  // The last page URL reported via FetchFavicon().
  GURL last_page_url_;

  // Whether we got data back for the initial request to the CoreFaviconService.
  bool got_favicon_from_history_;

  // Whether the history data returned in
  // OnFaviconDataForInitialURLFromFaviconService() is out of date or is known
  // to be incomplete. If true, it means there is a favicon mapped to |url_| in
  // history, but we need to redownload the icon URLs because the icon in the
  // database has expired or the data in the database is incomplete.
  bool initial_history_result_expired_or_incomplete_;

  // Whether FaviconHandler should ignore history state and determine the
  // optimal icon URL out of |image_urls_| for |url_| by downloading
  // |image_urls_| one by one.
  bool redownload_icons_;

  // Requests to the renderer to download a manifest.
  base::CancelableOnceCallback<Delegate::ManifestDownloadCallback::RunType>
      manifest_download_request_;

  // Requests to the renderer to download favicons.
  base::CancelableOnceCallback<Delegate::ImageDownloadCallback::RunType>
      image_download_request_;

  // The combination of the supported icon types.
  const favicon_base::IconTypeSet icon_types_;

  // Whether the largest icon should be downloaded.
  const bool download_largest_icon_;

  // Whether candidates have been received (OnUpdateCandidates() has been
  // called, regardless of whether the provided list was empty).
  bool candidates_received_;

  // Whether among the processed candidates at least one download attempt
  // resulted in an error other than a 404.
  bool error_other_than_404_found_;

  // The manifest URL from the renderer (or empty URL if none).
  GURL manifest_url_;

  // Original list of candidates provided to OnUpdateCandidates(), stored to
  // be able to fall back to, in case a manifest was provided and downloading it
  // failed (or provided no icons).
  std::vector<FaviconURL> non_manifest_original_candidates_;

  // The prioritized favicon candidates from the page back from the renderer.
  // Populated by OnGotFinalIconURLCandidates().
  absl::optional<std::vector<FaviconCandidate>> final_candidates_;

  // The icon URL and the icon type of the favicon in the most recent
  // FaviconDriver::OnFaviconAvailable() notification.
  GURL notification_icon_url_;
  favicon_base::IconType notification_icon_type_;

  // The CoreFaviconService which implements favicon operations. May be null.
  CoreFaviconService* service_;

  // This handler's delegate.
  Delegate* delegate_;

  // The index of the favicon URL in |image_urls_| which is currently being
  // requested from history or downloaded.
  size_t current_candidate_index_;

  // Best image we've seen so far.  As images are downloaded from the page they
  // are stored here. When a satisfying icon is found (as defined in
  // UpdateFaviconCandidate()), the favicon service and the delegate are
  // notified.
  DownloadedFavicon best_favicon_;

  DISALLOW_COPY_AND_ASSIGN(FaviconHandler);
};

}  // namespace favicon

#endif  // COMPONENTS_FAVICON_CORE_FAVICON_HANDLER_H_