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