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