logo_service_impl.cc 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751
  1. // Copyright 2017 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. #include "components/search_provider_logos/logo_service_impl.h"
  5. #include <algorithm>
  6. #include <memory>
  7. #include <utility>
  8. #include "base/bind.h"
  9. #include "base/callback_helpers.h"
  10. #include "base/command_line.h"
  11. #include "base/feature_list.h"
  12. #include "base/memory/weak_ptr.h"
  13. #include "base/metrics/field_trial_params.h"
  14. #include "base/metrics/histogram_macros.h"
  15. #include "base/task/task_runner_util.h"
  16. #include "base/task/thread_pool.h"
  17. #include "base/threading/thread_task_runner_handle.h"
  18. #include "base/time/default_clock.h"
  19. #include "build/build_config.h"
  20. #include "components/image_fetcher/core/image_decoder.h"
  21. #include "components/search_engines/search_terms_data.h"
  22. #include "components/search_engines/template_url_service.h"
  23. #include "components/search_provider_logos/fixed_logo_api.h"
  24. #include "components/search_provider_logos/google_logo_api.h"
  25. #include "components/search_provider_logos/logo_cache.h"
  26. #include "components/search_provider_logos/logo_observer.h"
  27. #include "components/search_provider_logos/switches.h"
  28. #include "net/http/http_status_code.h"
  29. #include "net/traffic_annotation/network_traffic_annotation.h"
  30. #include "services/network/public/cpp/resource_request.h"
  31. #include "services/network/public/cpp/shared_url_loader_factory.h"
  32. #include "services/network/public/cpp/simple_url_loader.h"
  33. #include "services/network/public/mojom/url_response_head.mojom.h"
  34. #include "ui/gfx/image/image.h"
  35. namespace search_provider_logos {
  36. namespace {
  37. const int64_t kMaxDownloadBytes = 1024 * 1024;
  38. const int kDecodeLogoTimeoutSeconds = 30;
  39. // Implements a callback for image_fetcher::ImageDecoder. If Run() is called on
  40. // a callback returned by GetCallback() within 30 seconds, forwards the decoded
  41. // image to the wrapped callback. If not, sends an empty image to the wrapped
  42. // callback instead. Either way, deletes the object and prevents further calls.
  43. //
  44. // TODO(sfiera): find a more idiomatic way of setting a deadline on the
  45. // callback. This is implemented as a self-deleting object in part because it
  46. // needed to when it used to be a delegate and in part because I couldn't figure
  47. // out a better way, now that it isn't.
  48. class ImageDecodedHandlerWithTimeout {
  49. public:
  50. ImageDecodedHandlerWithTimeout(const ImageDecodedHandlerWithTimeout&) =
  51. delete;
  52. ImageDecodedHandlerWithTimeout& operator=(
  53. const ImageDecodedHandlerWithTimeout&) = delete;
  54. static base::OnceCallback<void(const gfx::Image&)> Wrap(
  55. base::OnceCallback<void(const SkBitmap&)> image_decoded_callback) {
  56. auto* handler =
  57. new ImageDecodedHandlerWithTimeout(std::move(image_decoded_callback));
  58. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
  59. FROM_HERE,
  60. base::BindOnce(&ImageDecodedHandlerWithTimeout::OnImageDecoded,
  61. handler->weak_ptr_factory_.GetWeakPtr(), gfx::Image()),
  62. base::Seconds(kDecodeLogoTimeoutSeconds));
  63. return base::BindOnce(&ImageDecodedHandlerWithTimeout::OnImageDecoded,
  64. handler->weak_ptr_factory_.GetWeakPtr());
  65. }
  66. private:
  67. explicit ImageDecodedHandlerWithTimeout(
  68. base::OnceCallback<void(const SkBitmap&)> image_decoded_callback)
  69. : image_decoded_callback_(std::move(image_decoded_callback)) {}
  70. void OnImageDecoded(const gfx::Image& decoded_image) {
  71. std::move(image_decoded_callback_).Run(decoded_image.AsBitmap());
  72. delete this;
  73. }
  74. base::OnceCallback<void(const SkBitmap&)> image_decoded_callback_;
  75. base::WeakPtrFactory<ImageDecodedHandlerWithTimeout> weak_ptr_factory_{this};
  76. };
  77. void ObserverOnLogoAvailable(LogoObserver* observer,
  78. bool from_cache,
  79. LogoCallbackReason type,
  80. const absl::optional<Logo>& logo) {
  81. switch (type) {
  82. case LogoCallbackReason::DISABLED:
  83. case LogoCallbackReason::CANCELED:
  84. case LogoCallbackReason::FAILED:
  85. break;
  86. case LogoCallbackReason::REVALIDATED:
  87. observer->OnCachedLogoRevalidated();
  88. break;
  89. case LogoCallbackReason::DETERMINED:
  90. observer->OnLogoAvailable(logo ? &logo.value() : nullptr, from_cache);
  91. break;
  92. }
  93. if (!from_cache) {
  94. observer->OnObserverRemoved();
  95. }
  96. }
  97. void RunCallbacksWithDisabled(LogoCallbacks callbacks) {
  98. if (callbacks.on_cached_encoded_logo_available) {
  99. std::move(callbacks.on_cached_encoded_logo_available)
  100. .Run(LogoCallbackReason::DISABLED, absl::nullopt);
  101. }
  102. if (callbacks.on_cached_decoded_logo_available) {
  103. std::move(callbacks.on_cached_decoded_logo_available)
  104. .Run(LogoCallbackReason::DISABLED, absl::nullopt);
  105. }
  106. if (callbacks.on_fresh_encoded_logo_available) {
  107. std::move(callbacks.on_fresh_encoded_logo_available)
  108. .Run(LogoCallbackReason::DISABLED, absl::nullopt);
  109. }
  110. if (callbacks.on_fresh_decoded_logo_available) {
  111. std::move(callbacks.on_fresh_decoded_logo_available)
  112. .Run(LogoCallbackReason::DISABLED, absl::nullopt);
  113. }
  114. }
  115. // Returns whether the metadata for the cached logo indicates that the logo is
  116. // OK to show, i.e. it's not expired or it's allowed to be shown temporarily
  117. // after expiration.
  118. bool IsLogoOkToShow(const LogoMetadata& metadata, base::Time now) {
  119. base::TimeDelta offset = base::Milliseconds(kMaxTimeToLiveMS * 3 / 2);
  120. base::Time distant_past = now - offset;
  121. // Sanity check so logos aren't accidentally cached forever.
  122. if (metadata.expiration_time < distant_past) {
  123. return false;
  124. }
  125. return metadata.can_show_after_expiration || metadata.expiration_time >= now;
  126. }
  127. // Reads the logo from the cache and returns it. Returns NULL if the cache is
  128. // empty, corrupt, expired, or doesn't apply to the current logo URL.
  129. std::unique_ptr<EncodedLogo> GetLogoFromCacheOnFileThread(LogoCache* logo_cache,
  130. const GURL& logo_url,
  131. base::Time now) {
  132. const LogoMetadata* metadata = logo_cache->GetCachedLogoMetadata();
  133. if (!metadata)
  134. return nullptr;
  135. if (metadata->source_url != logo_url || !IsLogoOkToShow(*metadata, now)) {
  136. logo_cache->SetCachedLogo(nullptr);
  137. return nullptr;
  138. }
  139. return logo_cache->GetCachedLogo();
  140. }
  141. void NotifyAndClear(std::vector<EncodedLogoCallback>* encoded_callbacks,
  142. std::vector<LogoCallback>* decoded_callbacks,
  143. LogoCallbackReason type,
  144. const EncodedLogo* encoded_logo,
  145. const Logo* decoded_logo) {
  146. auto opt_encoded_logo =
  147. encoded_logo ? absl::optional<EncodedLogo>(*encoded_logo) : absl::nullopt;
  148. for (EncodedLogoCallback& callback : *encoded_callbacks) {
  149. std::move(callback).Run(type, opt_encoded_logo);
  150. }
  151. encoded_callbacks->clear();
  152. auto opt_decoded_logo =
  153. decoded_logo ? absl::optional<Logo>(*decoded_logo) : absl::nullopt;
  154. for (LogoCallback& callback : *decoded_callbacks) {
  155. std::move(callback).Run(type, opt_decoded_logo);
  156. }
  157. decoded_callbacks->clear();
  158. }
  159. } // namespace
  160. LogoServiceImpl::LogoServiceImpl(
  161. const base::FilePath& cache_directory,
  162. signin::IdentityManager* identity_manager,
  163. TemplateURLService* template_url_service,
  164. std::unique_ptr<image_fetcher::ImageDecoder> image_decoder,
  165. scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
  166. base::RepeatingCallback<bool()> want_gray_logo_getter)
  167. : cache_directory_(cache_directory),
  168. identity_manager_(identity_manager),
  169. template_url_service_(template_url_service),
  170. url_loader_factory_(url_loader_factory),
  171. want_gray_logo_getter_(std::move(want_gray_logo_getter)),
  172. image_decoder_(std::move(image_decoder)),
  173. is_idle_(true),
  174. is_cached_logo_valid_(false),
  175. cache_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
  176. {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
  177. base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
  178. logo_cache_(new LogoCache(cache_directory_),
  179. base::OnTaskRunnerDeleter(cache_task_runner_)) {
  180. identity_manager_->AddObserver(this);
  181. }
  182. LogoServiceImpl::~LogoServiceImpl() = default;
  183. void LogoServiceImpl::Shutdown() {
  184. // The IdentityManager may be destroyed at any point after Shutdown,
  185. // so make sure we drop any references to it.
  186. identity_manager_->RemoveObserver(this);
  187. ReturnToIdle(kDownloadOutcomeNotTracked);
  188. }
  189. void LogoServiceImpl::GetLogo(search_provider_logos::LogoObserver* observer) {
  190. LogoCallbacks callbacks;
  191. callbacks.on_cached_decoded_logo_available =
  192. base::BindOnce(ObserverOnLogoAvailable, observer, true);
  193. callbacks.on_fresh_decoded_logo_available =
  194. base::BindOnce(ObserverOnLogoAvailable, observer, false);
  195. GetLogo(std::move(callbacks), false);
  196. }
  197. void LogoServiceImpl::GetLogo(LogoCallbacks callbacks, bool for_webui_ntp) {
  198. if (!template_url_service_) {
  199. RunCallbacksWithDisabled(std::move(callbacks));
  200. return;
  201. }
  202. const TemplateURL* template_url =
  203. template_url_service_->GetDefaultSearchProvider();
  204. if (!template_url) {
  205. RunCallbacksWithDisabled(std::move(callbacks));
  206. return;
  207. }
  208. const base::CommandLine* command_line =
  209. base::CommandLine::ForCurrentProcess();
  210. GURL logo_url;
  211. if (command_line->HasSwitch(switches::kSearchProviderLogoURL)) {
  212. logo_url = GURL(
  213. command_line->GetSwitchValueASCII(switches::kSearchProviderLogoURL));
  214. } else {
  215. #if BUILDFLAG(IS_ANDROID)
  216. // Non-Google default search engine logos are currently enabled only on
  217. // Android (https://crbug.com/737283).
  218. logo_url = template_url->logo_url();
  219. #endif
  220. }
  221. GURL base_url;
  222. GURL doodle_url;
  223. const bool is_google = template_url->url_ref().HasGoogleBaseURLs(
  224. template_url_service_->search_terms_data());
  225. if (is_google) {
  226. // TODO(treib): Put the Google doodle URL into prepopulated_engines.json.
  227. base_url =
  228. GURL(template_url_service_->search_terms_data().GoogleBaseURLValue());
  229. doodle_url = search_provider_logos::GetGoogleDoodleURL(base_url);
  230. } else {
  231. if (command_line->HasSwitch(switches::kThirdPartyDoodleURL)) {
  232. doodle_url = GURL(
  233. command_line->GetSwitchValueASCII(switches::kThirdPartyDoodleURL));
  234. } else {
  235. doodle_url = template_url->doodle_url();
  236. }
  237. base_url = doodle_url.DeprecatedGetOriginAsURL();
  238. }
  239. if (!logo_url.is_valid() && !doodle_url.is_valid()) {
  240. RunCallbacksWithDisabled(std::move(callbacks));
  241. return;
  242. }
  243. if (!clock_) {
  244. clock_ = base::DefaultClock::GetInstance();
  245. }
  246. const bool use_fixed_logo = !doodle_url.is_valid();
  247. if (use_fixed_logo) {
  248. SetServerAPI(
  249. logo_url,
  250. base::BindRepeating(&search_provider_logos::ParseFixedLogoResponse),
  251. base::BindRepeating(&search_provider_logos::UseFixedLogoUrl));
  252. } else {
  253. // We encode the type of doodle (regular or gray) in the URL so that the
  254. // logo cache gets cleared when that value changes.
  255. GURL prefilled_url = AppendPreliminaryParamsToDoodleURL(
  256. want_gray_logo_getter_.Run(), for_webui_ntp, doodle_url);
  257. SetServerAPI(
  258. prefilled_url,
  259. base::BindRepeating(&search_provider_logos::ParseDoodleLogoResponse,
  260. base_url),
  261. base::BindRepeating(
  262. &search_provider_logos::AppendFingerprintParamToDoodleURL));
  263. }
  264. DCHECK(!logo_url_.is_empty());
  265. DCHECK(callbacks.on_cached_decoded_logo_available ||
  266. callbacks.on_cached_encoded_logo_available ||
  267. callbacks.on_fresh_decoded_logo_available ||
  268. callbacks.on_fresh_encoded_logo_available);
  269. if (callbacks.on_cached_encoded_logo_available) {
  270. on_cached_encoded_logo_.push_back(
  271. std::move(callbacks.on_cached_encoded_logo_available));
  272. }
  273. if (callbacks.on_cached_decoded_logo_available) {
  274. on_cached_decoded_logo_.push_back(
  275. std::move(callbacks.on_cached_decoded_logo_available));
  276. }
  277. if (callbacks.on_fresh_encoded_logo_available) {
  278. on_fresh_encoded_logo_.push_back(
  279. std::move(callbacks.on_fresh_encoded_logo_available));
  280. }
  281. if (callbacks.on_fresh_decoded_logo_available) {
  282. on_fresh_decoded_logo_.push_back(
  283. std::move(callbacks.on_fresh_decoded_logo_available));
  284. }
  285. if (is_idle_) {
  286. is_idle_ = false;
  287. base::PostTaskAndReplyWithResult(
  288. cache_task_runner_.get(), FROM_HERE,
  289. base::BindOnce(&GetLogoFromCacheOnFileThread,
  290. base::Unretained(logo_cache_.get()), logo_url_,
  291. clock_->Now()),
  292. base::BindOnce(&LogoServiceImpl::OnCachedLogoRead,
  293. weak_ptr_factory_.GetWeakPtr()));
  294. } else if (is_cached_logo_valid_) {
  295. NotifyAndClear(&on_cached_encoded_logo_, &on_cached_decoded_logo_,
  296. LogoCallbackReason::DETERMINED, cached_encoded_logo_.get(),
  297. cached_logo_.get());
  298. }
  299. }
  300. void LogoServiceImpl::SetLogoCacheForTests(std::unique_ptr<LogoCache> cache) {
  301. // |logo_cache_| has a custom deleter, which makes the two unique_ptrs
  302. // be different types. so one can't be moved on top of the other.
  303. logo_cache_.reset(std::move(cache).release());
  304. }
  305. void LogoServiceImpl::SetClockForTests(base::Clock* clock) {
  306. clock_ = clock;
  307. }
  308. void LogoServiceImpl::SetServerAPI(
  309. const GURL& logo_url,
  310. const ParseLogoResponse& parse_logo_response_func,
  311. const AppendQueryparamsToLogoURL& append_queryparams_func) {
  312. if (logo_url == logo_url_)
  313. return;
  314. ReturnToIdle(kDownloadOutcomeNotTracked);
  315. logo_url_ = logo_url;
  316. parse_logo_response_func_ = parse_logo_response_func;
  317. append_queryparams_func_ = append_queryparams_func;
  318. }
  319. void LogoServiceImpl::ClearCachedLogo() {
  320. // First cancel any fetch that might be ongoing.
  321. ReturnToIdle(kDownloadOutcomeNotTracked);
  322. // Then clear any cached logo.
  323. SetCachedLogo(nullptr);
  324. }
  325. void LogoServiceImpl::ReturnToIdle(int outcome) {
  326. if (outcome != kDownloadOutcomeNotTracked) {
  327. UMA_HISTOGRAM_ENUMERATION("NewTabPage.LogoDownloadOutcome",
  328. static_cast<LogoDownloadOutcome>(outcome),
  329. DOWNLOAD_OUTCOME_COUNT);
  330. }
  331. // Cancel the current asynchronous operation, if any.
  332. loader_.reset();
  333. weak_ptr_factory_.InvalidateWeakPtrs();
  334. // Reset state.
  335. is_idle_ = true;
  336. cached_logo_.reset();
  337. cached_encoded_logo_.reset();
  338. is_cached_logo_valid_ = false;
  339. // Clear callbacks.
  340. NotifyAndClear(&on_cached_encoded_logo_, &on_cached_decoded_logo_,
  341. LogoCallbackReason::CANCELED, nullptr, nullptr);
  342. NotifyAndClear(&on_fresh_encoded_logo_, &on_fresh_decoded_logo_,
  343. LogoCallbackReason::CANCELED, nullptr, nullptr);
  344. }
  345. void LogoServiceImpl::OnCachedLogoRead(
  346. std::unique_ptr<EncodedLogo> cached_logo) {
  347. DCHECK(!is_idle_);
  348. if (cached_logo && cached_logo->encoded_image) {
  349. // Store the value of logo->encoded_image for use below. This ensures that
  350. // logo->encoded_image is evaluated before base::Passed(&logo), which sets
  351. // logo to NULL.
  352. scoped_refptr<base::RefCountedString> encoded_image =
  353. cached_logo->encoded_image;
  354. image_decoder_->DecodeImage(
  355. encoded_image->data(), gfx::Size(), // No particular size desired.
  356. /*data_decoder=*/nullptr,
  357. ImageDecodedHandlerWithTimeout::Wrap(base::BindOnce(
  358. &LogoServiceImpl::OnLightCachedImageDecoded,
  359. weak_ptr_factory_.GetWeakPtr(), std::move(cached_logo))));
  360. } else if (cached_logo) {
  361. OnCachedLogoAvailable(std::move(cached_logo), SkBitmap(), SkBitmap());
  362. } else {
  363. OnCachedLogoAvailable({}, SkBitmap(), SkBitmap());
  364. }
  365. }
  366. void LogoServiceImpl::SetCachedLogo(std::unique_ptr<EncodedLogo> logo) {
  367. cache_task_runner_->PostTask(
  368. FROM_HERE, base::BindOnce(&LogoCache::SetCachedLogo,
  369. base::Unretained(logo_cache_.get()),
  370. base::Owned(logo.release())));
  371. }
  372. void LogoServiceImpl::SetCachedMetadata(const LogoMetadata& metadata) {
  373. cache_task_runner_->PostTask(
  374. FROM_HERE, base::BindOnce(&LogoCache::UpdateCachedLogoMetadata,
  375. base::Unretained(logo_cache_.get()), metadata));
  376. }
  377. void LogoServiceImpl::OnLightCachedImageDecoded(
  378. std::unique_ptr<EncodedLogo> cached_logo,
  379. const SkBitmap& image) {
  380. if (cached_logo->metadata.dark_mime_type.empty()) {
  381. OnCachedLogoAvailable(std::move(cached_logo), image, SkBitmap());
  382. return;
  383. }
  384. // Store the value of logo->dark_encoded_image for use below. This ensures
  385. // that logo->dark_encoded_image is evaluated before base::Passed(&logo),
  386. // which sets logo to NULL.
  387. scoped_refptr<base::RefCountedString> dark_encoded_image =
  388. cached_logo->dark_encoded_image;
  389. image_decoder_->DecodeImage(
  390. dark_encoded_image->data(), gfx::Size(), // No particular size desired.
  391. /*data_decoder=*/nullptr,
  392. ImageDecodedHandlerWithTimeout::Wrap(base::BindOnce(
  393. &LogoServiceImpl::OnCachedLogoAvailable,
  394. weak_ptr_factory_.GetWeakPtr(), std::move(cached_logo), image)));
  395. }
  396. void LogoServiceImpl::OnCachedLogoAvailable(
  397. std::unique_ptr<EncodedLogo> encoded_logo,
  398. const SkBitmap& image,
  399. const SkBitmap& dark_image) {
  400. DCHECK(!is_idle_);
  401. // A dark image is not required, but if one exists (mime type is non-empty)
  402. // it must be successfully decoded.
  403. if (encoded_logo && !image.isNull() &&
  404. (encoded_logo->metadata.dark_mime_type.empty() || !dark_image.isNull())) {
  405. cached_logo_ = std::make_unique<Logo>();
  406. cached_logo_->metadata = encoded_logo->metadata;
  407. cached_logo_->image = image;
  408. cached_logo_->dark_image = dark_image;
  409. cached_encoded_logo_ = std::move(encoded_logo);
  410. }
  411. is_cached_logo_valid_ = true;
  412. NotifyAndClear(&on_cached_encoded_logo_, &on_cached_decoded_logo_,
  413. LogoCallbackReason::DETERMINED, cached_encoded_logo_.get(),
  414. cached_logo_.get());
  415. FetchLogo();
  416. }
  417. void LogoServiceImpl::FetchLogo() {
  418. DCHECK(!loader_);
  419. DCHECK(!is_idle_);
  420. std::string fingerprint;
  421. if (cached_logo_ && !cached_logo_->metadata.fingerprint.empty() &&
  422. cached_logo_->metadata.expiration_time >= clock_->Now()) {
  423. fingerprint = cached_logo_->metadata.fingerprint;
  424. }
  425. GURL url = append_queryparams_func_.Run(logo_url_, fingerprint);
  426. net::NetworkTrafficAnnotationTag traffic_annotation =
  427. net::DefineNetworkTrafficAnnotation("logo_service", R"(
  428. semantics {
  429. sender: "Logo Service"
  430. description:
  431. "Provides the logo image (aka Doodle) if Google is your configured "
  432. "search provider."
  433. trigger: "Displaying the new tab page on iOS or Android."
  434. data:
  435. "Logo ID, and the user's Google cookies to show for example "
  436. "birthday doodles at appropriate times."
  437. destination: OTHER
  438. }
  439. policy {
  440. cookies_allowed: YES
  441. cookies_store: "user"
  442. setting:
  443. "Choosing a non-Google search engine in Chromium settings under "
  444. "'Search Engine' will disable this feature."
  445. policy_exception_justification:
  446. "Not implemented, considered not useful as it does not upload any"
  447. "data and just downloads a logo image."
  448. })");
  449. auto request = std::make_unique<network::ResourceRequest>();
  450. request->url = url;
  451. loader_ =
  452. network::SimpleURLLoader::Create(std::move(request), traffic_annotation);
  453. loader_->DownloadToString(
  454. url_loader_factory_.get(),
  455. base::BindOnce(&LogoServiceImpl::OnURLLoadComplete,
  456. base::Unretained(this), loader_.get()),
  457. kMaxDownloadBytes);
  458. logo_download_start_time_ = base::TimeTicks::Now();
  459. }
  460. void LogoServiceImpl::OnFreshLogoParsed(bool* parsing_failed,
  461. bool from_http_cache,
  462. std::unique_ptr<EncodedLogo> logo) {
  463. DCHECK(!is_idle_);
  464. if (logo)
  465. logo->metadata.source_url = logo_url_;
  466. if (!logo || !logo->encoded_image ||
  467. (!logo->metadata.dark_mime_type.empty() && !logo->dark_encoded_image)) {
  468. OnFreshLogoAvailable(std::move(logo), /*download_failed=*/false,
  469. *parsing_failed, from_http_cache, SkBitmap(),
  470. SkBitmap());
  471. } else {
  472. // Store the value of logo->encoded_image for use below. This ensures that
  473. // logo->encoded_image is evaluated before base::Passed(&logo), which sets
  474. // logo to NULL.
  475. scoped_refptr<base::RefCountedString> encoded_image = logo->encoded_image;
  476. image_decoder_->DecodeImage(
  477. encoded_image->data(), gfx::Size(), // No particular size desired.
  478. /*data_decoder=*/nullptr,
  479. ImageDecodedHandlerWithTimeout::Wrap(base::BindOnce(
  480. &LogoServiceImpl::OnLightFreshImageDecoded,
  481. weak_ptr_factory_.GetWeakPtr(), std::move(logo),
  482. /*download_failed=*/false, *parsing_failed, from_http_cache)));
  483. }
  484. }
  485. void LogoServiceImpl::OnLightFreshImageDecoded(
  486. std::unique_ptr<EncodedLogo> logo,
  487. bool download_failed,
  488. bool parsing_failed,
  489. bool from_http_cache,
  490. const SkBitmap& image) {
  491. if (logo->metadata.dark_mime_type.empty()) {
  492. OnFreshLogoAvailable(std::move(logo), download_failed, parsing_failed,
  493. from_http_cache, image, SkBitmap());
  494. return;
  495. }
  496. // Store the value of logo->dark_encoded_image for use below. This ensures
  497. // that logo->encoded_image is evaluated before base::Passed(&logo), which
  498. // sets logo to NULL.
  499. scoped_refptr<base::RefCountedString> dark_encoded_image =
  500. logo->dark_encoded_image;
  501. image_decoder_->DecodeImage(
  502. dark_encoded_image->data(), gfx::Size(), // No particular size desired.
  503. /*data_decoder=*/nullptr,
  504. ImageDecodedHandlerWithTimeout::Wrap(base::BindOnce(
  505. &LogoServiceImpl::OnFreshLogoAvailable,
  506. weak_ptr_factory_.GetWeakPtr(), std::move(logo), download_failed,
  507. parsing_failed, from_http_cache, image)));
  508. }
  509. void LogoServiceImpl::OnFreshLogoAvailable(
  510. std::unique_ptr<EncodedLogo> encoded_logo,
  511. bool download_failed,
  512. bool parsing_failed,
  513. bool from_http_cache,
  514. const SkBitmap& image,
  515. const SkBitmap& dark_image) {
  516. DCHECK(!is_idle_);
  517. LogoDownloadOutcome download_outcome = DOWNLOAD_OUTCOME_COUNT;
  518. std::unique_ptr<Logo> logo;
  519. if (download_failed) {
  520. download_outcome = DOWNLOAD_OUTCOME_DOWNLOAD_FAILED;
  521. } else if (encoded_logo && !encoded_logo->encoded_image && cached_logo_ &&
  522. !encoded_logo->metadata.fingerprint.empty() &&
  523. encoded_logo->metadata.fingerprint ==
  524. cached_logo_->metadata.fingerprint) {
  525. // The cached logo was revalidated, i.e. its fingerprint was verified.
  526. // mime_type isn't sent when revalidating, so copy it from the cached logo.
  527. encoded_logo->metadata.mime_type = cached_logo_->metadata.mime_type;
  528. encoded_logo->metadata.dark_mime_type =
  529. cached_logo_->metadata.dark_mime_type;
  530. SetCachedMetadata(encoded_logo->metadata);
  531. download_outcome = DOWNLOAD_OUTCOME_LOGO_REVALIDATED;
  532. } else if ((encoded_logo && encoded_logo->encoded_image && image.isNull()) ||
  533. (encoded_logo && !encoded_logo->metadata.dark_mime_type.empty() &&
  534. dark_image.isNull())) {
  535. // Image decoding failed. Do nothing.
  536. download_outcome = DOWNLOAD_OUTCOME_DECODING_FAILED;
  537. } else if (encoded_logo && !encoded_logo->encoded_image &&
  538. encoded_logo->metadata.type != LogoType::INTERACTIVE) {
  539. download_outcome = DOWNLOAD_OUTCOME_MISSING_REQUIRED_IMAGE;
  540. #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
  541. } else if (encoded_logo && !encoded_logo->encoded_image) {
  542. // On Mobile interactive doodles require a static CTA image, on Desktop the
  543. // static image is not required as it's handled by the iframed page.
  544. download_outcome = DOWNLOAD_OUTCOME_MISSING_REQUIRED_IMAGE;
  545. #endif
  546. } else {
  547. // Check if the server returned a valid, non-empty response.
  548. if (encoded_logo) {
  549. UMA_HISTOGRAM_BOOLEAN("NewTabPage.LogoImageDownloaded", from_http_cache);
  550. DCHECK(!encoded_logo->encoded_image || !image.isNull());
  551. logo = std::make_unique<Logo>();
  552. logo->metadata = encoded_logo->metadata;
  553. logo->image = image;
  554. logo->dark_image = dark_image;
  555. }
  556. if (logo) {
  557. download_outcome = DOWNLOAD_OUTCOME_NEW_LOGO_SUCCESS;
  558. } else {
  559. if (parsing_failed)
  560. download_outcome = DOWNLOAD_OUTCOME_PARSING_FAILED;
  561. else
  562. download_outcome = DOWNLOAD_OUTCOME_NO_LOGO_TODAY;
  563. }
  564. }
  565. LogoCallbackReason callback_type = LogoCallbackReason::FAILED;
  566. switch (download_outcome) {
  567. case DOWNLOAD_OUTCOME_NEW_LOGO_SUCCESS:
  568. DCHECK(encoded_logo);
  569. DCHECK(logo);
  570. callback_type = LogoCallbackReason::DETERMINED;
  571. break;
  572. case DOWNLOAD_OUTCOME_PARSING_FAILED:
  573. case DOWNLOAD_OUTCOME_NO_LOGO_TODAY:
  574. // Clear the cached logo if it was non-null. Otherwise, report this as a
  575. // revalidation of "no logo".
  576. DCHECK(!encoded_logo);
  577. DCHECK(!logo);
  578. if (cached_logo_) {
  579. callback_type = LogoCallbackReason::DETERMINED;
  580. } else {
  581. callback_type = LogoCallbackReason::REVALIDATED;
  582. }
  583. break;
  584. case DOWNLOAD_OUTCOME_MISSING_REQUIRED_IMAGE:
  585. case DOWNLOAD_OUTCOME_DOWNLOAD_FAILED:
  586. // In the download failed, don't notify the callback at all, since the
  587. // callback should continue to use the cached logo.
  588. DCHECK(!encoded_logo || !encoded_logo->encoded_image);
  589. DCHECK(!logo);
  590. callback_type = LogoCallbackReason::FAILED;
  591. break;
  592. case DOWNLOAD_OUTCOME_DECODING_FAILED:
  593. DCHECK(encoded_logo);
  594. DCHECK(!logo);
  595. encoded_logo.reset();
  596. callback_type = LogoCallbackReason::FAILED;
  597. break;
  598. case DOWNLOAD_OUTCOME_LOGO_REVALIDATED:
  599. // In the server reported that the cached logo is still current, don't
  600. // notify the callback at all, since the callback should continue to use
  601. // the cached logo.
  602. DCHECK(encoded_logo);
  603. DCHECK(!logo);
  604. callback_type = LogoCallbackReason::REVALIDATED;
  605. break;
  606. case DOWNLOAD_OUTCOME_COUNT:
  607. NOTREACHED();
  608. return;
  609. }
  610. NotifyAndClear(&on_fresh_encoded_logo_, &on_fresh_decoded_logo_,
  611. callback_type, encoded_logo.get(), logo.get());
  612. switch (callback_type) {
  613. case LogoCallbackReason::DETERMINED:
  614. SetCachedLogo(std::move(encoded_logo));
  615. break;
  616. default:
  617. break;
  618. }
  619. ReturnToIdle(download_outcome);
  620. }
  621. void LogoServiceImpl::OnURLLoadComplete(const network::SimpleURLLoader* source,
  622. std::unique_ptr<std::string> body) {
  623. DCHECK(!is_idle_);
  624. std::unique_ptr<network::SimpleURLLoader> cleanup_loader(loader_.release());
  625. if (source->NetError() != net::OK) {
  626. OnFreshLogoAvailable({}, /*download_failed=*/true, false, false, SkBitmap(),
  627. SkBitmap());
  628. return;
  629. }
  630. if (!source->ResponseInfo() || !source->ResponseInfo()->headers ||
  631. source->ResponseInfo()->headers->response_code() != net::HTTP_OK) {
  632. OnFreshLogoAvailable({}, /*download_failed=*/true, false, false, SkBitmap(),
  633. SkBitmap());
  634. return;
  635. }
  636. UMA_HISTOGRAM_TIMES("NewTabPage.LogoDownloadTime",
  637. base::TimeTicks::Now() - logo_download_start_time_);
  638. std::unique_ptr<std::string> response =
  639. body ? std::move(body) : std::make_unique<std::string>();
  640. base::Time response_time = clock_->Now();
  641. bool from_http_cache = !source->ResponseInfo()->network_accessed;
  642. bool* parsing_failed = new bool(false);
  643. base::ThreadPool::PostTaskAndReplyWithResult(
  644. FROM_HERE,
  645. {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
  646. base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
  647. base::BindOnce(parse_logo_response_func_, std::move(response),
  648. response_time, parsing_failed),
  649. base::BindOnce(&LogoServiceImpl::OnFreshLogoParsed,
  650. weak_ptr_factory_.GetWeakPtr(),
  651. base::Owned(parsing_failed), from_http_cache));
  652. }
  653. void LogoServiceImpl::OnAccountsInCookieUpdated(
  654. const signin::AccountsInCookieJarInfo&,
  655. const GoogleServiceAuthError&) {
  656. // Clear any cached logo, since it may be personalized (e.g. birthday Doodle).
  657. if (!clock_) {
  658. clock_ = base::DefaultClock::GetInstance();
  659. }
  660. ClearCachedLogo();
  661. }
  662. } // namespace search_provider_logos