Home | History | Annotate | Download | only in serviceworkers
      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 "config.h"
      6 #include "modules/serviceworkers/Body.h"
      7 
      8 #include "bindings/core/v8/ScriptPromiseResolver.h"
      9 #include "bindings/core/v8/ScriptState.h"
     10 #include "bindings/core/v8/V8ThrowException.h"
     11 #include "core/fileapi/Blob.h"
     12 #include "core/fileapi/FileReaderLoader.h"
     13 #include "core/fileapi/FileReaderLoaderClient.h"
     14 
     15 namespace blink {
     16 
     17 ScriptPromise Body::readAsync(ScriptState* scriptState, ResponseType type)
     18 {
     19     if (m_bodyUsed)
     20         return ScriptPromise::reject(scriptState, V8ThrowException::createTypeError("Already read", scriptState->isolate()));
     21 
     22     // When the main thread sends a V8::TerminateExecution() signal to a worker
     23     // thread, any V8 API on the worker thread starts returning an empty
     24     // handle. This can happen in Body::readAsync. To avoid the situation, we
     25     // first check the ExecutionContext and return immediately if it's already
     26     // gone (which means that the V8::TerminateExecution() signal has been sent
     27     // to this worker thread).
     28     ExecutionContext* executionContext = scriptState->executionContext();
     29     if (!executionContext)
     30         return ScriptPromise();
     31 
     32     m_bodyUsed = true;
     33     m_responseType = type;
     34 
     35     ASSERT(!m_resolver);
     36     m_resolver = ScriptPromiseResolver::create(scriptState);
     37     ScriptPromise promise = m_resolver->promise();
     38 
     39     FileReaderLoader::ReadType readType = FileReaderLoader::ReadAsText;
     40     RefPtr<BlobDataHandle> blobHandle = blobDataHandle();
     41     if (!blobHandle.get()) {
     42         blobHandle = BlobDataHandle::create(BlobData::create(), 0);
     43     }
     44     switch (type) {
     45     case ResponseAsArrayBuffer:
     46         readType = FileReaderLoader::ReadAsArrayBuffer;
     47         break;
     48     case ResponseAsBlob:
     49         if (blobHandle->size() != kuint64max) {
     50             // If the size of |blobHandle| is set correctly, creates Blob from
     51             // it.
     52             m_resolver->resolve(Blob::create(blobHandle));
     53             m_resolver.clear();
     54             return promise;
     55         }
     56         // If the size is not set, read as ArrayBuffer and create a new blob to
     57         // get the size.
     58         // FIXME: This workaround is not good for performance.
     59         // When we will stop using Blob as a base system of Body to support
     60         // stream, this problem should be solved.
     61         readType = FileReaderLoader::ReadAsArrayBuffer;
     62         break;
     63     case ResponseAsFormData:
     64         // FIXME: Implement this.
     65         ASSERT_NOT_REACHED();
     66         break;
     67     case ResponseAsJSON:
     68     case ResponseAsText:
     69         break;
     70     default:
     71         ASSERT_NOT_REACHED();
     72     }
     73 
     74     m_loader = adoptPtr(new FileReaderLoader(readType, this));
     75     m_loader->start(executionContext, blobHandle);
     76 
     77     return promise;
     78 }
     79 
     80 ScriptPromise Body::arrayBuffer(ScriptState* scriptState)
     81 {
     82     return readAsync(scriptState, ResponseAsArrayBuffer);
     83 }
     84 
     85 ScriptPromise Body::blob(ScriptState* scriptState)
     86 {
     87     return readAsync(scriptState, ResponseAsBlob);
     88 }
     89 
     90 ScriptPromise Body::formData(ScriptState* scriptState)
     91 {
     92     return readAsync(scriptState, ResponseAsFormData);
     93 }
     94 
     95 ScriptPromise Body::json(ScriptState* scriptState)
     96 {
     97     return readAsync(scriptState, ResponseAsJSON);
     98 }
     99 
    100 ScriptPromise Body::text(ScriptState* scriptState)
    101 {
    102     return readAsync(scriptState, ResponseAsText);
    103 }
    104 
    105 bool Body::bodyUsed() const
    106 {
    107     return m_bodyUsed;
    108 }
    109 
    110 void Body::setBodyUsed()
    111 {
    112     m_bodyUsed = true;
    113 }
    114 
    115 void Body::stop()
    116 {
    117     // Canceling the load will call didFail which will remove the resolver.
    118     if (m_resolver)
    119         m_loader->cancel();
    120 }
    121 
    122 bool Body::hasPendingActivity() const
    123 {
    124     return m_resolver;
    125 }
    126 
    127 Body::Body(ExecutionContext* context)
    128     : ActiveDOMObject(context)
    129     , m_bodyUsed(false)
    130     , m_responseType(ResponseType::ResponseUnknown)
    131 {
    132 }
    133 
    134 Body::Body(const Body& copy_from)
    135     : ActiveDOMObject(copy_from.lifecycleContext())
    136     , m_bodyUsed(copy_from.bodyUsed())
    137     , m_responseType(ResponseType::ResponseUnknown)
    138 {
    139 }
    140 
    141 void Body::resolveJSON()
    142 {
    143     ASSERT(m_responseType == ResponseAsJSON);
    144     ScriptState::Scope scope(m_resolver->scriptState());
    145     v8::Isolate* isolate = m_resolver->scriptState()->isolate();
    146     v8::Local<v8::String> inputString = v8String(isolate, m_loader->stringResult());
    147     v8::TryCatch trycatch;
    148     v8::Local<v8::Value> parsed = v8::JSON::Parse(inputString);
    149     if (parsed.IsEmpty()) {
    150         if (trycatch.HasCaught())
    151             m_resolver->reject(trycatch.Exception());
    152         else
    153             m_resolver->reject(v8::Exception::Error(v8::String::NewFromUtf8(isolate, "JSON parse error")));
    154         return;
    155     }
    156     m_resolver->resolve(parsed);
    157 }
    158 
    159 // FileReaderLoaderClient functions.
    160 void Body::didStartLoading() { }
    161 void Body::didReceiveData() { }
    162 void Body::didFinishLoading()
    163 {
    164     if (!m_resolver->executionContext() || m_resolver->executionContext()->activeDOMObjectsAreStopped())
    165         return;
    166 
    167     switch (m_responseType) {
    168     case ResponseAsArrayBuffer:
    169         m_resolver->resolve(m_loader->arrayBufferResult());
    170         break;
    171     case ResponseAsBlob: {
    172         ASSERT(blobDataHandle()->size() == kuint64max);
    173         OwnPtr<BlobData> blobData = BlobData::create();
    174         RefPtr<ArrayBuffer> buffer = m_loader->arrayBufferResult();
    175         blobData->appendArrayBuffer(buffer.get());
    176         const size_t length = blobData->length();
    177         m_resolver->resolve(Blob::create(BlobDataHandle::create(blobData.release(), length)));
    178         break;
    179     }
    180     case ResponseAsFormData:
    181         ASSERT_NOT_REACHED();
    182         break;
    183     case ResponseAsJSON:
    184         resolveJSON();
    185         break;
    186     case ResponseAsText:
    187         m_resolver->resolve(m_loader->stringResult());
    188         break;
    189     default:
    190         ASSERT_NOT_REACHED();
    191     }
    192     m_resolver.clear();
    193 }
    194 
    195 void Body::didFail(FileError::ErrorCode code)
    196 {
    197     ASSERT(m_resolver);
    198     if (!m_resolver->executionContext() || m_resolver->executionContext()->activeDOMObjectsAreStopped())
    199         return;
    200 
    201     m_resolver->resolve("");
    202     m_resolver.clear();
    203 }
    204 
    205 } // namespace blink
    206