1// Copyright 2014 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
5#include "chrome/browser/android/logo_service.h"
6
7#include "base/memory/weak_ptr.h"
8#include "chrome/browser/google/google_profile_helper.h"
9#include "chrome/browser/image_decoder.h"
10#include "chrome/browser/profiles/profile.h"
11#include "chrome/browser/search_engines/template_url_service_factory.h"
12#include "components/google/core/browser/google_url_tracker.h"
13#include "components/google/core/browser/google_util.h"
14#include "components/keyed_service/content/browser_context_dependency_manager.h"
15#include "components/search_engines/template_url_service.h"
16#include "components/search_provider_logos/google_logo_api.h"
17#include "content/public/browser/browser_thread.h"
18#include "net/url_request/url_request_context_getter.h"
19
20using content::BrowserThread;
21using search_provider_logos::Logo;
22using search_provider_logos::LogoDelegate;
23using search_provider_logos::LogoTracker;
24
25namespace {
26
27const char kGoogleDoodleURLPath[] = "async/newtab_mobile";
28const char kCachedLogoDirectory[] = "Search Logo";
29const int kDecodeLogoTimeoutSeconds = 30;
30
31// Returns the URL where the doodle can be downloaded, e.g.
32// https://www.google.com/async/newtab_mobile. This depends on the user's
33// Google domain.
34GURL GetGoogleDoodleURL(Profile* profile) {
35  // SetPathStr() requires its argument to stay in scope as long as
36  // |replacements| is, so a std::string is needed, instead of a char*.
37  std::string path = kGoogleDoodleURLPath;
38  GURL::Replacements replacements;
39  replacements.SetPathStr(path);
40
41  GURL base_url(google_util::CommandLineGoogleBaseURL());
42  if (!base_url.is_valid())
43    base_url = google_profile_helper::GetGoogleHomePageURL(profile);
44  return base_url.ReplaceComponents(replacements);
45}
46
47class LogoDecoderDelegate : public ImageDecoder::Delegate {
48 public:
49  LogoDecoderDelegate(
50      const scoped_refptr<ImageDecoder>& image_decoder,
51      const base::Callback<void(const SkBitmap&)>& image_decoded_callback)
52      : image_decoder_(image_decoder),
53        image_decoded_callback_(image_decoded_callback),
54        weak_ptr_factory_(this) {
55    // If the ImageDecoder crashes or otherwise never completes, call
56    // OnImageDecodeTimedOut() eventually to ensure that image_decoded_callback_
57    // is run.
58    base::MessageLoopProxy::current()->PostDelayedTask(
59        FROM_HERE,
60        base::Bind(&LogoDecoderDelegate::OnDecodeImageFailed,
61                   weak_ptr_factory_.GetWeakPtr(),
62                   (const ImageDecoder*) NULL),
63        base::TimeDelta::FromSeconds(kDecodeLogoTimeoutSeconds));
64  }
65
66  virtual ~LogoDecoderDelegate() {
67    image_decoder_->set_delegate(NULL);
68  }
69
70  // ImageDecoder::Delegate:
71  virtual void OnImageDecoded(const ImageDecoder* decoder,
72                              const SkBitmap& decoded_image) OVERRIDE {
73    image_decoded_callback_.Run(decoded_image);
74    delete this;
75  }
76
77  virtual void OnDecodeImageFailed(const ImageDecoder* decoder) OVERRIDE {
78    image_decoded_callback_.Run(SkBitmap());
79    delete this;
80  }
81
82 private:
83  scoped_refptr<ImageDecoder> image_decoder_;
84  base::Callback<void(const SkBitmap&)> image_decoded_callback_;
85  base::WeakPtrFactory<LogoDecoderDelegate> weak_ptr_factory_;
86
87  DISALLOW_COPY_AND_ASSIGN(LogoDecoderDelegate);
88};
89
90class ChromeLogoDelegate : public search_provider_logos::LogoDelegate {
91 public:
92  ChromeLogoDelegate() {}
93  virtual ~ChromeLogoDelegate() {}
94
95  // search_provider_logos::LogoDelegate:
96  virtual void DecodeUntrustedImage(
97      const scoped_refptr<base::RefCountedString>& encoded_image,
98      base::Callback<void(const SkBitmap&)> image_decoded_callback) OVERRIDE {
99    scoped_refptr<ImageDecoder> image_decoder = new ImageDecoder(
100        NULL,
101        encoded_image->data(),
102        ImageDecoder::DEFAULT_CODEC);
103    LogoDecoderDelegate* delegate =
104        new LogoDecoderDelegate(image_decoder, image_decoded_callback);
105    image_decoder->set_delegate(delegate);
106    image_decoder->Start(base::MessageLoopProxy::current());
107  }
108
109 private:
110  DISALLOW_COPY_AND_ASSIGN(ChromeLogoDelegate);
111};
112
113}  // namespace
114
115// LogoService ----------------------------------------------------------------
116
117LogoService::LogoService(Profile* profile) : profile_(profile) {
118}
119
120LogoService::~LogoService() {
121}
122
123void LogoService::GetLogo(search_provider_logos::LogoObserver* observer) {
124  TemplateURLService* template_url_service =
125      TemplateURLServiceFactory::GetForProfile(profile_);
126  if (!template_url_service)
127    return;
128
129  TemplateURL* template_url = template_url_service->GetDefaultSearchProvider();
130  if (!template_url || !template_url->url_ref().HasGoogleBaseURLs(
131          template_url_service->search_terms_data()))
132    return;
133
134  if (!logo_tracker_) {
135    logo_tracker_.reset(new LogoTracker(
136        profile_->GetPath().Append(kCachedLogoDirectory),
137        BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE),
138        BrowserThread::GetBlockingPool(),
139        profile_->GetRequestContext(),
140        scoped_ptr<search_provider_logos::LogoDelegate>(
141            new ChromeLogoDelegate())));
142  }
143
144  logo_tracker_->SetServerAPI(
145      GetGoogleDoodleURL(profile_),
146      base::Bind(&search_provider_logos::GoogleParseLogoResponse),
147      base::Bind(&search_provider_logos::GoogleAppendFingerprintToLogoURL));
148  logo_tracker_->GetLogo(observer);
149}
150
151// LogoServiceFactory ---------------------------------------------------------
152
153// static
154LogoService* LogoServiceFactory::GetForProfile(Profile* profile) {
155  return static_cast<LogoService*>(
156      GetInstance()->GetServiceForBrowserContext(profile, true));
157}
158
159// static
160LogoServiceFactory* LogoServiceFactory::GetInstance() {
161  return Singleton<LogoServiceFactory>::get();
162}
163
164LogoServiceFactory::LogoServiceFactory()
165    : BrowserContextKeyedServiceFactory(
166          "LogoService",
167          BrowserContextDependencyManager::GetInstance()) {
168}
169
170LogoServiceFactory::~LogoServiceFactory() {}
171
172KeyedService* LogoServiceFactory::BuildServiceInstanceFor(
173    content::BrowserContext* context) const {
174  Profile* profile = static_cast<Profile*>(context);
175  DCHECK(!profile->IsOffTheRecord());
176  return new LogoService(profile);
177}
178