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