Home | History | Annotate | Download | only in web_resource
      1 // Copyright (c) 2011 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/web_resource/web_resource_service.h"
      6 
      7 #include "base/command_line.h"
      8 #include "base/file_path.h"
      9 #include "base/string_number_conversions.h"
     10 #include "base/string_util.h"
     11 #include "base/threading/thread_restrictions.h"
     12 #include "base/time.h"
     13 #include "base/utf_string_conversions.h"
     14 #include "base/values.h"
     15 #include "chrome/browser/browser_process.h"
     16 #include "chrome/browser/prefs/pref_service.h"
     17 #include "chrome/browser/profiles/profile.h"
     18 #include "chrome/browser/sync/sync_ui_util.h"
     19 #include "chrome/common/chrome_switches.h"
     20 #include "chrome/common/extensions/extension.h"
     21 #include "chrome/common/net/url_fetcher.h"
     22 #include "chrome/common/web_resource/web_resource_unpacker.h"
     23 #include "content/browser/browser_thread.h"
     24 #include "content/common/notification_service.h"
     25 #include "googleurl/src/gurl.h"
     26 #include "net/base/load_flags.h"
     27 #include "net/url_request/url_request_status.h"
     28 
     29 class WebResourceService::WebResourceFetcher
     30     : public URLFetcher::Delegate {
     31  public:
     32   explicit WebResourceFetcher(WebResourceService* web_resource_service) :
     33       ALLOW_THIS_IN_INITIALIZER_LIST(fetcher_factory_(this)),
     34       web_resource_service_(web_resource_service) {
     35   }
     36 
     37   // Delay initial load of resource data into cache so as not to interfere
     38   // with startup time.
     39   void StartAfterDelay(int64 delay_ms) {
     40     MessageLoop::current()->PostDelayedTask(FROM_HERE,
     41         fetcher_factory_.NewRunnableMethod(&WebResourceFetcher::StartFetch),
     42                                            delay_ms);
     43   }
     44 
     45   // Initializes the fetching of data from the resource server.  Data
     46   // load calls OnURLFetchComplete.
     47   void StartFetch() {
     48     // Balanced in OnURLFetchComplete.
     49     web_resource_service_->AddRef();
     50     // First, put our next cache load on the MessageLoop.
     51     MessageLoop::current()->PostDelayedTask(FROM_HERE,
     52         fetcher_factory_.NewRunnableMethod(&WebResourceFetcher::StartFetch),
     53             web_resource_service_->cache_update_delay_);
     54     // If we are still fetching data, exit.
     55     if (web_resource_service_->in_fetch_)
     56       return;
     57     else
     58       web_resource_service_->in_fetch_ = true;
     59 
     60     std::string web_resource_server =
     61         web_resource_service_->web_resource_server_;
     62     if (web_resource_service_->apply_locale_to_url_) {
     63       std::string locale = g_browser_process->GetApplicationLocale();
     64       web_resource_server.append(locale);
     65     }
     66 
     67     url_fetcher_.reset(new URLFetcher(GURL(
     68         web_resource_server),
     69         URLFetcher::GET, this));
     70     // Do not let url fetcher affect existing state in profile (by setting
     71     // cookies, for example.
     72     url_fetcher_->set_load_flags(net::LOAD_DISABLE_CACHE |
     73         net::LOAD_DO_NOT_SAVE_COOKIES);
     74     net::URLRequestContextGetter* url_request_context_getter =
     75         web_resource_service_->profile_->GetRequestContext();
     76     url_fetcher_->set_request_context(url_request_context_getter);
     77     url_fetcher_->Start();
     78   }
     79 
     80   // From URLFetcher::Delegate.
     81   void OnURLFetchComplete(const URLFetcher* source,
     82                           const GURL& url,
     83                           const net::URLRequestStatus& status,
     84                           int response_code,
     85                           const ResponseCookies& cookies,
     86                           const std::string& data) {
     87     // Delete the URLFetcher when this function exits.
     88     scoped_ptr<URLFetcher> clean_up_fetcher(url_fetcher_.release());
     89 
     90     // Don't parse data if attempt to download was unsuccessful.
     91     // Stop loading new web resource data, and silently exit.
     92     if (!status.is_success() || (response_code != 200))
     93       return;
     94 
     95     web_resource_service_->UpdateResourceCache(data);
     96     web_resource_service_->Release();
     97   }
     98 
     99  private:
    100   // So that we can delay our start so as not to affect start-up time; also,
    101   // so that we can schedule future cache updates.
    102   ScopedRunnableMethodFactory<WebResourceFetcher> fetcher_factory_;
    103 
    104   // The tool that fetches the url data from the server.
    105   scoped_ptr<URLFetcher> url_fetcher_;
    106 
    107   // Our owner and creator. Ref counted.
    108   WebResourceService* web_resource_service_;
    109 };
    110 
    111 // This class coordinates a web resource unpack and parse task which is run in
    112 // a separate process.  Results are sent back to this class and routed to
    113 // the WebResourceService.
    114 class WebResourceService::UnpackerClient
    115     : public UtilityProcessHost::Client {
    116  public:
    117   UnpackerClient(WebResourceService* web_resource_service,
    118                  const std::string& json_data)
    119     : web_resource_service_(web_resource_service),
    120       json_data_(json_data), got_response_(false) {
    121   }
    122 
    123   void Start() {
    124     AddRef();  // balanced in Cleanup.
    125 
    126     // TODO(willchan): Look for a better signal of whether we're in a unit test
    127     // or not. Using |resource_dispatcher_host_| for this is pretty lame.
    128     // If we don't have a resource_dispatcher_host_, assume we're in
    129     // a test and run the unpacker directly in-process.
    130     bool use_utility_process =
    131         web_resource_service_->resource_dispatcher_host_ != NULL &&
    132         !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess);
    133     if (use_utility_process) {
    134       BrowserThread::ID thread_id;
    135       CHECK(BrowserThread::GetCurrentThreadIdentifier(&thread_id));
    136       BrowserThread::PostTask(
    137           BrowserThread::IO, FROM_HERE,
    138           NewRunnableMethod(this, &UnpackerClient::StartProcessOnIOThread,
    139                             thread_id));
    140     } else {
    141       WebResourceUnpacker unpacker(json_data_);
    142       if (unpacker.Run()) {
    143         OnUnpackWebResourceSucceeded(*unpacker.parsed_json());
    144       } else {
    145         OnUnpackWebResourceFailed(unpacker.error_message());
    146       }
    147     }
    148   }
    149 
    150  private:
    151   ~UnpackerClient() {}
    152 
    153   // UtilityProcessHost::Client
    154   virtual void OnProcessCrashed(int exit_code) {
    155     if (got_response_)
    156       return;
    157 
    158     OnUnpackWebResourceFailed(
    159         "Chrome crashed while trying to retrieve web resources.");
    160   }
    161 
    162   virtual void OnUnpackWebResourceSucceeded(
    163       const DictionaryValue& parsed_json) {
    164     web_resource_service_->OnWebResourceUnpacked(parsed_json);
    165     Cleanup();
    166   }
    167 
    168   virtual void OnUnpackWebResourceFailed(const std::string& error_message) {
    169     web_resource_service_->EndFetch();
    170     Cleanup();
    171   }
    172 
    173   // Release reference and set got_response_.
    174   void Cleanup() {
    175     if (got_response_)
    176       return;
    177 
    178     got_response_ = true;
    179     Release();
    180   }
    181 
    182   void StartProcessOnIOThread(BrowserThread::ID thread_id) {
    183     UtilityProcessHost* host = new UtilityProcessHost(this, thread_id);
    184     // TODO(mrc): get proper file path when we start using web resources
    185     // that need to be unpacked.
    186     host->StartWebResourceUnpacker(json_data_);
    187   }
    188 
    189   scoped_refptr<WebResourceService> web_resource_service_;
    190 
    191   // Holds raw JSON string.
    192   const std::string& json_data_;
    193 
    194   // True if we got a response from the utility process and have cleaned up
    195   // already.
    196   bool got_response_;
    197 };
    198 
    199 WebResourceService::WebResourceService(
    200     Profile* profile,
    201     PrefService* prefs,
    202     const char* web_resource_server,
    203     bool apply_locale_to_url,
    204     NotificationType::Type notification_type,
    205     const char* last_update_time_pref_name,
    206     int start_fetch_delay,
    207     int cache_update_delay)
    208     : prefs_(prefs),
    209       profile_(profile),
    210       ALLOW_THIS_IN_INITIALIZER_LIST(service_factory_(this)),
    211       in_fetch_(false),
    212       web_resource_server_(web_resource_server),
    213       apply_locale_to_url_(apply_locale_to_url),
    214       notification_type_(notification_type),
    215       last_update_time_pref_name_(last_update_time_pref_name),
    216       start_fetch_delay_(start_fetch_delay),
    217       cache_update_delay_(cache_update_delay),
    218       web_resource_update_scheduled_(false) {
    219   DCHECK(prefs);
    220   DCHECK(profile);
    221   prefs_->RegisterStringPref(last_update_time_pref_name, "0");
    222   resource_dispatcher_host_ = g_browser_process->resource_dispatcher_host();
    223   web_resource_fetcher_.reset(new WebResourceFetcher(this));
    224 }
    225 
    226 WebResourceService::~WebResourceService() { }
    227 
    228 void WebResourceService::PostNotification(int64 delay_ms) {
    229   if (web_resource_update_scheduled_)
    230     return;
    231   if (delay_ms > 0) {
    232     web_resource_update_scheduled_ = true;
    233     MessageLoop::current()->PostDelayedTask(FROM_HERE,
    234         service_factory_.NewRunnableMethod(
    235             &WebResourceService::WebResourceStateChange), delay_ms);
    236   } else if (delay_ms == 0) {
    237     WebResourceStateChange();
    238   }
    239 }
    240 
    241 void WebResourceService::EndFetch() {
    242   in_fetch_ = false;
    243 }
    244 
    245 void WebResourceService::OnWebResourceUnpacked(
    246   const DictionaryValue& parsed_json) {
    247   Unpack(parsed_json);
    248   EndFetch();
    249 }
    250 
    251 void WebResourceService::WebResourceStateChange() {
    252   web_resource_update_scheduled_ = false;
    253   if (notification_type_ == NotificationType::NOTIFICATION_TYPE_COUNT)
    254     return;
    255   NotificationService* service = NotificationService::current();
    256   service->Notify(notification_type_,
    257                   Source<WebResourceService>(this),
    258                   NotificationService::NoDetails());
    259 }
    260 
    261 void WebResourceService::StartAfterDelay() {
    262   int64 delay = start_fetch_delay_;
    263   // Check whether we have ever put a value in the web resource cache;
    264   // if so, pull it out and see if it's time to update again.
    265   if (prefs_->HasPrefPath(last_update_time_pref_name_)) {
    266     std::string last_update_pref =
    267         prefs_->GetString(last_update_time_pref_name_);
    268     if (!last_update_pref.empty()) {
    269       double last_update_value;
    270       base::StringToDouble(last_update_pref, &last_update_value);
    271       int64 ms_until_update = cache_update_delay_ -
    272           static_cast<int64>((base::Time::Now() - base::Time::FromDoubleT(
    273           last_update_value)).InMilliseconds());
    274       delay = ms_until_update > cache_update_delay_ ?
    275           cache_update_delay_ : (ms_until_update < start_fetch_delay_ ?
    276                                 start_fetch_delay_ : ms_until_update);
    277     }
    278   }
    279   // Start fetch and wait for UpdateResourceCache.
    280   web_resource_fetcher_->StartAfterDelay(delay);
    281 }
    282 
    283 void WebResourceService::UpdateResourceCache(const std::string& json_data) {
    284   UnpackerClient* client = new UnpackerClient(this, json_data);
    285   client->Start();
    286 
    287   // Set cache update time in preferences.
    288   prefs_->SetString(last_update_time_pref_name_,
    289       base::DoubleToString(base::Time::Now().ToDoubleT()));
    290 }
    291