Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / net / base / sdch_dictionary_fetcher.cc
index aa2061e..8d08b9a 100644 (file)
@@ -4,25 +4,36 @@
 
 #include "net/base/sdch_dictionary_fetcher.h"
 
+#include <stdint.h>
+
+#include "base/auto_reset.h"
 #include "base/bind.h"
 #include "base/compiler_specific.h"
-#include "base/message_loop/message_loop.h"
+#include "base/thread_task_runner_handle.h"
 #include "net/base/load_flags.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_context_getter.h"
+#include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_status.h"
+#include "net/url_request/url_request_throttler_manager.h"
+
+namespace {
+
+const int kBufferSize = 4096;
+
+}  // namespace
 
 namespace net {
 
 SdchDictionaryFetcher::SdchDictionaryFetcher(
-    SdchManager* manager,
-    URLRequestContextGetter* context)
-    : manager_(manager),
-      weak_factory_(this),
-      task_is_pending_(false),
-      context_(context) {
+    SdchFetcher::Delegate* consumer,
+    URLRequestContext* context)
+    : next_state_(STATE_NONE),
+      in_loop_(false),
+      consumer_(consumer),
+      context_(context),
+      weak_factory_(this) {
   DCHECK(CalledOnValidThread());
-  DCHECK(manager);
+  DCHECK(consumer);
+  DCHECK(context);
 }
 
 SdchDictionaryFetcher::~SdchDictionaryFetcher() {
@@ -32,7 +43,7 @@ SdchDictionaryFetcher::~SdchDictionaryFetcher() {
 void SdchDictionaryFetcher::Schedule(const GURL& dictionary_url) {
   DCHECK(CalledOnValidThread());
 
-  // Avoid pushing duplicate copy onto queue.  We may fetch this url again later
+  // Avoid pushing duplicate copy onto queue. We may fetch this url again later
   // and get a different dictionary, but there is no reason to have it in the
   // queue twice at one time.
   if (!fetch_queue_.empty() && fetch_queue_.back() == dictionary_url) {
@@ -47,59 +58,187 @@ void SdchDictionaryFetcher::Schedule(const GURL& dictionary_url) {
   }
   attempted_load_.insert(dictionary_url);
   fetch_queue_.push(dictionary_url);
-  ScheduleDelayedRun();
+
+  next_state_ = STATE_IDLE;
+
+  // There are no callbacks to user code from the dictionary fetcher,
+  // and Schedule() is only called from user code, so this call to DoLoop()
+  // does not require an |if (in_loop_) return;| guard.
+  DoLoop(OK);
 }
 
 void SdchDictionaryFetcher::Cancel() {
   DCHECK(CalledOnValidThread());
 
+  next_state_ = STATE_NONE;
+
   while (!fetch_queue_.empty())
     fetch_queue_.pop();
   attempted_load_.clear();
   weak_factory_.InvalidateWeakPtrs();
-  current_fetch_.reset(NULL);
+  current_request_.reset(NULL);
+  buffer_ = NULL;
+  dictionary_.clear();
 }
 
-void SdchDictionaryFetcher::ScheduleDelayedRun() {
-  if (fetch_queue_.empty() || current_fetch_.get() || task_is_pending_)
+void SdchDictionaryFetcher::OnResponseStarted(URLRequest* request) {
+  DCHECK(CalledOnValidThread());
+  DCHECK_EQ(request, current_request_.get());
+  DCHECK_EQ(next_state_, STATE_REQUEST_STARTED);
+
+  // The response has started, so the stream can be read from.
+  next_state_ = STATE_REQUEST_READING;
+
+  // If this function was synchronously called, the containing
+  // state machine loop will handle the state transition. Otherwise,
+  // restart the state machine loop.
+  if (in_loop_)
     return;
-  base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
-      base::Bind(&SdchDictionaryFetcher::StartFetching,
-                 weak_factory_.GetWeakPtr()),
-      base::TimeDelta::FromMilliseconds(kMsDelayFromRequestTillDownload));
-  task_is_pending_ = true;
+
+  DoLoop(request->status().error());
 }
 
-void SdchDictionaryFetcher::StartFetching() {
+void SdchDictionaryFetcher::OnReadCompleted(URLRequest* request,
+                                            int bytes_read) {
   DCHECK(CalledOnValidThread());
-  DCHECK(task_is_pending_);
-  task_is_pending_ = false;
+  DCHECK_EQ(request, current_request_.get());
+  DCHECK_EQ(next_state_, STATE_REQUEST_READING);
+
+  // No state transition is required in this function; the
+  // completion of the request is detected in DoRead().
 
-  // Handle losing the race against Cancel().
-  if (fetch_queue_.empty())
+  if (request->status().is_success())
+    dictionary_.append(buffer_->data(), bytes_read);
+
+  // If this function was synchronously called, the containing
+  // state machine loop will handle the state transition. Otherwise,
+  // restart the state machine loop.
+  if (in_loop_)
     return;
 
-  DCHECK(context_.get());
-  current_fetch_.reset(URLFetcher::Create(
-      fetch_queue_.front(), URLFetcher::GET, this));
+  DoLoop(request->status().error());
+}
+
+int SdchDictionaryFetcher::DoLoop(int rv) {
+  DCHECK(!in_loop_);
+  base::AutoReset<bool> auto_reset_in_loop(&in_loop_, true);
+
+  do {
+    State state = next_state_;
+    next_state_ = STATE_NONE;
+    switch (state) {
+      case STATE_IDLE:
+        rv = DoDispatchRequest(rv);
+        break;
+      case STATE_REQUEST_STARTED:
+        rv = DoRequestStarted(rv);
+        break;
+      case STATE_REQUEST_READING:
+        rv = DoRead(rv);
+        break;
+      case STATE_REQUEST_COMPLETE:
+        rv = DoCompleteRequest(rv);
+        break;
+      case STATE_NONE:
+        NOTREACHED();
+    }
+  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
+
+  return rv;
+}
+
+int SdchDictionaryFetcher::DoDispatchRequest(int rv) {
+  DCHECK(CalledOnValidThread());
+
+  // |rv| is ignored, as the result from the previous request doesn't
+  // affect the next request.
+
+  if (fetch_queue_.empty() || current_request_.get()) {
+    next_state_ = STATE_NONE;
+    return OK;
+  }
+
+  current_request_ = context_->CreateRequest(
+      fetch_queue_.front(), IDLE, this, NULL);
+  current_request_->SetLoadFlags(LOAD_DO_NOT_SEND_COOKIES |
+                                 LOAD_DO_NOT_SAVE_COOKIES);
+  buffer_ = new IOBuffer(kBufferSize);
   fetch_queue_.pop();
-  current_fetch_->SetRequestContext(context_.get());
-  current_fetch_->SetLoadFlags(LOAD_DO_NOT_SEND_COOKIES |
-                               LOAD_DO_NOT_SAVE_COOKIES);
-  current_fetch_->Start();
+
+  next_state_ = STATE_REQUEST_STARTED;
+  current_request_->Start();
+
+  return OK;
 }
 
-void SdchDictionaryFetcher::OnURLFetchComplete(
-    const URLFetcher* source) {
+int SdchDictionaryFetcher::DoRequestStarted(int rv) {
   DCHECK(CalledOnValidThread());
-  if ((200 == source->GetResponseCode()) &&
-      (source->GetStatus().status() == URLRequestStatus::SUCCESS)) {
-    std::string data;
-    source->GetResponseAsString(&data);
-    manager_->AddSdchDictionary(data, source->GetURL());
+  DCHECK_EQ(rv, OK);  // Can only come straight from above function.
+
+  // The transition to STATE_REQUEST_READING occurs in the
+  // OnResponseStarted() callback triggered by URLRequest::Start()
+  // (called in DoDispatchRequest(), above). If that callback did not
+  // occur synchronously, this routine is executed; it returns ERR_IO_PENDING,
+  // indicating to the controlling loop that no further work should be done
+  // until the callback occurs (which will re-invoke DoLoop()).
+  next_state_ = STATE_REQUEST_STARTED;
+  return ERR_IO_PENDING;
+}
+
+int SdchDictionaryFetcher::DoRead(int rv) {
+  DCHECK(CalledOnValidThread());
+
+  // If there's been an error, abort the current request.
+  if (rv != OK) {
+    current_request_.reset();
+    buffer_ = NULL;
+    next_state_ = STATE_IDLE;
+
+    return OK;
+  }
+
+  next_state_ = STATE_REQUEST_READING;
+  int bytes_read = 0;
+  if (!current_request_->Read(buffer_.get(), kBufferSize, &bytes_read)) {
+    if (current_request_->status().is_io_pending())
+      return ERR_IO_PENDING;
+
+    if (current_request_->status().error() == OK) {
+      // This "should never happen", but if it does the result will be
+      // an infinite loop.  It's not clear how to handle a read failure
+      // without a promise to invoke the callback at some point in the future,
+      // so the request is failed.
+      SdchManager::SdchErrorRecovery(SdchManager::DICTIONARY_FETCH_READ_FAILED);
+      DLOG(FATAL) <<
+          "URLRequest::Read() returned false without IO pending or error!";
+      return ERR_FAILED;
+    }
+
+    return current_request_->status().error();
   }
-  current_fetch_.reset(NULL);
-  ScheduleDelayedRun();
+
+  if (bytes_read != 0)
+    dictionary_.append(buffer_->data(), bytes_read);
+  else
+    next_state_ = STATE_REQUEST_COMPLETE;
+
+  return OK;
+}
+
+int SdchDictionaryFetcher::DoCompleteRequest(int rv) {
+  DCHECK(CalledOnValidThread());
+
+  // If the dictionary was successfully fetched, add it to the manager.
+  if (rv == OK)
+    consumer_->AddSdchDictionary(dictionary_, current_request_->url());
+
+  current_request_.reset();
+  buffer_ = NULL;
+  dictionary_.clear();
+
+  next_state_ = STATE_IDLE;
+
+  return OK;
 }
 
 }  // namespace net