Merged LogoService and LogoTracker.
LogoTracker is now an implementation detail of LogoService.
In order to simplify things further,
its implementation was moved inside LogoService.
Bug: 761829
Change-Id: I33e5ab909e3f66aec31546bb1833069f973216c9
Reviewed-on: https://chromium-review.googlesource.com/c/1349511
Reviewed-by: Ramin Halavati <rhalavati@chromium.org>
Reviewed-by: Marc Treib <treib@chromium.org>
Commit-Queue: Yeol Park <peary2@gmail.com>
Cr-Commit-Position: refs/heads/master@{#614651}
diff --git a/components/search_provider_logos/BUILD.gn b/components/search_provider_logos/BUILD.gn
index 18619792..44688b7f 100644
--- a/components/search_provider_logos/BUILD.gn
+++ b/components/search_provider_logos/BUILD.gn
@@ -18,7 +18,6 @@
"logo_service.h",
"logo_service_impl.cc",
"logo_service_impl.h",
- "logo_tracker.cc",
"logo_tracker.h",
"switches.cc",
"switches.h",
diff --git a/components/search_provider_logos/fixed_logo_api.h b/components/search_provider_logos/fixed_logo_api.h
index 7801357..4d625d0 100644
--- a/components/search_provider_logos/fixed_logo_api.h
+++ b/components/search_provider_logos/fixed_logo_api.h
@@ -16,11 +16,12 @@
struct EncodedLogo;
-// Implements AppendFingerprintToLogoURL, defined in logo_tracker.h,
+// Implements AppendQueryparamsToLogoURL, defined in logo_common.h,
// for static logos.
GURL UseFixedLogoUrl(const GURL& logo_url, const std::string& fingerprint);
-// Implements ParseLogoResponse, defined in logo_tracker.h, for static logos.
+// Implements ParseLogoResponse, defined in logo_common.h,
+// for static logos.
std::unique_ptr<EncodedLogo> ParseFixedLogoResponse(
std::unique_ptr<std::string> response,
base::Time response_time,
diff --git a/components/search_provider_logos/logo_service_impl.cc b/components/search_provider_logos/logo_service_impl.cc
index 692bf78..b7dc7df2 100644
--- a/components/search_provider_logos/logo_service_impl.cc
+++ b/components/search_provider_logos/logo_service_impl.cc
@@ -4,11 +4,17 @@
#include "components/search_provider_logos/logo_service_impl.h"
+#include <algorithm>
+#include <utility>
+
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/task/post_task.h"
+#include "base/task_runner_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/default_clock.h"
#include "build/build_config.h"
@@ -22,13 +28,60 @@
#include "components/search_provider_logos/logo_tracker.h"
#include "components/search_provider_logos/switches.h"
#include "components/signin/core/browser/gaia_cookie_manager_service.h"
+#include "net/http/http_status_code.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
-
-using search_provider_logos::LogoTracker;
+#include "services/network/public/cpp/simple_url_loader.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:
+ static base::RepeatingCallback<void(const gfx::Image&)> Wrap(
+ const base::RepeatingCallback<void(const SkBitmap&)>&
+ image_decoded_callback) {
+ auto* handler = new ImageDecodedHandlerWithTimeout(image_decoded_callback);
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&ImageDecodedHandlerWithTimeout::OnImageDecoded,
+ handler->weak_ptr_factory_.GetWeakPtr(), gfx::Image()),
+ base::TimeDelta::FromSeconds(kDecodeLogoTimeoutSeconds));
+ return base::BindRepeating(&ImageDecodedHandlerWithTimeout::OnImageDecoded,
+ handler->weak_ptr_factory_.GetWeakPtr());
+ }
+
+ private:
+ explicit ImageDecodedHandlerWithTimeout(
+ const base::RepeatingCallback<void(const SkBitmap&)>&
+ image_decoded_callback)
+ : image_decoded_callback_(image_decoded_callback),
+ weak_ptr_factory_(this) {}
+
+ void OnImageDecoded(const gfx::Image& decoded_image) {
+ image_decoded_callback_.Run(decoded_image.AsBitmap());
+ delete this;
+ }
+
+ base::RepeatingCallback<void(const SkBitmap&)> image_decoded_callback_;
+ base::WeakPtrFactory<ImageDecodedHandlerWithTimeout> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImageDecodedHandlerWithTimeout);
+};
+
void ObserverOnLogoAvailable(LogoObserver* observer,
bool from_cache,
LogoCallbackReason type,
@@ -72,6 +125,57 @@
}
}
+// 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::TimeDelta::FromMilliseconds(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 ? base::Optional<EncodedLogo>(*encoded_logo) : base::nullopt;
+ for (EncodedLogoCallback& callback : *encoded_callbacks) {
+ std::move(callback).Run(type, opt_encoded_logo);
+ }
+ encoded_callbacks->clear();
+
+ auto opt_decoded_logo =
+ decoded_logo ? base::Optional<Logo>(*decoded_logo) : base::nullopt;
+ for (LogoCallback& callback : *decoded_callbacks) {
+ std::move(callback).Run(type, opt_decoded_logo);
+ }
+ decoded_callbacks->clear();
+}
+
} // namespace
class LogoServiceImpl::SigninObserver
@@ -120,7 +224,15 @@
signin_observer_(std::make_unique<SigninObserver>(
cookie_service,
base::BindRepeating(&LogoServiceImpl::SigninStatusChanged,
- base::Unretained(this)))) {}
+ base::Unretained(this)))),
+ is_idle_(true),
+ is_cached_logo_valid_(false),
+ cache_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
+ {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
+ base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
+ logo_cache_(new LogoCache(cache_directory_),
+ base::OnTaskRunnerDeleter(cache_task_runner_)),
+ weak_ptr_factory_(this) {}
LogoServiceImpl::~LogoServiceImpl() = default;
@@ -128,6 +240,7 @@
// The GaiaCookieManagerService may be destroyed at any point after Shutdown,
// so make sure we drop any references to it.
signin_observer_.reset();
+ ReturnToIdle(kDownloadOutcomeNotTracked);
}
void LogoServiceImpl::GetLogo(search_provider_logos::LogoObserver* observer) {
@@ -191,59 +304,402 @@
return;
}
- InitializeLogoTrackerIfNecessary();
+ if (!clock_) {
+ clock_ = base::DefaultClock::GetInstance();
+ }
const bool use_fixed_logo = !doodle_url.is_valid();
if (use_fixed_logo) {
- logo_tracker_->SetServerAPI(
- logo_url, base::Bind(&search_provider_logos::ParseFixedLogoResponse),
- base::Bind(&search_provider_logos::UseFixedLogoUrl));
+ 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(), doodle_url);
- logo_tracker_->SetServerAPI(
+ SetServerAPI(
prefilled_url,
base::Bind(&search_provider_logos::ParseDoodleLogoResponse, base_url),
base::Bind(&search_provider_logos::AppendFingerprintParamToDoodleURL));
}
- logo_tracker_->GetLogo(std::move(callbacks));
+ 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::BindRepeating(&GetLogoFromCacheOnFileThread,
+ base::Unretained(logo_cache_.get()), logo_url_,
+ clock_->Now()),
+ base::BindRepeating(&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_for_test_ = std::move(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_for_test_ = clock;
+ clock_ = clock;
}
-void LogoServiceImpl::InitializeLogoTrackerIfNecessary() {
- if (logo_tracker_) {
+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) {
+ // Store the value of logo->encoded_image for use below. This ensures that
+ // logo->encoded_image is evaulated 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.
+ ImageDecodedHandlerWithTimeout::Wrap(base::BindRepeating(
+ &LogoServiceImpl::OnCachedLogoAvailable,
+ weak_ptr_factory_.GetWeakPtr(), base::Passed(&cached_logo))));
+ } else {
+ OnCachedLogoAvailable({}, 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::OnCachedLogoAvailable(
+ std::unique_ptr<EncodedLogo> encoded_logo,
+ const SkBitmap& image) {
+ DCHECK(!is_idle_);
+
+ if (!image.isNull()) {
+ cached_logo_.reset(new Logo());
+ cached_logo_->metadata = encoded_logo->metadata;
+ cached_logo_->image = 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;
+ // TODO(https://crbug.com/808498) re-add data use measurement once
+ // SimpleURLLoader supports it:
+ // data_use_measurement::DataUseUserData::SEARCH_PROVIDER_LOGOS
+ 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) {
+ OnFreshLogoAvailable(std::move(logo), /*download_failed=*/false,
+ *parsing_failed, from_http_cache, SkBitmap());
+ } else {
+ // Store the value of logo->encoded_image for use below. This ensures that
+ // logo->encoded_image is evaulated 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.
+ ImageDecodedHandlerWithTimeout::Wrap(base::BindRepeating(
+ &LogoServiceImpl::OnFreshLogoAvailable,
+ weak_ptr_factory_.GetWeakPtr(), base::Passed(&logo),
+ /*download_failed=*/false, *parsing_failed, from_http_cache)));
+ }
+}
+
+void LogoServiceImpl::OnFreshLogoAvailable(
+ std::unique_ptr<EncodedLogo> encoded_logo,
+ bool download_failed,
+ bool parsing_failed,
+ bool from_http_cache,
+ const SkBitmap& 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;
+ SetCachedMetadata(encoded_logo->metadata);
+ download_outcome = DOWNLOAD_OUTCOME_LOGO_REVALIDATED;
+ } else if (encoded_logo && image.isNull()) {
+ // Image decoding failed. Do nothing.
+ download_outcome = DOWNLOAD_OUTCOME_DECODING_FAILED;
+ } else {
+ // Check if the server returned a valid, non-empty response.
+ if (encoded_logo) {
+ UMA_HISTOGRAM_BOOLEAN("NewTabPage.LogoImageDownloaded", from_http_cache);
+
+ DCHECK(!image.isNull());
+ logo.reset(new Logo());
+ logo->metadata = encoded_logo->metadata;
+ logo->image = 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_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);
+ 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());
return;
}
- std::unique_ptr<LogoCache> logo_cache = std::move(logo_cache_for_test_);
- if (!logo_cache) {
- logo_cache = std::make_unique<LogoCache>(cache_directory_);
+ if (!source->ResponseInfo() || !source->ResponseInfo()->headers ||
+ source->ResponseInfo()->headers->response_code() != net::HTTP_OK) {
+ OnFreshLogoAvailable({}, /*download_failed=*/true, false, false,
+ SkBitmap());
+ return;
}
- base::Clock* clock = clock_for_test_;
- if (!clock) {
- clock = base::DefaultClock::GetInstance();
- }
+ UMA_HISTOGRAM_TIMES("NewTabPage.LogoDownloadTime",
+ base::TimeTicks::Now() - logo_download_start_time_);
- logo_tracker_ = std::make_unique<LogoTracker>(url_loader_factory_,
- std::move(image_decoder_),
- std::move(logo_cache), clock);
+ 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::PostTaskWithTraitsAndReplyWithResult(
+ 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::SigninStatusChanged() {
// Clear any cached logo, since it may be personalized (e.g. birthday Doodle).
- InitializeLogoTrackerIfNecessary();
- logo_tracker_->ClearCachedLogo();
+ if (!clock_) {
+ clock_ = base::DefaultClock::GetInstance();
+ }
+ ClearCachedLogo();
}
} // namespace search_provider_logos
diff --git a/components/search_provider_logos/logo_service_impl.h b/components/search_provider_logos/logo_service_impl.h
index 16bf118..276d72f23 100644
--- a/components/search_provider_logos/logo_service_impl.h
+++ b/components/search_provider_logos/logo_service_impl.h
@@ -5,10 +5,18 @@
#ifndef COMPONENTS_SEARCH_PROVIDER_LOGOS_ANDROID_LOGO_SERVICE_IMPL_H_
#define COMPONENTS_SEARCH_PROVIDER_LOGOS_ANDROID_LOGO_SERVICE_IMPL_H_
+#include <memory>
+#include <string>
+#include <vector>
+
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "base/time/clock.h"
+#include "base/time/time.h"
#include "components/search_provider_logos/logo_common.h"
#include "components/search_provider_logos/logo_service.h"
@@ -24,13 +32,13 @@
} // namespace image_fetcher
namespace network {
+class SimpleURLLoader;
class SharedURLLoaderFactory;
} // namespace network
namespace search_provider_logos {
class LogoCache;
-class LogoTracker;
class LogoObserver;
class LogoServiceImpl : public LogoService {
@@ -48,9 +56,28 @@
// KeyedService implementation.
void Shutdown() override;
- // LogoService implementation.
- void GetLogo(LogoCallbacks callbacks) override;
+ // Defines the server API for downloading and parsing the logo. This must be
+ // called at least once before calling GetLogo().
+ //
+ // |logo_url| is the URL from which the logo will be downloaded. If |logo_url|
+ // is different than the current logo URL, any pending callbacks will be run
+ // with LogoCallbackReason::CANCELED.
+ //
+ // |parse_logo_response_func| is a callback that will be used to parse the
+ // server's response into a EncodedLogo object. |append_queryparams_func| is a
+ // callback that will return the URL from which to download the logo.
+ void SetServerAPI(const GURL& logo_url,
+ const ParseLogoResponse& parse_logo_response_func,
+ const AppendQueryparamsToLogoURL& append_queryparams_func);
+
+ // Retrieves the current search provider's logo from the local cache and/or
+ // over the network, and registers the callbacks to be called when the cached
+ // and/or fresh logos are available.
+ //
+ // At least one callback must be non-null. All non-null callbacks will be
+ // invoked exactly once.
void GetLogo(LogoObserver* observer) override;
+ void GetLogo(LogoCallbacks callbacks) override;
// Overrides the cache used to store logos.
void SetLogoCacheForTests(std::unique_ptr<LogoCache> cache);
@@ -59,12 +86,70 @@
void SetClockForTests(base::Clock* clock);
private:
+ // These values must stay in sync with the NewTabPageLogoDownloadOutcome enum
+ // in histograms.xml. And any addtion should be treated as append-only!
+ // Animated doodle is not covered by this enum.
+ enum LogoDownloadOutcome {
+ DOWNLOAD_OUTCOME_NEW_LOGO_SUCCESS,
+ DOWNLOAD_OUTCOME_NO_LOGO_TODAY,
+ DOWNLOAD_OUTCOME_DOWNLOAD_FAILED,
+ DOWNLOAD_OUTCOME_PARSING_FAILED,
+ DOWNLOAD_OUTCOME_DECODING_FAILED,
+ DOWNLOAD_OUTCOME_LOGO_REVALIDATED,
+ DOWNLOAD_OUTCOME_COUNT,
+ };
+
+ const int kDownloadOutcomeNotTracked = -1;
+
class SigninObserver;
- void InitializeLogoTrackerIfNecessary();
-
void SigninStatusChanged();
+ // Clear any cached logo we might have. Useful on sign-out to get rid of
+ // (potentially) personalized data.
+ void ClearCachedLogo();
+
+ // Cancels the current asynchronous operation, if any, and resets all member
+ // variables that change as the logo is fetched. This method also records UMA
+ // histograms for for the given LogoDownloadOutcome.
+ void ReturnToIdle(int outcome);
+
+ // Called when the cached logo has been read from the cache. |cached_logo|
+ // will be NULL if there wasn't a valid, up-to-date logo in the cache.
+ void OnCachedLogoRead(std::unique_ptr<EncodedLogo> cached_logo);
+
+ // Called when the cached logo has been decoded into an SkBitmap. |image| will
+ // be NULL if decoding failed.
+ void OnCachedLogoAvailable(std::unique_ptr<EncodedLogo> encoded_logo,
+ const SkBitmap& image);
+
+ // Stores |logo| in the cache.
+ void SetCachedLogo(std::unique_ptr<EncodedLogo> logo);
+
+ // Updates the metadata for the logo already stored in the cache.
+ void SetCachedMetadata(const LogoMetadata& metadata);
+
+ // Starts fetching the current logo over the network.
+ void FetchLogo();
+
+ // Called when the logo has been downloaded and parsed. |logo| will be NULL
+ // if the server's response was invalid.
+ void OnFreshLogoParsed(bool* parsing_failed,
+ bool from_http_cache,
+ std::unique_ptr<EncodedLogo> logo);
+
+ // Called when the fresh logo has been decoded into an SkBitmap. |image| will
+ // be NULL if decoding failed.
+ void OnFreshLogoAvailable(std::unique_ptr<EncodedLogo> logo,
+ bool download_failed,
+ bool parsing_failed,
+ bool from_http_cache,
+ const SkBitmap& image);
+
+ // Invoked by |loader|.
+ void OnURLLoadComplete(const network::SimpleURLLoader* source,
+ std::unique_ptr<std::string> body);
+
// Constructor arguments.
const base::FilePath cache_directory_;
TemplateURLService* const template_url_service_;
@@ -74,19 +159,58 @@
// optimized for gray backgrounds or not.
base::RepeatingCallback<bool()> want_gray_logo_getter_;
- // logo_tracker_ takes ownership if/when it is initialized.
std::unique_ptr<image_fetcher::ImageDecoder> image_decoder_;
std::unique_ptr<SigninObserver> signin_observer_;
- // For testing.
- base::Clock* clock_for_test_ = nullptr;
+ // The URL from which the logo is fetched.
+ GURL logo_url_;
- // For testing. logo_tracker_ takes ownership if/when it is initialized.
- std::unique_ptr<LogoCache> logo_cache_for_test_;
+ // The function used to parse the logo response from the server.
+ ParseLogoResponse parse_logo_response_func_;
- // Lazily initialized on first call to GetLogo().
- std::unique_ptr<search_provider_logos::LogoTracker> logo_tracker_;
+ // The function used to include the cached logo's fingerprint and call to
+ // action request in the logo URL.
+ AppendQueryparamsToLogoURL append_queryparams_func_;
+
+ // False if an asynchronous task is currently running.
+ bool is_idle_;
+
+ // The logo that's been read from the cache, or NULL if the cache is empty.
+ // Meaningful only if is_cached_logo_valid_ is true; NULL otherwise.
+ std::unique_ptr<Logo> cached_logo_;
+ std::unique_ptr<EncodedLogo> cached_encoded_logo_;
+
+ // Whether the value of |cached_logo_| reflects the actual cached logo.
+ // This will be false if the logo hasn't been read from the cache yet.
+ // |cached_logo_| may be NULL even if |is_cached_logo_valid_| is true, if no
+ // logo is cached.
+ bool is_cached_logo_valid_;
+
+ // The timestamp for the last time a logo was stated to be downloaded.
+ base::TimeTicks logo_download_start_time_;
+
+ // The SimpleURLLoader currently fetching the logo. NULL when not loading.
+ std::unique_ptr<network::SimpleURLLoader> loader_;
+
+ // Lists of callbacks to be invoked when logos are available. All should be
+ // empty when the state is IDLE.
+ std::vector<LogoCallback> on_cached_decoded_logo_;
+ std::vector<EncodedLogoCallback> on_cached_encoded_logo_;
+ std::vector<LogoCallback> on_fresh_decoded_logo_;
+ std::vector<EncodedLogoCallback> on_fresh_encoded_logo_;
+
+ // The SequencedTaskRunner on which the cache lives.
+ scoped_refptr<base::SequencedTaskRunner> cache_task_runner_;
+
+ // The cache used to persist the logo on disk. Used only on a background
+ // SequencedTaskRunner.
+ std::unique_ptr<LogoCache, base::OnTaskRunnerDeleter> logo_cache_;
+
+ // Clock used to determine current time. Can be overridden in tests.
+ base::Clock* clock_ = nullptr;
+
+ base::WeakPtrFactory<LogoServiceImpl> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(LogoServiceImpl);
};
diff --git a/components/search_provider_logos/logo_tracker.cc b/components/search_provider_logos/logo_tracker.cc
deleted file mode 100644
index bb2f5bf..0000000
--- a/components/search_provider_logos/logo_tracker.cc
+++ /dev/null
@@ -1,506 +0,0 @@
-// Copyright 2014 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_tracker.h"
-
-#include <algorithm>
-#include <utility>
-
-#include "base/bind_helpers.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/task/post_task.h"
-#include "base/task_runner_util.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/clock.h"
-#include "components/data_use_measurement/core/data_use_user_data.h"
-#include "components/image_fetcher/core/image_decoder.h"
-#include "net/http/http_response_headers.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 "ui/gfx/geometry/size.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:
- static base::Callback<void(const gfx::Image&)> Wrap(
- const base::Callback<void(const SkBitmap&)>& image_decoded_callback) {
- auto* handler = new ImageDecodedHandlerWithTimeout(image_decoded_callback);
- base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(&ImageDecodedHandlerWithTimeout::OnImageDecoded,
- handler->weak_ptr_factory_.GetWeakPtr(), gfx::Image()),
- base::TimeDelta::FromSeconds(kDecodeLogoTimeoutSeconds));
- return base::Bind(&ImageDecodedHandlerWithTimeout::OnImageDecoded,
- handler->weak_ptr_factory_.GetWeakPtr());
- }
-
- private:
- explicit ImageDecodedHandlerWithTimeout(
- const base::Callback<void(const SkBitmap&)>& image_decoded_callback)
- : image_decoded_callback_(image_decoded_callback),
- weak_ptr_factory_(this) {}
-
- void OnImageDecoded(const gfx::Image& decoded_image) {
- image_decoded_callback_.Run(decoded_image.AsBitmap());
- delete this;
- }
-
- base::Callback<void(const SkBitmap&)> image_decoded_callback_;
- base::WeakPtrFactory<ImageDecodedHandlerWithTimeout> weak_ptr_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(ImageDecodedHandlerWithTimeout);
-};
-
-// 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::TimeDelta::FromMilliseconds(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 ? base::Optional<EncodedLogo>(*encoded_logo) : base::nullopt;
- for (EncodedLogoCallback& callback : *encoded_callbacks) {
- std::move(callback).Run(type, opt_encoded_logo);
- }
- encoded_callbacks->clear();
-
- auto opt_decoded_logo =
- decoded_logo ? base::Optional<Logo>(*decoded_logo) : base::nullopt;
- for (LogoCallback& callback : *decoded_callbacks) {
- std::move(callback).Run(type, opt_decoded_logo);
- }
- decoded_callbacks->clear();
-}
-
-} // namespace
-
-LogoTracker::LogoTracker(
- scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
- std::unique_ptr<image_fetcher::ImageDecoder> image_decoder,
- std::unique_ptr<LogoCache> logo_cache,
- base::Clock* clock)
- : is_idle_(true),
- is_cached_logo_valid_(false),
- image_decoder_(std::move(image_decoder)),
- cache_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
- {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
- base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
- logo_cache_(logo_cache.release(),
- base::OnTaskRunnerDeleter(cache_task_runner_)),
- clock_(clock),
- url_loader_factory_(std::move(url_loader_factory)),
- weak_ptr_factory_(this) {}
-
-LogoTracker::~LogoTracker() {
- ReturnToIdle(kDownloadOutcomeNotTracked);
-}
-
-void LogoTracker::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 LogoTracker::GetLogo(LogoCallbacks callbacks) {
- 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::Bind(&GetLogoFromCacheOnFileThread,
- base::Unretained(logo_cache_.get()), logo_url_,
- clock_->Now()),
- base::Bind(&LogoTracker::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 LogoTracker::ClearCachedLogo() {
- // First cancel any fetch that might be ongoing.
- ReturnToIdle(kDownloadOutcomeNotTracked);
- // Then clear any cached logo.
- SetCachedLogo(nullptr);
-}
-
-void LogoTracker::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 LogoTracker::OnCachedLogoRead(std::unique_ptr<EncodedLogo> cached_logo) {
- DCHECK(!is_idle_);
-
- if (cached_logo) {
- // Store the value of logo->encoded_image for use below. This ensures that
- // logo->encoded_image is evaulated 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.
- ImageDecodedHandlerWithTimeout::Wrap(base::Bind(
- &LogoTracker::OnCachedLogoAvailable, weak_ptr_factory_.GetWeakPtr(),
- base::Passed(&cached_logo))));
- } else {
- OnCachedLogoAvailable({}, SkBitmap());
- }
-}
-
-void LogoTracker::OnCachedLogoAvailable(
- std::unique_ptr<EncodedLogo> encoded_logo,
- const SkBitmap& image) {
- DCHECK(!is_idle_);
-
- if (!image.isNull()) {
- cached_logo_.reset(new Logo());
- cached_logo_->metadata = encoded_logo->metadata;
- cached_logo_->image = 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 LogoTracker::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 LogoTracker::SetCachedMetadata(const LogoMetadata& metadata) {
- cache_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&LogoCache::UpdateCachedLogoMetadata,
- base::Unretained(logo_cache_.get()), metadata));
-}
-
-void LogoTracker::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_tracker", R"(
- semantics {
- sender: "Logo Tracker"
- 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;
- // TODO(https://crbug.com/808498) re-add data use measurement once
- // SimpleURLLoader supports it:
- // data_use_measurement::DataUseUserData::SEARCH_PROVIDER_LOGOS
- loader_ =
- network::SimpleURLLoader::Create(std::move(request), traffic_annotation);
- loader_->DownloadToString(
- url_loader_factory_.get(),
- base::BindOnce(&LogoTracker::OnURLLoadComplete, base::Unretained(this),
- loader_.get()),
- kMaxDownloadBytes);
- logo_download_start_time_ = base::TimeTicks::Now();
-}
-
-void LogoTracker::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) {
- OnFreshLogoAvailable(std::move(logo), /*download_failed=*/false,
- *parsing_failed, from_http_cache, SkBitmap());
- } else {
- // Store the value of logo->encoded_image for use below. This ensures that
- // logo->encoded_image is evaulated 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.
- ImageDecodedHandlerWithTimeout::Wrap(base::Bind(
- &LogoTracker::OnFreshLogoAvailable, weak_ptr_factory_.GetWeakPtr(),
- base::Passed(&logo), /*download_failed=*/false, *parsing_failed,
- from_http_cache)));
- }
-}
-
-void LogoTracker::OnFreshLogoAvailable(
- std::unique_ptr<EncodedLogo> encoded_logo,
- bool download_failed,
- bool parsing_failed,
- bool from_http_cache,
- const SkBitmap& 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;
- SetCachedMetadata(encoded_logo->metadata);
- download_outcome = DOWNLOAD_OUTCOME_LOGO_REVALIDATED;
- } else if (encoded_logo && image.isNull()) {
- // Image decoding failed. Do nothing.
- download_outcome = DOWNLOAD_OUTCOME_DECODING_FAILED;
- } else {
- // Check if the server returned a valid, non-empty response.
- if (encoded_logo) {
- UMA_HISTOGRAM_BOOLEAN("NewTabPage.LogoImageDownloaded", from_http_cache);
-
- DCHECK(!image.isNull());
- logo.reset(new Logo());
- logo->metadata = encoded_logo->metadata;
- logo->image = 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_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);
- 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 LogoTracker::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());
- return;
- }
-
- if (!source->ResponseInfo() || !source->ResponseInfo()->headers ||
- source->ResponseInfo()->headers->response_code() != net::HTTP_OK) {
- OnFreshLogoAvailable({}, /*download_failed=*/true, false, false,
- 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::PostTaskWithTraitsAndReplyWithResult(
- 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(&LogoTracker::OnFreshLogoParsed,
- weak_ptr_factory_.GetWeakPtr(),
- base::Owned(parsing_failed), from_http_cache));
-}
-
-} // namespace search_provider_logos
diff --git a/components/search_provider_logos/logo_tracker.h b/components/search_provider_logos/logo_tracker.h
index 762aa8db..af057c8 100644
--- a/components/search_provider_logos/logo_tracker.h
+++ b/components/search_provider_logos/logo_tracker.h
@@ -5,32 +5,7 @@
#ifndef COMPONENTS_SEARCH_PROVIDER_LOGOS_LOGO_TRACKER_H_
#define COMPONENTS_SEARCH_PROVIDER_LOGOS_LOGO_TRACKER_H_
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/files/file_path.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/optional.h"
-#include "base/sequenced_task_runner.h"
-#include "base/time/clock.h"
-#include "base/time/time.h"
-#include "components/search_provider_logos/logo_cache.h"
#include "components/search_provider_logos/logo_common.h"
-#include "url/gurl.h"
-
-namespace image_fetcher {
-class ImageDecoder;
-}
-
-namespace network {
-class SimpleURLLoader;
-class SharedURLLoaderFactory;
-}
namespace search_provider_logos {
@@ -55,173 +30,6 @@
virtual void OnObserverRemoved() = 0;
};
-// This class provides the logo for a search provider. Logos are downloaded from
-// the search provider's logo URL and cached on disk.
-//
-// Call SetServerAPI() at least once to specify how to get the logo from the
-// server. Then call GetLogo() to trigger retrieval of the logo and receive
-// updates once the cached and/or fresh logos are available.
-class LogoTracker {
- public:
- // Constructs a LogoTracker with the given LogoDelegate. Takes ownership of
- // |delegate|, which will be deleted at the same time as the LogoTracker.
- //
- // |cached_logo_directory| is the directory in which the cached logo and its
- // metadata should be saved.
- //
- // |background_task_runner| is the TaskRunner that should be used to for
- // CPU-intensive background operations.
- //
- // |url_loader_factory| is the SharedURLLoaderFactory used to download
- // the logo.
- explicit LogoTracker(
- scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
- std::unique_ptr<image_fetcher::ImageDecoder> image_decoder,
- std::unique_ptr<LogoCache> logo_cache,
- base::Clock* clock);
-
- ~LogoTracker();
-
- // Defines the server API for downloading and parsing the logo. This must be
- // called at least once before calling GetLogo().
- //
- // |logo_url| is the URL from which the logo will be downloaded. If |logo_url|
- // is different than the current logo URL, any pending callbacks will be run
- // with LogoCallbackReason::CANCELED.
- //
- // |parse_logo_response_func| is a callback that will be used to parse the
- // server's response into a EncodedLogo object. |append_queryparams_func| is a
- // callback that will return the URL from which to download the logo.
- void SetServerAPI(const GURL& logo_url,
- const ParseLogoResponse& parse_logo_response_func,
- const AppendQueryparamsToLogoURL& append_queryparams_func);
-
- // Retrieves the current search provider's logo from the local cache and/or
- // over the network, and registers the callbacks to be called when the cached
- // and/or fresh logos are available.
- //
- // At least one callback must be non-null. All non-null callbacks will be
- // invoked exactly once.
- void GetLogo(LogoCallbacks callbacks);
-
- // Clear any cached logo we might have. Useful on sign-out to get rid of
- // (potentially) personalized data.
- void ClearCachedLogo();
-
- private:
- // These values must stay in sync with the NewTabPageLogoDownloadOutcome enum
- // in histograms.xml. And any addtion should be treated as append-only!
- // Animated doodle is not covered by this enum.
- enum LogoDownloadOutcome {
- DOWNLOAD_OUTCOME_NEW_LOGO_SUCCESS,
- DOWNLOAD_OUTCOME_NO_LOGO_TODAY,
- DOWNLOAD_OUTCOME_DOWNLOAD_FAILED,
- DOWNLOAD_OUTCOME_PARSING_FAILED,
- DOWNLOAD_OUTCOME_DECODING_FAILED,
- DOWNLOAD_OUTCOME_LOGO_REVALIDATED,
- DOWNLOAD_OUTCOME_COUNT,
- };
-
- const int kDownloadOutcomeNotTracked = -1;
-
- // Cancels the current asynchronous operation, if any, and resets all member
- // variables that change as the logo is fetched. This method also records UMA
- // histograms for for the given LogoDownloadOutcome.
- void ReturnToIdle(int outcome);
-
- // Called when the cached logo has been read from the cache. |cached_logo|
- // will be NULL if there wasn't a valid, up-to-date logo in the cache.
- void OnCachedLogoRead(std::unique_ptr<EncodedLogo> cached_logo);
-
- // Called when the cached logo has been decoded into an SkBitmap. |image| will
- // be NULL if decoding failed.
- void OnCachedLogoAvailable(std::unique_ptr<EncodedLogo> encoded_logo,
- const SkBitmap& image);
-
- // Stores |logo| in the cache.
- void SetCachedLogo(std::unique_ptr<EncodedLogo> logo);
-
- // Updates the metadata for the logo already stored in the cache.
- void SetCachedMetadata(const LogoMetadata& metadata);
-
- // Starts fetching the current logo over the network.
- void FetchLogo();
-
- // Called when the logo has been downloaded and parsed. |logo| will be NULL
- // if the server's response was invalid.
- void OnFreshLogoParsed(bool* parsing_failed,
- bool from_http_cache,
- std::unique_ptr<EncodedLogo> logo);
-
- // Called when the fresh logo has been decoded into an SkBitmap. |image| will
- // be NULL if decoding failed.
- void OnFreshLogoAvailable(std::unique_ptr<EncodedLogo> logo,
- bool download_failed,
- bool parsing_failed,
- bool from_http_cache,
- const SkBitmap& image);
-
- // Invoked by |loader|.
- void OnURLLoadComplete(const network::SimpleURLLoader* source,
- std::unique_ptr<std::string> body);
-
- // The URL from which the logo is fetched.
- GURL logo_url_;
-
- // The function used to parse the logo response from the server.
- ParseLogoResponse parse_logo_response_func_;
-
- // The function used to include the cached logo's fingerprint and call to
- // action request in the logo URL.
- AppendQueryparamsToLogoURL append_queryparams_func_;
-
- // False if an asynchronous task is currently running.
- bool is_idle_;
-
- // The logo that's been read from the cache, or NULL if the cache is empty.
- // Meaningful only if is_cached_logo_valid_ is true; NULL otherwise.
- std::unique_ptr<Logo> cached_logo_;
- std::unique_ptr<EncodedLogo> cached_encoded_logo_;
-
- // Whether the value of |cached_logo_| reflects the actual cached logo.
- // This will be false if the logo hasn't been read from the cache yet.
- // |cached_logo_| may be NULL even if |is_cached_logo_valid_| is true, if no
- // logo is cached.
- bool is_cached_logo_valid_;
-
- // The timestamp for the last time a logo is stated to be downloaded.
- base::TimeTicks logo_download_start_time_;
-
- // The SimpleURLLoader currently fetching the logo. NULL when not loading.
- std::unique_ptr<network::SimpleURLLoader> loader_;
-
- // Lists of callbacks to be invoked when logos are available. All should be
- // empty when the state is IDLE.
- std::vector<LogoCallback> on_cached_decoded_logo_;
- std::vector<EncodedLogoCallback> on_cached_encoded_logo_;
- std::vector<LogoCallback> on_fresh_decoded_logo_;
- std::vector<EncodedLogoCallback> on_fresh_encoded_logo_;
-
- const std::unique_ptr<image_fetcher::ImageDecoder> image_decoder_;
-
- // The SequencedTaskRunner on which the cache lives.
- scoped_refptr<base::SequencedTaskRunner> cache_task_runner_;
-
- // The cache used to persist the logo on disk. Used only on a background
- // SequencedTaskRunner.
- std::unique_ptr<LogoCache, base::OnTaskRunnerDeleter> logo_cache_;
-
- // Clock used to determine current time. Can be overridden in tests.
- base::Clock* clock_;
-
- // Used for network requests.
- scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-
- base::WeakPtrFactory<LogoTracker> weak_ptr_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(LogoTracker);
-};
-
} // namespace search_provider_logos
#endif // COMPONENTS_SEARCH_PROVIDER_LOGOS_LOGO_TRACKER_H_
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 98445ce..3f02682 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -36878,7 +36878,7 @@
<enum name="NewTabPageLogoDownloadOutcome">
<summary>
These values are defined in LogoDownloadOutcome enum in
- components/search_provider_logos/logo_tracker.h.
+ components/search_provider_logos/logo_service_impl.h.
</summary>
<int value="0" label="New logo success"/>
<int value="1" label="No logo today"/>
@@ -51419,7 +51419,7 @@
<int value="35370363" label="suggestions_service"/>
<int value="35380758" label="download_manager_resume"/>
<int value="35565745" label="gaia_auth_list_accounts"/>
- <int value="36859107" label="logo_tracker"/>
+ <int value="36859107" label="logo_service"/>
<int value="37249086" label="android_device_manager_socket"/>
<int value="37531401" label="proxy_script_fetcher"/>
<int value="38670228" label="download_internals_webui_source"/>
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 473df42..f3c86896 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -142,7 +142,8 @@
<item id="intranet_redirect_detector" hash_code="21785164" type="0" content_hash_code="62025595" os_list="linux,windows" file_path="chrome/browser/intranet_redirect_detector.cc"/>
<item id="invalidation_service" hash_code="72354423" type="0" content_hash_code="78425687" os_list="linux,windows" file_path="components/invalidation/impl/gcm_network_channel.cc"/>
<item id="lib_address_input" hash_code="50816767" type="0" content_hash_code="57977576" os_list="linux,windows" file_path="third_party/libaddressinput/chromium/chrome_metadata_source.cc"/>
- <item id="logo_tracker" hash_code="36859107" type="0" content_hash_code="67588075" os_list="linux,windows" file_path="components/search_provider_logos/logo_tracker.cc"/>
+ <item id="logo_service" hash_code="35473769" type="0" content_hash_code="20271299" os_list="linux,windows" file_path="components/search_provider_logos/logo_service_impl.cc"/>
+ <item id="logo_tracker" hash_code="36859107" type="0" deprecated="2018-12-07" content_hash_code="67588075" file_path=""/>
<item id="md_downloads_dom_handler" hash_code="65603364" type="0" content_hash_code="134779147" os_list="linux,windows" file_path="chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler.cc"/>
<item id="metrics_report_ukm" hash_code="727478" type="0" content_hash_code="40919254" os_list="linux,windows" file_path="components/metrics/net/net_metrics_log_uploader.cc"/>
<item id="metrics_report_uma" hash_code="727528" type="0" content_hash_code="10176197" os_list="linux,windows" file_path="components/metrics/net/net_metrics_log_uploader.cc"/>