Home | History | Annotate | Download | only in WebCoreSupport
      1 /*
      2  * Copyright 2010, The Android Open Source Project
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  *  * Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  *  * Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "WebRequest.h"
     28 
     29 #include "JNIUtility.h"
     30 #include "MainThread.h"
     31 #include "UrlInterceptResponse.h"
     32 #include "WebCoreFrameBridge.h"
     33 #include "WebCoreJni.h"
     34 #include "WebRequestContext.h"
     35 #include "WebResourceRequest.h"
     36 #include "WebUrlLoaderClient.h"
     37 #include "jni.h"
     38 
     39 #include <cutils/log.h>
     40 #include <openssl/x509.h>
     41 #include <string>
     42 #include <androidfw/AssetManager.h>
     43 
     44 extern android::AssetManager* globalAssetManager();
     45 
     46 // TODO:
     47 // - Finish the file upload. Testcase is mobile buzz
     48 // - Add network throttle needed by Android plugins
     49 
     50 // TODO: Turn off asserts crashing before release
     51 // http://b/issue?id=2951985
     52 #undef ASSERT
     53 #define ASSERT(assertion, ...) do \
     54     if (!(assertion)) { \
     55         android_printLog(ANDROID_LOG_ERROR, __FILE__, __VA_ARGS__); \
     56     } \
     57 while (0)
     58 
     59 namespace android {
     60 
     61 namespace {
     62 const int kInitialReadBufSize = 32768;
     63 const char* kXRequestedWithHeader = "X-Requested-With";
     64 
     65 struct RequestPackageName {
     66     std::string value;
     67     RequestPackageName();
     68 };
     69 
     70 RequestPackageName::RequestPackageName() {
     71     JNIEnv* env = JSC::Bindings::getJNIEnv();
     72     jclass bridgeClass = env->FindClass("android/webkit/JniUtil");
     73     jmethodID method = env->GetStaticMethodID(bridgeClass, "getPackageName", "()Ljava/lang/String;");
     74     value = jstringToStdString(env, static_cast<jstring>(env->CallStaticObjectMethod(bridgeClass, method)));
     75     env->DeleteLocalRef(bridgeClass);
     76 }
     77 
     78 base::LazyInstance<RequestPackageName> s_packageName(base::LINKER_INITIALIZED);
     79 
     80 }
     81 
     82 WebRequest::WebRequest(WebUrlLoaderClient* loader, const WebResourceRequest& webResourceRequest)
     83     : m_urlLoader(loader)
     84     , m_url(webResourceRequest.url())
     85     , m_userAgent(webResourceRequest.userAgent())
     86     , m_referer(webResourceRequest.referrer())
     87     , m_loadState(Created)
     88     , m_authRequestCount(0)
     89     , m_cacheMode(0)
     90     , m_runnableFactory(this)
     91     , m_wantToPause(false)
     92     , m_isPaused(false)
     93     , m_isSync(false)
     94 {
     95     GURL gurl(m_url);
     96 
     97     m_request = new net::URLRequest(gurl, this);
     98 
     99     m_request->SetExtraRequestHeaders(webResourceRequest.requestHeaders());
    100     m_request->SetExtraRequestHeaderByName(kXRequestedWithHeader, s_packageName.Get().value, false);
    101     m_request->set_referrer(webResourceRequest.referrer());
    102     m_request->set_method(webResourceRequest.method());
    103     m_request->set_load_flags(webResourceRequest.loadFlags());
    104 }
    105 
    106 // This is a special URL for Android. Query the Java InputStream
    107 // for data and send to WebCore
    108 WebRequest::WebRequest(WebUrlLoaderClient* loader, const WebResourceRequest& webResourceRequest, UrlInterceptResponse* intercept)
    109     : m_urlLoader(loader)
    110     , m_interceptResponse(intercept)
    111     , m_url(webResourceRequest.url())
    112     , m_userAgent(webResourceRequest.userAgent())
    113     , m_referer(webResourceRequest.referrer())
    114     , m_loadState(Created)
    115     , m_authRequestCount(0)
    116     , m_cacheMode(0)
    117     , m_runnableFactory(this)
    118     , m_wantToPause(false)
    119     , m_isPaused(false)
    120     , m_isSync(false)
    121 {
    122 }
    123 
    124 WebRequest::~WebRequest()
    125 {
    126     ASSERT(m_loadState == Finished, "dtor called on a WebRequest in a different state than finished (%d)", m_loadState);
    127 
    128     m_loadState = Deleted;
    129 }
    130 
    131 const std::string& WebRequest::getUrl() const
    132 {
    133     return m_url;
    134 }
    135 
    136 const std::string& WebRequest::getUserAgent() const
    137 {
    138     return m_userAgent;
    139 }
    140 
    141 const std::string& WebRequest::getReferer() const
    142 {
    143     return m_referer;
    144 }
    145 
    146 #ifdef LOG_REQUESTS
    147 namespace {
    148 int remaining = 0;
    149 }
    150 #endif
    151 
    152 void WebRequest::finish(bool success)
    153 {
    154     m_runnableFactory.RevokeAll();
    155     ASSERT(m_loadState < Finished, "(%p) called finish on an already finished WebRequest (%d) (%s)", this, m_loadState, m_url.c_str());
    156     if (m_loadState >= Finished)
    157         return;
    158 #ifdef LOG_REQUESTS
    159     time_t finish;
    160     time(&finish);
    161     finish = finish - m_startTime;
    162     struct tm * timeinfo;
    163     char buffer[80];
    164     timeinfo = localtime(&finish);
    165     strftime(buffer, 80, "Time: %M:%S",timeinfo);
    166     android_printLog(ANDROID_LOG_DEBUG, "KM", "(%p) finish (%d) (%s) (%d) (%s)", this, --remaining, buffer, success, m_url.c_str());
    167 #endif
    168 
    169     // Make sure WebUrlLoaderClient doesn't delete us in the middle of this method.
    170     scoped_refptr<WebRequest> guard(this);
    171 
    172     m_loadState = Finished;
    173     if (success) {
    174         m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(
    175                 m_urlLoader.get(), &WebUrlLoaderClient::didFinishLoading));
    176     } else {
    177         if (m_interceptResponse == NULL) {
    178             OwnPtr<WebResponse> webResponse(new WebResponse(m_request.get()));
    179             m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(
    180                     m_urlLoader.get(), &WebUrlLoaderClient::didFail, webResponse.release()));
    181         } else {
    182             OwnPtr<WebResponse> webResponse(new WebResponse(m_url, m_interceptResponse->mimeType(), 0,
    183                     m_interceptResponse->encoding(), m_interceptResponse->status()));
    184             m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(
    185                     m_urlLoader.get(), &WebUrlLoaderClient::didFail, webResponse.release()));
    186         }
    187     }
    188     m_networkBuffer = 0;
    189     m_request = 0;
    190     m_urlLoader = 0;
    191 }
    192 
    193 void WebRequest::appendFileToUpload(const std::string& filename)
    194 {
    195     // AppendFileToUpload is only valid before calling start
    196     ASSERT(m_loadState == Created, "appendFileToUpload called on a WebRequest not in CREATED state: (%s)", m_url.c_str());
    197     FilePath filePath(filename);
    198     m_request->AppendFileToUpload(filePath);
    199 }
    200 
    201 void WebRequest::appendBytesToUpload(WTF::Vector<char>* data)
    202 {
    203     // AppendBytesToUpload is only valid before calling start
    204     ASSERT(m_loadState == Created, "appendBytesToUpload called on a WebRequest not in CREATED state: (%s)", m_url.c_str());
    205     m_request->AppendBytesToUpload(data->data(), data->size());
    206     delete data;
    207 }
    208 
    209 void WebRequest::setRequestContext(WebRequestContext* context)
    210 {
    211     m_cacheMode = context->getCacheMode();
    212     if (m_request)
    213         m_request->set_context(context);
    214 }
    215 
    216 void WebRequest::updateLoadFlags(int& loadFlags)
    217 {
    218     if (m_cacheMode == 1) { // LOAD_CACHE_ELSE_NETWORK
    219         loadFlags |= net::LOAD_PREFERRING_CACHE;
    220         loadFlags &= ~net::LOAD_VALIDATE_CACHE;
    221     }
    222     if (m_cacheMode == 2) // LOAD_NO_CACHE
    223         loadFlags |= net::LOAD_BYPASS_CACHE;
    224     if (m_cacheMode == 3) // LOAD_CACHE_ONLY
    225         loadFlags |= net::LOAD_ONLY_FROM_CACHE;
    226 
    227     if (m_isSync)
    228         loadFlags |= net::LOAD_IGNORE_LIMITS;
    229 }
    230 
    231 void WebRequest::start()
    232 {
    233     ASSERT(m_loadState == Created, "Start called on a WebRequest not in CREATED state: (%s)", m_url.c_str());
    234 #ifdef LOG_REQUESTS
    235     android_printLog(ANDROID_LOG_DEBUG, "KM", "(%p) start (%d) (%s)", this, ++remaining, m_url.c_str());
    236     time(&m_startTime);
    237 #endif
    238 
    239     m_loadState = Started;
    240 
    241     if (m_interceptResponse != NULL)
    242         return handleInterceptedURL();
    243 
    244     // Handle data urls before we send it off to the http stack
    245     if (m_request->url().SchemeIs("data"))
    246         return handleDataURL(m_request->url());
    247 
    248     if (m_request->url().SchemeIs("browser"))
    249         return handleBrowserURL(m_request->url());
    250 
    251     // Update load flags with settings from WebSettings
    252     int loadFlags = m_request->load_flags();
    253     updateLoadFlags(loadFlags);
    254     m_request->set_load_flags(loadFlags);
    255 
    256     m_request->Start();
    257 }
    258 
    259 void WebRequest::cancel()
    260 {
    261     ASSERT(m_loadState >= Started, "Cancel called on a not started WebRequest: (%s)", m_url.c_str());
    262     ASSERT(m_loadState != Cancelled, "Cancel called on an already cancelled WebRequest: (%s)", m_url.c_str());
    263 
    264     // There is a possible race condition between the IO thread finishing the request and
    265     // the WebCore thread cancelling it. If the request has already finished, do
    266     // nothing to avoid sending duplicate finish messages to WebCore.
    267     if (m_loadState > Cancelled) {
    268         return;
    269     }
    270     ASSERT(m_request, "Request set to 0 before it is finished");
    271 
    272     m_loadState = Cancelled;
    273 
    274     m_request->Cancel();
    275     finish(true);
    276 }
    277 
    278 void WebRequest::pauseLoad(bool pause)
    279 {
    280     ASSERT(m_loadState >= GotData, "PauseLoad in state other than RESPONSE and GOTDATA");
    281     if (pause) {
    282         if (!m_isPaused)
    283             m_wantToPause = true;
    284     } else {
    285         m_wantToPause = false;
    286         if (m_isPaused) {
    287             m_isPaused = false;
    288             MessageLoop::current()->PostTask(FROM_HERE, m_runnableFactory.NewRunnableMethod(&WebRequest::startReading));
    289         }
    290     }
    291 }
    292 
    293 void WebRequest::handleInterceptedURL()
    294 {
    295     m_loadState = Response;
    296 
    297     const std::string& mime = m_interceptResponse->mimeType();
    298     // Get the MIME type from the URL. "text/html" is a last resort, hopefully overridden.
    299     std::string mimeType("text/html");
    300     if (mime == "") {
    301         // Get the MIME type from the file extension, if any.
    302         FilePath path(m_url);
    303         net::GetMimeTypeFromFile(path, &mimeType);
    304     } else {
    305         // Set from the intercept response.
    306         mimeType = mime;
    307     }
    308 
    309 
    310     OwnPtr<WebResponse> webResponse(new WebResponse(m_url, mimeType, 0, m_interceptResponse->encoding(), m_interceptResponse->status()));
    311     m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(
    312             m_urlLoader.get(), &WebUrlLoaderClient::didReceiveResponse, webResponse.release()));
    313 
    314     do {
    315         // data is deleted in WebUrlLoaderClient::didReceiveAndroidFileData
    316         // data is sent to the webcore thread
    317         OwnPtr<std::vector<char> > data(new std::vector<char>);
    318         data->reserve(kInitialReadBufSize);
    319 
    320         // Read returns false on error and size of 0 on eof.
    321         if (!m_interceptResponse->readStream(data.get()) || data->size() == 0)
    322             break;
    323 
    324         m_loadState = GotData;
    325         m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(
    326                 m_urlLoader.get(), &WebUrlLoaderClient::didReceiveAndroidFileData, data.release()));
    327     } while (true);
    328 
    329     finish(m_interceptResponse->status() == 200);
    330 }
    331 
    332 void WebRequest::handleDataURL(GURL url)
    333 {
    334     OwnPtr<std::string> data(new std::string);
    335     std::string mimeType;
    336     std::string charset;
    337 
    338     if (net::DataURL::Parse(url, &mimeType, &charset, data.get())) {
    339         // PopulateURLResponse from chrome implementation
    340         // weburlloader_impl.cc
    341         m_loadState = Response;
    342         OwnPtr<WebResponse> webResponse(new WebResponse(url.spec(), mimeType, data->size(), charset, 200));
    343         m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(
    344                 m_urlLoader.get(), &WebUrlLoaderClient::didReceiveResponse, webResponse.release()));
    345 
    346         if (!data->empty()) {
    347             m_loadState = GotData;
    348             m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(
    349                     m_urlLoader.get(), &WebUrlLoaderClient::didReceiveDataUrl, data.release()));
    350         }
    351     } else {
    352         // handle the failed case
    353     }
    354 
    355     finish(true);
    356 }
    357 
    358 void WebRequest::handleBrowserURL(GURL url)
    359 {
    360     std::string data("data:text/html;charset=utf-8,");
    361     if (url.spec() == "browser:incognito") {
    362         AssetManager* assetManager = globalAssetManager();
    363         Asset* asset = assetManager->open("webkit/incognito_mode_start_page.html", Asset::ACCESS_BUFFER);
    364         if (asset) {
    365             data.append((const char*)asset->getBuffer(false), asset->getLength());
    366             delete asset;
    367         }
    368     }
    369     GURL dataURL(data.c_str());
    370     handleDataURL(dataURL);
    371 }
    372 
    373 // Called upon a server-initiated redirect.  The delegate may call the
    374 // request's Cancel method to prevent the redirect from being followed.
    375 // Since there may be multiple chained redirects, there may also be more
    376 // than one redirect call.
    377 //
    378 // When this function is called, the request will still contain the
    379 // original URL, the destination of the redirect is provided in 'new_url'.
    380 // If the delegate does not cancel the request and |*defer_redirect| is
    381 // false, then the redirect will be followed, and the request's URL will be
    382 // changed to the new URL.  Otherwise if the delegate does not cancel the
    383 // request and |*defer_redirect| is true, then the redirect will be
    384 // followed once FollowDeferredRedirect is called on the URLRequest.
    385 //
    386 // The caller must set |*defer_redirect| to false, so that delegates do not
    387 // need to set it if they are happy with the default behavior of not
    388 // deferring redirect.
    389 void WebRequest::OnReceivedRedirect(net::URLRequest* newRequest, const GURL& newUrl, bool* deferRedirect)
    390 {
    391     ASSERT(m_loadState < Response, "Redirect after receiving response");
    392     ASSERT(newRequest && newRequest->status().is_success(), "Invalid redirect");
    393 
    394     OwnPtr<WebResponse> webResponse(new WebResponse(newRequest));
    395     webResponse->setUrl(newUrl.spec());
    396     m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(
    397             m_urlLoader.get(), &WebUrlLoaderClient::willSendRequest, webResponse.release()));
    398 
    399     // Defer the redirect until followDeferredRedirect() is called.
    400     *deferRedirect = true;
    401 }
    402 
    403 // Called when we receive an authentication failure.  The delegate should
    404 // call request->SetAuth() with the user's credentials once it obtains them,
    405 // or request->CancelAuth() to cancel the login and display the error page.
    406 // When it does so, the request will be reissued, restarting the sequence
    407 // of On* callbacks.
    408 void WebRequest::OnAuthRequired(net::URLRequest* request, net::AuthChallengeInfo* authInfo)
    409 {
    410     ASSERT(m_loadState == Started, "OnAuthRequired called on a WebRequest not in STARTED state (state=%d)", m_loadState);
    411 
    412     scoped_refptr<net::AuthChallengeInfo> authInfoPtr(authInfo);
    413     bool firstTime = (m_authRequestCount == 0);
    414     ++m_authRequestCount;
    415 
    416     bool suppressDialog = (request->load_flags() & net::LOAD_DO_NOT_PROMPT_FOR_LOGIN);
    417 
    418     m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(
    419             m_urlLoader.get(), &WebUrlLoaderClient::authRequired, authInfoPtr, firstTime, suppressDialog));
    420 }
    421 
    422 // Called when we received an SSL certificate error. The delegate will provide
    423 // the user the options to proceed, cancel, or view certificates.
    424 void WebRequest::OnSSLCertificateError(net::URLRequest* request, int cert_error, net::X509Certificate* cert)
    425 {
    426     scoped_refptr<net::X509Certificate> scoped_cert = cert;
    427     m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(
    428             m_urlLoader.get(), &WebUrlLoaderClient::reportSslCertError, cert_error, scoped_cert));
    429 }
    430 
    431 void WebRequest::OnCertificateRequested(net::URLRequest* request, net::SSLCertRequestInfo* cert_request_info)
    432 {
    433     scoped_refptr<net::SSLCertRequestInfo> scoped_cert_request_info = cert_request_info;
    434     m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(
    435             m_urlLoader.get(), &WebUrlLoaderClient::requestClientCert, scoped_cert_request_info));
    436 }
    437 
    438 
    439 // After calling Start(), the delegate will receive an OnResponseStarted
    440 // callback when the request has completed.  If an error occurred, the
    441 // request->status() will be set.  On success, all redirects have been
    442 // followed and the final response is beginning to arrive.  At this point,
    443 // meta data about the response is available, including for example HTTP
    444 // response headers if this is a request for a HTTP resource.
    445 void WebRequest::OnResponseStarted(net::URLRequest* request)
    446 {
    447     ASSERT(m_loadState == Started, "Got response after receiving response");
    448 
    449     m_loadState = Response;
    450     if (request && request->status().is_success()) {
    451         OwnPtr<WebResponse> webResponse(new WebResponse(request));
    452         m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(
    453                 m_urlLoader.get(), &WebUrlLoaderClient::didReceiveResponse, webResponse.release()));
    454 
    455         // Start reading the response
    456         startReading();
    457     } else {
    458         finish(false);
    459     }
    460 }
    461 
    462 void WebRequest::setAuth(const string16& username, const string16& password)
    463 {
    464     ASSERT(m_loadState == Started, "setAuth called on a WebRequest not in STARTED state (state=%d)", m_loadState);
    465 
    466     m_request->SetAuth(username, password);
    467 }
    468 
    469 void WebRequest::cancelAuth()
    470 {
    471     ASSERT(m_loadState == Started, "cancelAuth called on a WebRequest not in STARTED state (state=%d)", m_loadState);
    472 
    473     m_request->CancelAuth();
    474 }
    475 
    476 void WebRequest::followDeferredRedirect()
    477 {
    478     ASSERT(m_loadState < Response, "Redirect after receiving response");
    479 
    480     m_request->FollowDeferredRedirect();
    481 }
    482 
    483 void WebRequest::proceedSslCertError()
    484 {
    485     m_request->ContinueDespiteLastError();
    486 }
    487 
    488 void WebRequest::cancelSslCertError(int cert_error)
    489 {
    490     m_request->SimulateError(cert_error);
    491 }
    492 
    493 void WebRequest::sslClientCert(EVP_PKEY* pkey, scoped_refptr<net::X509Certificate> chain)
    494 {
    495     base::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> privateKey(pkey);
    496     if (privateKey.get() == NULL || chain.get() == NULL) {
    497         m_request->ContinueWithCertificate(NULL);
    498         return;
    499     }
    500     GURL gurl(m_url);
    501     net::OpenSSLPrivateKeyStore::GetInstance()->StorePrivateKey(gurl, privateKey.release());
    502     m_request->ContinueWithCertificate(chain.release());
    503 }
    504 
    505 void WebRequest::startReading()
    506 {
    507     ASSERT(m_networkBuffer == 0, "startReading called with a nonzero buffer");
    508     ASSERT(m_isPaused == 0, "startReading called in paused state");
    509     ASSERT(m_loadState == Response || m_loadState == GotData, "StartReading in state other than RESPONSE and GOTDATA");
    510     if (m_loadState > GotData) // We have been cancelled between reads
    511         return;
    512 
    513     if (m_wantToPause) {
    514         m_isPaused = true;
    515         return;
    516     }
    517 
    518     int bytesRead = 0;
    519 
    520     if (!read(&bytesRead)) {
    521         if (m_request && m_request->status().is_io_pending())
    522             return; // Wait for OnReadCompleted()
    523         return finish(false);
    524     }
    525 
    526     // bytesRead == 0 indicates finished
    527     if (!bytesRead)
    528         return finish(true);
    529 
    530     m_loadState = GotData;
    531     // Read ok, forward buffer to webcore
    532     m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(m_urlLoader.get(), &WebUrlLoaderClient::didReceiveData, m_networkBuffer, bytesRead));
    533     m_networkBuffer = 0;
    534     MessageLoop::current()->PostTask(FROM_HERE, m_runnableFactory.NewRunnableMethod(&WebRequest::startReading));
    535 }
    536 
    537 bool WebRequest::read(int* bytesRead)
    538 {
    539     ASSERT(m_loadState == Response || m_loadState == GotData, "read in state other than RESPONSE and GOTDATA");
    540     ASSERT(m_networkBuffer == 0, "Read called with a nonzero buffer");
    541 
    542     // TODO: when asserts work, check that the buffer is 0 here
    543     m_networkBuffer = new net::IOBuffer(kInitialReadBufSize);
    544     return m_request->Read(m_networkBuffer, kInitialReadBufSize, bytesRead);
    545 }
    546 
    547 // This is called when there is data available
    548 
    549 // Called when the a Read of the response body is completed after an
    550 // IO_PENDING status from a Read() call.
    551 // The data read is filled into the buffer which the caller passed
    552 // to Read() previously.
    553 //
    554 // If an error occurred, request->status() will contain the error,
    555 // and bytes read will be -1.
    556 void WebRequest::OnReadCompleted(net::URLRequest* request, int bytesRead)
    557 {
    558     ASSERT(m_loadState == Response || m_loadState == GotData, "OnReadCompleted in state other than RESPONSE and GOTDATA");
    559 
    560     if (request->status().is_success()) {
    561         m_loadState = GotData;
    562         m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(
    563                 m_urlLoader.get(), &WebUrlLoaderClient::didReceiveData, m_networkBuffer, bytesRead));
    564         m_networkBuffer = 0;
    565 
    566         // Get the rest of the data
    567         startReading();
    568     } else {
    569         finish(false);
    570     }
    571 }
    572 
    573 } // namespace android
    574