File: large_icon_worker.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (242 lines) | stat: -rw-r--r-- 9,792 bytes parent folder | download | duplicates (6)
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
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/favicon/core/large_icon_worker.h"

#include <vector>

#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/no_destructor.h"
#include "base/task/task_runner.h"
#include "base/task/thread_pool.h"
#include "components/favicon/core/favicon_service.h"
#include "components/favicon_base/fallback_icon_style.h"
#include "skia/ext/image_operations.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/geometry/size.h"

namespace favicon {

// The resized and decoded images generated from LargeIconService are sometimes
// (but not always) shown to the user, so the task priority was increased from
// BEST_EFFORT to USER_VISIBLE. If this potentially expensive change causes any
// issues, enable the kill switch below.
BASE_FEATURE(kLargeIconWorkerTaskPriorityKillSwitch,
             "LargeIconWorkerTaskPriorityKillSwitch",
             base::FEATURE_DISABLED_BY_DEFAULT);

using NoBigEnoughIconBehavior = LargeIconService::NoBigEnoughIconBehavior;

namespace {

bool ShouldReturnBitmap(const favicon_base::FaviconRawBitmapResult& db_result,
                        int min_source_size,
                        NoBigEnoughIconBehavior no_big_enough_icon_behavior) {
  return db_result.is_valid() &&
         db_result.pixel_size.width() == db_result.pixel_size.height() &&
         (db_result.pixel_size.width() >= min_source_size ||
          no_big_enough_icon_behavior ==
              NoBigEnoughIconBehavior::kReturnBitmap);
}

// Wraps the PNG data in `db_result` in a gfx::Image. If `desired_size` is not
// 0, the image gets decoded and resized to `desired_size` (in px). Must run on
// a background thread in production.
gfx::Image ResizeLargeIconOnBackgroundThread(
    const favicon_base::FaviconRawBitmapResult& db_result,
    int desired_size) {
  gfx::Image image = gfx::Image::CreateFrom1xPNGBytes(db_result.bitmap_data);

  if (desired_size == 0 || db_result.pixel_size.width() == desired_size) {
    return image;
  }

  SkBitmap resized = skia::ImageOperations::Resize(
      image.AsBitmap(), skia::ImageOperations::RESIZE_LANCZOS3, desired_size,
      desired_size);
  return gfx::Image::CreateFrom1xBitmap(resized);
}

// Returns `fallback_icon_style` or a default value picked based on
// `no_big_enough_icon_behavior`.
std::unique_ptr<favicon_base::FallbackIconStyle> FallbackIconStyleOrDefault(
    std::unique_ptr<favicon_base::FallbackIconStyle> fallback_icon_style,
    LargeIconService::NoBigEnoughIconBehavior no_big_enough_icon_behavior) {
  if (no_big_enough_icon_behavior !=
      NoBigEnoughIconBehavior::kReturnFallbackColor) {
    return nullptr;
  }
  return fallback_icon_style
             ? std::move(fallback_icon_style)
             : std::make_unique<favicon_base::FallbackIconStyle>();
}

void LogFallbackIconSizeIfNeeded(
    int favicon_width_according_to_database,
    LargeIconService::NoBigEnoughIconBehavior no_big_enough_icon_behavior) {
  if (no_big_enough_icon_behavior !=
      NoBigEnoughIconBehavior::kReturnFallbackColor) {
    return;
  }
  // The size must be positive, we cap to 128 to avoid the sparse histogram
  // to explode (having too many different values, server-side). Size 128
  // already indicates that there is a problem in the code, 128 px _should_
  // be enough in all current UI surfaces.
  DCHECK_GE(favicon_width_according_to_database, 0);
  favicon_width_according_to_database =
      std::min(favicon_width_according_to_database, 128);
  base::UmaHistogramSparse("Favicons.LargeIconService.FallbackSize",
                           favicon_width_according_to_database);
}

}  // namespace

LargeIconWorker::LargeIconWorker(
    int min_source_size_in_pixel,
    int size_in_pixel_to_resize_to,
    NoBigEnoughIconBehavior no_big_enough_icon_behavior,
    favicon_base::LargeIconCallback raw_bitmap_callback,
    favicon_base::LargeIconImageCallback image_callback,
    base::CancelableTaskTracker* tracker)
    : min_source_size_in_pixel_(min_source_size_in_pixel),
      size_in_pixel_to_resize_to_(size_in_pixel_to_resize_to),
      no_big_enough_icon_behavior_(no_big_enough_icon_behavior),
      raw_bitmap_callback_(std::move(raw_bitmap_callback)),
      image_callback_(std::move(image_callback)),
      background_task_runner_(base::ThreadPool::CreateTaskRunner(
          {base::MayBlock(),
           base::FeatureList::IsEnabled(kLargeIconWorkerTaskPriorityKillSwitch)
               ? base::TaskPriority::BEST_EFFORT
               : base::TaskPriority::USER_VISIBLE,
           base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
      tracker_(tracker) {}

LargeIconWorker::~LargeIconWorker() = default;

void LargeIconWorker::OnIconLookupComplete(
    const favicon_base::FaviconRawBitmapResult& db_result) {
  icon_url_ = db_result.icon_url;
  favicon_width_according_to_database_ =
      db_result.is_valid() ? db_result.pixel_size.width() : 0;
  fallback_icon_style_ = std::make_unique<favicon_base::FallbackIconStyle>();

  if (ShouldReturnBitmap(db_result, min_source_size_in_pixel_,
                         no_big_enough_icon_behavior_)) {
    tracker_->PostTaskAndReply(
        background_task_runner_.get(), FROM_HERE,
        base::BindOnce(&LargeIconWorker::ResizeAndEncodeOnBackgroundThread,
                       this, db_result),
        base::BindOnce(&LargeIconWorker::OnIconProcessingComplete, this));
    return;
  }
  if (no_big_enough_icon_behavior_ ==
          NoBigEnoughIconBehavior::kReturnFallbackColor &&
      db_result.is_valid()) {
    tracker_->PostTaskAndReply(
        background_task_runner_.get(), FROM_HERE,
        base::BindOnce(&LargeIconWorker::ComputeDominantColorOnBackgroundThread,
                       this, db_result.bitmap_data),
        base::BindOnce(&LargeIconWorker::OnIconProcessingComplete, this));
    return;
  }

  OnIconProcessingComplete();
}

// static
base::CancelableTaskTracker::TaskId LargeIconWorker::GetLargeIconRawBitmap(
    FaviconService* favicon_service,
    const GURL& page_url,
    int min_source_size_in_pixel,
    int size_in_pixel_to_resize_to,
    NoBigEnoughIconBehavior no_big_enough_icon_behavior,
    favicon_base::LargeIconCallback raw_bitmap_callback,
    favicon_base::LargeIconImageCallback image_callback,
    base::CancelableTaskTracker* tracker) {
  DCHECK_LE(1, min_source_size_in_pixel);
  DCHECK_LE(0, size_in_pixel_to_resize_to);

  auto worker = base::MakeRefCounted<LargeIconWorker>(
      min_source_size_in_pixel, size_in_pixel_to_resize_to,
      no_big_enough_icon_behavior, std::move(raw_bitmap_callback),
      std::move(image_callback), tracker);

  int max_size_in_pixel =
      std::max(size_in_pixel_to_resize_to, min_source_size_in_pixel);

  static const base::NoDestructor<std::vector<favicon_base::IconTypeSet>>
      large_icon_types({{favicon_base::IconType::kWebManifestIcon},
                        {favicon_base::IconType::kFavicon},
                        {favicon_base::IconType::kTouchIcon},
                        {favicon_base::IconType::kTouchPrecomposedIcon}});

  // TODO(beaudoin): For now this is just a wrapper around
  //   GetLargestRawFaviconForPageURL. Add the logic required to select the
  //   best possible large icon. Also add logic to fetch-on-demand when the
  //   URL of a large icon is known but its bitmap is not available.
  return favicon_service->GetLargestRawFaviconForPageURL(
      page_url, *large_icon_types, max_size_in_pixel,
      base::BindOnce(&LargeIconWorker::OnIconLookupComplete, worker), tracker);
}

void LargeIconWorker::ResizeAndEncodeOnBackgroundThread(
    const favicon_base::FaviconRawBitmapResult& db_result) {
  gfx::Image image =
      ResizeLargeIconOnBackgroundThread(db_result, size_in_pixel_to_resize_to_);
  if (image.IsEmpty()) {
    return;
  }

  if (raw_bitmap_callback_) {
    raw_bitmap_result_ = db_result;
    if (size_in_pixel_to_resize_to_ != 0) {
      raw_bitmap_result_.pixel_size =
          gfx::Size(size_in_pixel_to_resize_to_, size_in_pixel_to_resize_to_);
    }
    raw_bitmap_result_.bitmap_data = image.As1xPNGBytes();
  }
  if (image_callback_) {
    bitmap_result_ = image.AsBitmap();
  }
}

void LargeIconWorker::ComputeDominantColorOnBackgroundThread(
    scoped_refptr<base::RefCountedMemory> favicon_bytes) {
  favicon_base::SetDominantColorAsBackground(*favicon_bytes,
                                             fallback_icon_style_.get());
}

void LargeIconWorker::OnIconProcessingComplete() {
  // If `raw_bitmap_callback_` is provided, return the raw result.
  if (raw_bitmap_callback_) {
    if (raw_bitmap_result_.is_valid()) {
      std::move(raw_bitmap_callback_)
          .Run(favicon_base::LargeIconResult(raw_bitmap_result_));
      return;
    }
    LogFallbackIconSizeIfNeeded(favicon_width_according_to_database_,
                                no_big_enough_icon_behavior_);
    std::move(raw_bitmap_callback_)
        .Run(favicon_base::LargeIconResult(FallbackIconStyleOrDefault(
            std::move(fallback_icon_style_), no_big_enough_icon_behavior_)));
    return;
  }

  if (!bitmap_result_.isNull()) {
    std::move(image_callback_)
        .Run(favicon_base::LargeIconImageResult(
            gfx::Image::CreateFrom1xBitmap(bitmap_result_), icon_url_));
    return;
  }
  LogFallbackIconSizeIfNeeded(favicon_width_according_to_database_,
                              no_big_enough_icon_behavior_);
  std::move(image_callback_)
      .Run(favicon_base::LargeIconImageResult(FallbackIconStyleOrDefault(
          std::move(fallback_icon_style_), no_big_enough_icon_behavior_)));
}

}  // namespace favicon