123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751 |
- // Copyright 2017 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.
- #include "components/search_provider_logos/logo_service_impl.h"
- #include <algorithm>
- #include <memory>
- #include <utility>
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/command_line.h"
- #include "base/feature_list.h"
- #include "base/memory/weak_ptr.h"
- #include "base/metrics/field_trial_params.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/task/task_runner_util.h"
- #include "base/task/thread_pool.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "base/time/default_clock.h"
- #include "build/build_config.h"
- #include "components/image_fetcher/core/image_decoder.h"
- #include "components/search_engines/search_terms_data.h"
- #include "components/search_engines/template_url_service.h"
- #include "components/search_provider_logos/fixed_logo_api.h"
- #include "components/search_provider_logos/google_logo_api.h"
- #include "components/search_provider_logos/logo_cache.h"
- #include "components/search_provider_logos/logo_observer.h"
- #include "components/search_provider_logos/switches.h"
- #include "net/http/http_status_code.h"
- #include "net/traffic_annotation/network_traffic_annotation.h"
- #include "services/network/public/cpp/resource_request.h"
- #include "services/network/public/cpp/shared_url_loader_factory.h"
- #include "services/network/public/cpp/simple_url_loader.h"
- #include "services/network/public/mojom/url_response_head.mojom.h"
- #include "ui/gfx/image/image.h"
- namespace search_provider_logos {
- namespace {
- const int64_t kMaxDownloadBytes = 1024 * 1024;
- const int kDecodeLogoTimeoutSeconds = 30;
- // Implements a callback for image_fetcher::ImageDecoder. If Run() is called on
- // a callback returned by GetCallback() within 30 seconds, forwards the decoded
- // image to the wrapped callback. If not, sends an empty image to the wrapped
- // callback instead. Either way, deletes the object and prevents further calls.
- //
- // TODO(sfiera): find a more idiomatic way of setting a deadline on the
- // callback. This is implemented as a self-deleting object in part because it
- // needed to when it used to be a delegate and in part because I couldn't figure
- // out a better way, now that it isn't.
- class ImageDecodedHandlerWithTimeout {
- public:
- ImageDecodedHandlerWithTimeout(const ImageDecodedHandlerWithTimeout&) =
- delete;
- ImageDecodedHandlerWithTimeout& operator=(
- const ImageDecodedHandlerWithTimeout&) = delete;
- static base::OnceCallback<void(const gfx::Image&)> Wrap(
- base::OnceCallback<void(const SkBitmap&)> image_decoded_callback) {
- auto* handler =
- new ImageDecodedHandlerWithTimeout(std::move(image_decoded_callback));
- base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(&ImageDecodedHandlerWithTimeout::OnImageDecoded,
- handler->weak_ptr_factory_.GetWeakPtr(), gfx::Image()),
- base::Seconds(kDecodeLogoTimeoutSeconds));
- return base::BindOnce(&ImageDecodedHandlerWithTimeout::OnImageDecoded,
- handler->weak_ptr_factory_.GetWeakPtr());
- }
- private:
- explicit ImageDecodedHandlerWithTimeout(
- base::OnceCallback<void(const SkBitmap&)> image_decoded_callback)
- : image_decoded_callback_(std::move(image_decoded_callback)) {}
- void OnImageDecoded(const gfx::Image& decoded_image) {
- std::move(image_decoded_callback_).Run(decoded_image.AsBitmap());
- delete this;
- }
- base::OnceCallback<void(const SkBitmap&)> image_decoded_callback_;
- base::WeakPtrFactory<ImageDecodedHandlerWithTimeout> weak_ptr_factory_{this};
- };
- void ObserverOnLogoAvailable(LogoObserver* observer,
- bool from_cache,
- LogoCallbackReason type,
- const absl::optional<Logo>& logo) {
- switch (type) {
- case LogoCallbackReason::DISABLED:
- case LogoCallbackReason::CANCELED:
- case LogoCallbackReason::FAILED:
- break;
- case LogoCallbackReason::REVALIDATED:
- observer->OnCachedLogoRevalidated();
- break;
- case LogoCallbackReason::DETERMINED:
- observer->OnLogoAvailable(logo ? &logo.value() : nullptr, from_cache);
- break;
- }
- if (!from_cache) {
- observer->OnObserverRemoved();
- }
- }
- void RunCallbacksWithDisabled(LogoCallbacks callbacks) {
- if (callbacks.on_cached_encoded_logo_available) {
- std::move(callbacks.on_cached_encoded_logo_available)
- .Run(LogoCallbackReason::DISABLED, absl::nullopt);
- }
- if (callbacks.on_cached_decoded_logo_available) {
- std::move(callbacks.on_cached_decoded_logo_available)
- .Run(LogoCallbackReason::DISABLED, absl::nullopt);
- }
- if (callbacks.on_fresh_encoded_logo_available) {
- std::move(callbacks.on_fresh_encoded_logo_available)
- .Run(LogoCallbackReason::DISABLED, absl::nullopt);
- }
- if (callbacks.on_fresh_decoded_logo_available) {
- std::move(callbacks.on_fresh_decoded_logo_available)
- .Run(LogoCallbackReason::DISABLED, absl::nullopt);
- }
- }
- // Returns whether the metadata for the cached logo indicates that the logo is
- // OK to show, i.e. it's not expired or it's allowed to be shown temporarily
- // after expiration.
- bool IsLogoOkToShow(const LogoMetadata& metadata, base::Time now) {
- base::TimeDelta offset = base::Milliseconds(kMaxTimeToLiveMS * 3 / 2);
- base::Time distant_past = now - offset;
- // Sanity check so logos aren't accidentally cached forever.
- if (metadata.expiration_time < distant_past) {
- return false;
- }
- return metadata.can_show_after_expiration || metadata.expiration_time >= now;
- }
- // Reads the logo from the cache and returns it. Returns NULL if the cache is
- // empty, corrupt, expired, or doesn't apply to the current logo URL.
- std::unique_ptr<EncodedLogo> GetLogoFromCacheOnFileThread(LogoCache* logo_cache,
- const GURL& logo_url,
- base::Time now) {
- const LogoMetadata* metadata = logo_cache->GetCachedLogoMetadata();
- if (!metadata)
- return nullptr;
- if (metadata->source_url != logo_url || !IsLogoOkToShow(*metadata, now)) {
- logo_cache->SetCachedLogo(nullptr);
- return nullptr;
- }
- return logo_cache->GetCachedLogo();
- }
- void NotifyAndClear(std::vector<EncodedLogoCallback>* encoded_callbacks,
- std::vector<LogoCallback>* decoded_callbacks,
- LogoCallbackReason type,
- const EncodedLogo* encoded_logo,
- const Logo* decoded_logo) {
- auto opt_encoded_logo =
- encoded_logo ? absl::optional<EncodedLogo>(*encoded_logo) : absl::nullopt;
- for (EncodedLogoCallback& callback : *encoded_callbacks) {
- std::move(callback).Run(type, opt_encoded_logo);
- }
- encoded_callbacks->clear();
- auto opt_decoded_logo =
- decoded_logo ? absl::optional<Logo>(*decoded_logo) : absl::nullopt;
- for (LogoCallback& callback : *decoded_callbacks) {
- std::move(callback).Run(type, opt_decoded_logo);
- }
- decoded_callbacks->clear();
- }
- } // namespace
- LogoServiceImpl::LogoServiceImpl(
- const base::FilePath& cache_directory,
- signin::IdentityManager* identity_manager,
- TemplateURLService* template_url_service,
- std::unique_ptr<image_fetcher::ImageDecoder> image_decoder,
- scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
- base::RepeatingCallback<bool()> want_gray_logo_getter)
- : cache_directory_(cache_directory),
- identity_manager_(identity_manager),
- template_url_service_(template_url_service),
- url_loader_factory_(url_loader_factory),
- want_gray_logo_getter_(std::move(want_gray_logo_getter)),
- image_decoder_(std::move(image_decoder)),
- is_idle_(true),
- is_cached_logo_valid_(false),
- cache_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
- {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
- base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
- logo_cache_(new LogoCache(cache_directory_),
- base::OnTaskRunnerDeleter(cache_task_runner_)) {
- identity_manager_->AddObserver(this);
- }
- LogoServiceImpl::~LogoServiceImpl() = default;
- void LogoServiceImpl::Shutdown() {
- // The IdentityManager may be destroyed at any point after Shutdown,
- // so make sure we drop any references to it.
- identity_manager_->RemoveObserver(this);
- ReturnToIdle(kDownloadOutcomeNotTracked);
- }
- void LogoServiceImpl::GetLogo(search_provider_logos::LogoObserver* observer) {
- LogoCallbacks callbacks;
- callbacks.on_cached_decoded_logo_available =
- base::BindOnce(ObserverOnLogoAvailable, observer, true);
- callbacks.on_fresh_decoded_logo_available =
- base::BindOnce(ObserverOnLogoAvailable, observer, false);
- GetLogo(std::move(callbacks), false);
- }
- void LogoServiceImpl::GetLogo(LogoCallbacks callbacks, bool for_webui_ntp) {
- if (!template_url_service_) {
- RunCallbacksWithDisabled(std::move(callbacks));
- return;
- }
- const TemplateURL* template_url =
- template_url_service_->GetDefaultSearchProvider();
- if (!template_url) {
- RunCallbacksWithDisabled(std::move(callbacks));
- return;
- }
- const base::CommandLine* command_line =
- base::CommandLine::ForCurrentProcess();
- GURL logo_url;
- if (command_line->HasSwitch(switches::kSearchProviderLogoURL)) {
- logo_url = GURL(
- command_line->GetSwitchValueASCII(switches::kSearchProviderLogoURL));
- } else {
- #if BUILDFLAG(IS_ANDROID)
- // Non-Google default search engine logos are currently enabled only on
- // Android (https://crbug.com/737283).
- logo_url = template_url->logo_url();
- #endif
- }
- GURL base_url;
- GURL doodle_url;
- const bool is_google = template_url->url_ref().HasGoogleBaseURLs(
- template_url_service_->search_terms_data());
- if (is_google) {
- // TODO(treib): Put the Google doodle URL into prepopulated_engines.json.
- base_url =
- GURL(template_url_service_->search_terms_data().GoogleBaseURLValue());
- doodle_url = search_provider_logos::GetGoogleDoodleURL(base_url);
- } else {
- if (command_line->HasSwitch(switches::kThirdPartyDoodleURL)) {
- doodle_url = GURL(
- command_line->GetSwitchValueASCII(switches::kThirdPartyDoodleURL));
- } else {
- doodle_url = template_url->doodle_url();
- }
- base_url = doodle_url.DeprecatedGetOriginAsURL();
- }
- if (!logo_url.is_valid() && !doodle_url.is_valid()) {
- RunCallbacksWithDisabled(std::move(callbacks));
- return;
- }
- if (!clock_) {
- clock_ = base::DefaultClock::GetInstance();
- }
- const bool use_fixed_logo = !doodle_url.is_valid();
- if (use_fixed_logo) {
- SetServerAPI(
- logo_url,
- base::BindRepeating(&search_provider_logos::ParseFixedLogoResponse),
- base::BindRepeating(&search_provider_logos::UseFixedLogoUrl));
- } else {
- // We encode the type of doodle (regular or gray) in the URL so that the
- // logo cache gets cleared when that value changes.
- GURL prefilled_url = AppendPreliminaryParamsToDoodleURL(
- want_gray_logo_getter_.Run(), for_webui_ntp, doodle_url);
- SetServerAPI(
- prefilled_url,
- base::BindRepeating(&search_provider_logos::ParseDoodleLogoResponse,
- base_url),
- base::BindRepeating(
- &search_provider_logos::AppendFingerprintParamToDoodleURL));
- }
- DCHECK(!logo_url_.is_empty());
- DCHECK(callbacks.on_cached_decoded_logo_available ||
- callbacks.on_cached_encoded_logo_available ||
- callbacks.on_fresh_decoded_logo_available ||
- callbacks.on_fresh_encoded_logo_available);
- if (callbacks.on_cached_encoded_logo_available) {
- on_cached_encoded_logo_.push_back(
- std::move(callbacks.on_cached_encoded_logo_available));
- }
- if (callbacks.on_cached_decoded_logo_available) {
- on_cached_decoded_logo_.push_back(
- std::move(callbacks.on_cached_decoded_logo_available));
- }
- if (callbacks.on_fresh_encoded_logo_available) {
- on_fresh_encoded_logo_.push_back(
- std::move(callbacks.on_fresh_encoded_logo_available));
- }
- if (callbacks.on_fresh_decoded_logo_available) {
- on_fresh_decoded_logo_.push_back(
- std::move(callbacks.on_fresh_decoded_logo_available));
- }
- if (is_idle_) {
- is_idle_ = false;
- base::PostTaskAndReplyWithResult(
- cache_task_runner_.get(), FROM_HERE,
- base::BindOnce(&GetLogoFromCacheOnFileThread,
- base::Unretained(logo_cache_.get()), logo_url_,
- clock_->Now()),
- base::BindOnce(&LogoServiceImpl::OnCachedLogoRead,
- weak_ptr_factory_.GetWeakPtr()));
- } else if (is_cached_logo_valid_) {
- NotifyAndClear(&on_cached_encoded_logo_, &on_cached_decoded_logo_,
- LogoCallbackReason::DETERMINED, cached_encoded_logo_.get(),
- cached_logo_.get());
- }
- }
- void LogoServiceImpl::SetLogoCacheForTests(std::unique_ptr<LogoCache> cache) {
- // |logo_cache_| has a custom deleter, which makes the two unique_ptrs
- // be different types. so one can't be moved on top of the other.
- logo_cache_.reset(std::move(cache).release());
- }
- void LogoServiceImpl::SetClockForTests(base::Clock* clock) {
- clock_ = clock;
- }
- void LogoServiceImpl::SetServerAPI(
- const GURL& logo_url,
- const ParseLogoResponse& parse_logo_response_func,
- const AppendQueryparamsToLogoURL& append_queryparams_func) {
- if (logo_url == logo_url_)
- return;
- ReturnToIdle(kDownloadOutcomeNotTracked);
- logo_url_ = logo_url;
- parse_logo_response_func_ = parse_logo_response_func;
- append_queryparams_func_ = append_queryparams_func;
- }
- void LogoServiceImpl::ClearCachedLogo() {
- // First cancel any fetch that might be ongoing.
- ReturnToIdle(kDownloadOutcomeNotTracked);
- // Then clear any cached logo.
- SetCachedLogo(nullptr);
- }
- void LogoServiceImpl::ReturnToIdle(int outcome) {
- if (outcome != kDownloadOutcomeNotTracked) {
- UMA_HISTOGRAM_ENUMERATION("NewTabPage.LogoDownloadOutcome",
- static_cast<LogoDownloadOutcome>(outcome),
- DOWNLOAD_OUTCOME_COUNT);
- }
- // Cancel the current asynchronous operation, if any.
- loader_.reset();
- weak_ptr_factory_.InvalidateWeakPtrs();
- // Reset state.
- is_idle_ = true;
- cached_logo_.reset();
- cached_encoded_logo_.reset();
- is_cached_logo_valid_ = false;
- // Clear callbacks.
- NotifyAndClear(&on_cached_encoded_logo_, &on_cached_decoded_logo_,
- LogoCallbackReason::CANCELED, nullptr, nullptr);
- NotifyAndClear(&on_fresh_encoded_logo_, &on_fresh_decoded_logo_,
- LogoCallbackReason::CANCELED, nullptr, nullptr);
- }
- void LogoServiceImpl::OnCachedLogoRead(
- std::unique_ptr<EncodedLogo> cached_logo) {
- DCHECK(!is_idle_);
- if (cached_logo && cached_logo->encoded_image) {
- // Store the value of logo->encoded_image for use below. This ensures that
- // logo->encoded_image is evaluated before base::Passed(&logo), which sets
- // logo to NULL.
- scoped_refptr<base::RefCountedString> encoded_image =
- cached_logo->encoded_image;
- image_decoder_->DecodeImage(
- encoded_image->data(), gfx::Size(), // No particular size desired.
- /*data_decoder=*/nullptr,
- ImageDecodedHandlerWithTimeout::Wrap(base::BindOnce(
- &LogoServiceImpl::OnLightCachedImageDecoded,
- weak_ptr_factory_.GetWeakPtr(), std::move(cached_logo))));
- } else if (cached_logo) {
- OnCachedLogoAvailable(std::move(cached_logo), SkBitmap(), SkBitmap());
- } else {
- OnCachedLogoAvailable({}, SkBitmap(), SkBitmap());
- }
- }
- void LogoServiceImpl::SetCachedLogo(std::unique_ptr<EncodedLogo> logo) {
- cache_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&LogoCache::SetCachedLogo,
- base::Unretained(logo_cache_.get()),
- base::Owned(logo.release())));
- }
- void LogoServiceImpl::SetCachedMetadata(const LogoMetadata& metadata) {
- cache_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&LogoCache::UpdateCachedLogoMetadata,
- base::Unretained(logo_cache_.get()), metadata));
- }
- void LogoServiceImpl::OnLightCachedImageDecoded(
- std::unique_ptr<EncodedLogo> cached_logo,
- const SkBitmap& image) {
- if (cached_logo->metadata.dark_mime_type.empty()) {
- OnCachedLogoAvailable(std::move(cached_logo), image, SkBitmap());
- return;
- }
- // Store the value of logo->dark_encoded_image for use below. This ensures
- // that logo->dark_encoded_image is evaluated before base::Passed(&logo),
- // which sets logo to NULL.
- scoped_refptr<base::RefCountedString> dark_encoded_image =
- cached_logo->dark_encoded_image;
- image_decoder_->DecodeImage(
- dark_encoded_image->data(), gfx::Size(), // No particular size desired.
- /*data_decoder=*/nullptr,
- ImageDecodedHandlerWithTimeout::Wrap(base::BindOnce(
- &LogoServiceImpl::OnCachedLogoAvailable,
- weak_ptr_factory_.GetWeakPtr(), std::move(cached_logo), image)));
- }
- void LogoServiceImpl::OnCachedLogoAvailable(
- std::unique_ptr<EncodedLogo> encoded_logo,
- const SkBitmap& image,
- const SkBitmap& dark_image) {
- DCHECK(!is_idle_);
- // A dark image is not required, but if one exists (mime type is non-empty)
- // it must be successfully decoded.
- if (encoded_logo && !image.isNull() &&
- (encoded_logo->metadata.dark_mime_type.empty() || !dark_image.isNull())) {
- cached_logo_ = std::make_unique<Logo>();
- cached_logo_->metadata = encoded_logo->metadata;
- cached_logo_->image = image;
- cached_logo_->dark_image = dark_image;
- cached_encoded_logo_ = std::move(encoded_logo);
- }
- is_cached_logo_valid_ = true;
- NotifyAndClear(&on_cached_encoded_logo_, &on_cached_decoded_logo_,
- LogoCallbackReason::DETERMINED, cached_encoded_logo_.get(),
- cached_logo_.get());
- FetchLogo();
- }
- void LogoServiceImpl::FetchLogo() {
- DCHECK(!loader_);
- DCHECK(!is_idle_);
- std::string fingerprint;
- if (cached_logo_ && !cached_logo_->metadata.fingerprint.empty() &&
- cached_logo_->metadata.expiration_time >= clock_->Now()) {
- fingerprint = cached_logo_->metadata.fingerprint;
- }
- GURL url = append_queryparams_func_.Run(logo_url_, fingerprint);
- net::NetworkTrafficAnnotationTag traffic_annotation =
- net::DefineNetworkTrafficAnnotation("logo_service", R"(
- semantics {
- sender: "Logo Service"
- description:
- "Provides the logo image (aka Doodle) if Google is your configured "
- "search provider."
- trigger: "Displaying the new tab page on iOS or Android."
- data:
- "Logo ID, and the user's Google cookies to show for example "
- "birthday doodles at appropriate times."
- destination: OTHER
- }
- policy {
- cookies_allowed: YES
- cookies_store: "user"
- setting:
- "Choosing a non-Google search engine in Chromium settings under "
- "'Search Engine' will disable this feature."
- policy_exception_justification:
- "Not implemented, considered not useful as it does not upload any"
- "data and just downloads a logo image."
- })");
- auto request = std::make_unique<network::ResourceRequest>();
- request->url = url;
- loader_ =
- network::SimpleURLLoader::Create(std::move(request), traffic_annotation);
- loader_->DownloadToString(
- url_loader_factory_.get(),
- base::BindOnce(&LogoServiceImpl::OnURLLoadComplete,
- base::Unretained(this), loader_.get()),
- kMaxDownloadBytes);
- logo_download_start_time_ = base::TimeTicks::Now();
- }
- void LogoServiceImpl::OnFreshLogoParsed(bool* parsing_failed,
- bool from_http_cache,
- std::unique_ptr<EncodedLogo> logo) {
- DCHECK(!is_idle_);
- if (logo)
- logo->metadata.source_url = logo_url_;
- if (!logo || !logo->encoded_image ||
- (!logo->metadata.dark_mime_type.empty() && !logo->dark_encoded_image)) {
- OnFreshLogoAvailable(std::move(logo), /*download_failed=*/false,
- *parsing_failed, from_http_cache, SkBitmap(),
- SkBitmap());
- } else {
- // Store the value of logo->encoded_image for use below. This ensures that
- // logo->encoded_image is evaluated before base::Passed(&logo), which sets
- // logo to NULL.
- scoped_refptr<base::RefCountedString> encoded_image = logo->encoded_image;
- image_decoder_->DecodeImage(
- encoded_image->data(), gfx::Size(), // No particular size desired.
- /*data_decoder=*/nullptr,
- ImageDecodedHandlerWithTimeout::Wrap(base::BindOnce(
- &LogoServiceImpl::OnLightFreshImageDecoded,
- weak_ptr_factory_.GetWeakPtr(), std::move(logo),
- /*download_failed=*/false, *parsing_failed, from_http_cache)));
- }
- }
- void LogoServiceImpl::OnLightFreshImageDecoded(
- std::unique_ptr<EncodedLogo> logo,
- bool download_failed,
- bool parsing_failed,
- bool from_http_cache,
- const SkBitmap& image) {
- if (logo->metadata.dark_mime_type.empty()) {
- OnFreshLogoAvailable(std::move(logo), download_failed, parsing_failed,
- from_http_cache, image, SkBitmap());
- return;
- }
- // Store the value of logo->dark_encoded_image for use below. This ensures
- // that logo->encoded_image is evaluated before base::Passed(&logo), which
- // sets logo to NULL.
- scoped_refptr<base::RefCountedString> dark_encoded_image =
- logo->dark_encoded_image;
- image_decoder_->DecodeImage(
- dark_encoded_image->data(), gfx::Size(), // No particular size desired.
- /*data_decoder=*/nullptr,
- ImageDecodedHandlerWithTimeout::Wrap(base::BindOnce(
- &LogoServiceImpl::OnFreshLogoAvailable,
- weak_ptr_factory_.GetWeakPtr(), std::move(logo), download_failed,
- parsing_failed, from_http_cache, image)));
- }
- void LogoServiceImpl::OnFreshLogoAvailable(
- std::unique_ptr<EncodedLogo> encoded_logo,
- bool download_failed,
- bool parsing_failed,
- bool from_http_cache,
- const SkBitmap& image,
- const SkBitmap& dark_image) {
- DCHECK(!is_idle_);
- LogoDownloadOutcome download_outcome = DOWNLOAD_OUTCOME_COUNT;
- std::unique_ptr<Logo> logo;
- if (download_failed) {
- download_outcome = DOWNLOAD_OUTCOME_DOWNLOAD_FAILED;
- } else if (encoded_logo && !encoded_logo->encoded_image && cached_logo_ &&
- !encoded_logo->metadata.fingerprint.empty() &&
- encoded_logo->metadata.fingerprint ==
- cached_logo_->metadata.fingerprint) {
- // The cached logo was revalidated, i.e. its fingerprint was verified.
- // mime_type isn't sent when revalidating, so copy it from the cached logo.
- encoded_logo->metadata.mime_type = cached_logo_->metadata.mime_type;
- encoded_logo->metadata.dark_mime_type =
- cached_logo_->metadata.dark_mime_type;
- SetCachedMetadata(encoded_logo->metadata);
- download_outcome = DOWNLOAD_OUTCOME_LOGO_REVALIDATED;
- } else if ((encoded_logo && encoded_logo->encoded_image && image.isNull()) ||
- (encoded_logo && !encoded_logo->metadata.dark_mime_type.empty() &&
- dark_image.isNull())) {
- // Image decoding failed. Do nothing.
- download_outcome = DOWNLOAD_OUTCOME_DECODING_FAILED;
- } else if (encoded_logo && !encoded_logo->encoded_image &&
- encoded_logo->metadata.type != LogoType::INTERACTIVE) {
- download_outcome = DOWNLOAD_OUTCOME_MISSING_REQUIRED_IMAGE;
- #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
- } else if (encoded_logo && !encoded_logo->encoded_image) {
- // On Mobile interactive doodles require a static CTA image, on Desktop the
- // static image is not required as it's handled by the iframed page.
- download_outcome = DOWNLOAD_OUTCOME_MISSING_REQUIRED_IMAGE;
- #endif
- } else {
- // Check if the server returned a valid, non-empty response.
- if (encoded_logo) {
- UMA_HISTOGRAM_BOOLEAN("NewTabPage.LogoImageDownloaded", from_http_cache);
- DCHECK(!encoded_logo->encoded_image || !image.isNull());
- logo = std::make_unique<Logo>();
- logo->metadata = encoded_logo->metadata;
- logo->image = image;
- logo->dark_image = dark_image;
- }
- if (logo) {
- download_outcome = DOWNLOAD_OUTCOME_NEW_LOGO_SUCCESS;
- } else {
- if (parsing_failed)
- download_outcome = DOWNLOAD_OUTCOME_PARSING_FAILED;
- else
- download_outcome = DOWNLOAD_OUTCOME_NO_LOGO_TODAY;
- }
- }
- LogoCallbackReason callback_type = LogoCallbackReason::FAILED;
- switch (download_outcome) {
- case DOWNLOAD_OUTCOME_NEW_LOGO_SUCCESS:
- DCHECK(encoded_logo);
- DCHECK(logo);
- callback_type = LogoCallbackReason::DETERMINED;
- break;
- case DOWNLOAD_OUTCOME_PARSING_FAILED:
- case DOWNLOAD_OUTCOME_NO_LOGO_TODAY:
- // Clear the cached logo if it was non-null. Otherwise, report this as a
- // revalidation of "no logo".
- DCHECK(!encoded_logo);
- DCHECK(!logo);
- if (cached_logo_) {
- callback_type = LogoCallbackReason::DETERMINED;
- } else {
- callback_type = LogoCallbackReason::REVALIDATED;
- }
- break;
- case DOWNLOAD_OUTCOME_MISSING_REQUIRED_IMAGE:
- case DOWNLOAD_OUTCOME_DOWNLOAD_FAILED:
- // In the download failed, don't notify the callback at all, since the
- // callback should continue to use the cached logo.
- DCHECK(!encoded_logo || !encoded_logo->encoded_image);
- DCHECK(!logo);
- callback_type = LogoCallbackReason::FAILED;
- break;
- case DOWNLOAD_OUTCOME_DECODING_FAILED:
- DCHECK(encoded_logo);
- DCHECK(!logo);
- encoded_logo.reset();
- callback_type = LogoCallbackReason::FAILED;
- break;
- case DOWNLOAD_OUTCOME_LOGO_REVALIDATED:
- // In the server reported that the cached logo is still current, don't
- // notify the callback at all, since the callback should continue to use
- // the cached logo.
- DCHECK(encoded_logo);
- DCHECK(!logo);
- callback_type = LogoCallbackReason::REVALIDATED;
- break;
- case DOWNLOAD_OUTCOME_COUNT:
- NOTREACHED();
- return;
- }
- NotifyAndClear(&on_fresh_encoded_logo_, &on_fresh_decoded_logo_,
- callback_type, encoded_logo.get(), logo.get());
- switch (callback_type) {
- case LogoCallbackReason::DETERMINED:
- SetCachedLogo(std::move(encoded_logo));
- break;
- default:
- break;
- }
- ReturnToIdle(download_outcome);
- }
- void LogoServiceImpl::OnURLLoadComplete(const network::SimpleURLLoader* source,
- std::unique_ptr<std::string> body) {
- DCHECK(!is_idle_);
- std::unique_ptr<network::SimpleURLLoader> cleanup_loader(loader_.release());
- if (source->NetError() != net::OK) {
- OnFreshLogoAvailable({}, /*download_failed=*/true, false, false, SkBitmap(),
- SkBitmap());
- return;
- }
- if (!source->ResponseInfo() || !source->ResponseInfo()->headers ||
- source->ResponseInfo()->headers->response_code() != net::HTTP_OK) {
- OnFreshLogoAvailable({}, /*download_failed=*/true, false, false, SkBitmap(),
- SkBitmap());
- return;
- }
- UMA_HISTOGRAM_TIMES("NewTabPage.LogoDownloadTime",
- base::TimeTicks::Now() - logo_download_start_time_);
- std::unique_ptr<std::string> response =
- body ? std::move(body) : std::make_unique<std::string>();
- base::Time response_time = clock_->Now();
- bool from_http_cache = !source->ResponseInfo()->network_accessed;
- bool* parsing_failed = new bool(false);
- base::ThreadPool::PostTaskAndReplyWithResult(
- FROM_HERE,
- {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
- base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
- base::BindOnce(parse_logo_response_func_, std::move(response),
- response_time, parsing_failed),
- base::BindOnce(&LogoServiceImpl::OnFreshLogoParsed,
- weak_ptr_factory_.GetWeakPtr(),
- base::Owned(parsing_failed), from_http_cache));
- }
- void LogoServiceImpl::OnAccountsInCookieUpdated(
- const signin::AccountsInCookieJarInfo&,
- const GoogleServiceAuthError&) {
- // Clear any cached logo, since it may be personalized (e.g. birthday Doodle).
- if (!clock_) {
- clock_ = base::DefaultClock::GetInstance();
- }
- ClearCachedLogo();
- }
- } // namespace search_provider_logos
|