Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1687)

Side by Side Diff: third_party/WebKit/Source/modules/fetch/ReadableStreamDataConsumerHandle.cpp

Issue 1539803002: [Fetch API] Fix a memory leak with a Response constructed with a ReadableStream (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@response-constructed-with-stream
Patch Set: Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 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 "config.h" 5 #include "config.h"
6 #include "modules/fetch/ReadableStreamDataConsumerHandle.h" 6 #include "modules/fetch/ReadableStreamDataConsumerHandle.h"
7 7
8 #include "bindings/core/v8/ExceptionState.h" 8 #include "bindings/core/v8/ExceptionState.h"
9 #include "bindings/core/v8/ReadableStreamOperations.h" 9 #include "bindings/core/v8/ReadableStreamOperations.h"
10 #include "bindings/core/v8/ScopedPersistent.h"
10 #include "bindings/core/v8/ScriptFunction.h" 11 #include "bindings/core/v8/ScriptFunction.h"
11 #include "bindings/core/v8/ScriptState.h" 12 #include "bindings/core/v8/ScriptState.h"
12 #include "bindings/core/v8/ScriptValue.h" 13 #include "bindings/core/v8/ScriptValue.h"
13 #include "bindings/core/v8/V8BindingMacros.h" 14 #include "bindings/core/v8/V8BindingMacros.h"
14 #include "bindings/core/v8/V8IteratorResultValue.h" 15 #include "bindings/core/v8/V8IteratorResultValue.h"
15 #include "bindings/core/v8/V8RecursionScope.h" 16 #include "bindings/core/v8/V8RecursionScope.h"
16 #include "bindings/core/v8/V8Uint8Array.h" 17 #include "bindings/core/v8/V8Uint8Array.h"
17 #include "core/dom/DOMTypedArray.h" 18 #include "core/dom/DOMTypedArray.h"
18 #include "public/platform/Platform.h" 19 #include "public/platform/Platform.h"
19 #include "public/platform/WebTaskRunner.h" 20 #include "public/platform/WebTaskRunner.h"
20 #include "public/platform/WebThread.h" 21 #include "public/platform/WebThread.h"
21 #include "public/platform/WebTraceLocation.h" 22 #include "public/platform/WebTraceLocation.h"
22 #include "wtf/Assertions.h" 23 #include "wtf/Assertions.h"
23 #include "wtf/Functional.h" 24 #include "wtf/Functional.h"
24 #include "wtf/RefCounted.h" 25 #include "wtf/RefCounted.h"
25 #include "wtf/WeakPtr.h"
26 #include <algorithm> 26 #include <algorithm>
27 #include <string.h> 27 #include <string.h>
28 #include <v8.h> 28 #include <v8.h>
29 29
30 namespace blink { 30 namespace blink {
31 31
32 using Result = WebDataConsumerHandle::Result; 32 using Result = WebDataConsumerHandle::Result;
33 using Flags = WebDataConsumerHandle::Flags; 33 using Flags = WebDataConsumerHandle::Flags;
34 34
35 // This context is not yet thread-safe. 35 // This context is not yet thread-safe.
36 class ReadableStreamDataConsumerHandle::ReadingContext final : public RefCounted <ReadingContext> { 36 class ReadableStreamDataConsumerHandle::ReadingContext final : public RefCounted <ReadingContext> {
37 WTF_MAKE_NONCOPYABLE(ReadingContext); 37 WTF_MAKE_NONCOPYABLE(ReadingContext);
38 public: 38 public:
39 class OnFulfilled final : public ScriptFunction { 39 class OnFulfilled final : public ScriptFunction {
40 public: 40 public:
41 static v8::Local<v8::Function> createFunction(ScriptState* scriptState, WeakPtr<ReadingContext> context) 41 static v8::Local<v8::Function> createFunction(ScriptState* scriptState, PassRefPtr<ReadingContext> context)
42 { 42 {
43 return (new OnFulfilled(scriptState, context))->bindToV8Function(); 43 return (new OnFulfilled(scriptState, context))->bindToV8Function();
44 } 44 }
45 45
46 ScriptValue call(ScriptValue v) override 46 ScriptValue call(ScriptValue v) override
47 { 47 {
48 RefPtr<ReadingContext> readingContext(m_readingContext.get()); 48 RefPtr<ReadingContext> readingContext(m_readingContext);
49 if (!readingContext) 49 if (!readingContext)
50 return v; 50 return v;
51 bool done; 51 bool done;
52 v8::Local<v8::Value> item = v.v8Value(); 52 v8::Local<v8::Value> item = v.v8Value();
53 ASSERT(item->IsObject()); 53 ASSERT(item->IsObject());
54 v8::Local<v8::Value> value = v8CallOrCrash(v8UnpackIteratorResult(v. scriptState(), item.As<v8::Object>(), &done)); 54 v8::Local<v8::Value> value = v8CallOrCrash(v8UnpackIteratorResult(v. scriptState(), item.As<v8::Object>(), &done));
55 if (done) { 55 if (done) {
56 readingContext->onReadDone(); 56 readingContext->onReadDone();
57 return v; 57 return v;
58 } 58 }
59 if (!V8Uint8Array::hasInstance(value, v.isolate())) { 59 if (!V8Uint8Array::hasInstance(value, v.isolate())) {
60 readingContext->onRejected(); 60 readingContext->onRejected();
61 return ScriptValue(); 61 return ScriptValue();
62 } 62 }
63 readingContext->onRead(V8Uint8Array::toImpl(value.As<v8::Object>())) ; 63 readingContext->onRead(V8Uint8Array::toImpl(value.As<v8::Object>())) ;
64 return v; 64 return v;
65 } 65 }
66 66
67 private: 67 private:
68 OnFulfilled(ScriptState* scriptState, WeakPtr<ReadingContext> context) 68 OnFulfilled(ScriptState* scriptState, PassRefPtr<ReadingContext> context )
69 : ScriptFunction(scriptState), m_readingContext(context) {} 69 : ScriptFunction(scriptState), m_readingContext(context) {}
70 70
71 WeakPtr<ReadingContext> m_readingContext; 71 RefPtr<ReadingContext> m_readingContext;
72 }; 72 };
73 73
74 class OnRejected final : public ScriptFunction { 74 class OnRejected final : public ScriptFunction {
75 public: 75 public:
76 static v8::Local<v8::Function> createFunction(ScriptState* scriptState, WeakPtr<ReadingContext> context) 76 static v8::Local<v8::Function> createFunction(ScriptState* scriptState, PassRefPtr<ReadingContext> context)
77 { 77 {
78 return (new OnRejected(scriptState, context))->bindToV8Function(); 78 return (new OnRejected(scriptState, context))->bindToV8Function();
79 } 79 }
80 80
81 ScriptValue call(ScriptValue v) override 81 ScriptValue call(ScriptValue v) override
82 { 82 {
83 RefPtr<ReadingContext> readingContext(m_readingContext.get()); 83 RefPtr<ReadingContext> readingContext(m_readingContext);
84 if (!readingContext) 84 if (!readingContext)
85 return v; 85 return v;
86 readingContext->onRejected(); 86 readingContext->onRejected();
87 return v; 87 return v;
88 } 88 }
89 89
90 private: 90 private:
91 OnRejected(ScriptState* scriptState, WeakPtr<ReadingContext> context) 91 OnRejected(ScriptState* scriptState, PassRefPtr<ReadingContext> context)
92 : ScriptFunction(scriptState), m_readingContext(context) {} 92 : ScriptFunction(scriptState), m_readingContext(context) {}
93 93
94 WeakPtr<ReadingContext> m_readingContext; 94 RefPtr<ReadingContext> m_readingContext;
95 }; 95 };
96 96
97 class ReaderImpl final : public FetchDataConsumerHandle::Reader { 97 class ReaderImpl final : public FetchDataConsumerHandle::Reader {
98 public: 98 public:
99 ReaderImpl(PassRefPtr<ReadingContext> context, Client* client) 99 ReaderImpl(PassRefPtr<ReadingContext> context, Client* client)
100 : m_readingContext(context) 100 : m_readingContext(context)
101 { 101 {
102 m_readingContext->attachReader(client); 102 m_readingContext->attachReader(client);
103 } 103 }
104 ~ReaderImpl() override 104 ~ReaderImpl() override
(...skipping 21 matching lines...) Expand all
126 126
127 Result endRead(size_t readSize) override 127 Result endRead(size_t readSize) override
128 { 128 {
129 return m_readingContext->endRead(readSize); 129 return m_readingContext->endRead(readSize);
130 } 130 }
131 131
132 private: 132 private:
133 RefPtr<ReadingContext> m_readingContext; 133 RefPtr<ReadingContext> m_readingContext;
134 }; 134 };
135 135
136 static PassRefPtr<ReadingContext> create(ScriptState* scriptState, v8::Local <v8::Value> stream) 136 static PassRefPtr<ReadingContext> create(ScriptState* scriptState, v8::Local <v8::Object> stream)
tyoshino (SeeGerritForStatus) 2016/02/12 07:24:21 streamReader
tyoshino (SeeGerritForStatus) 2016/02/12 07:25:05 Sorry. It's already fixed in the latest ps. Please
yhirano 2016/02/12 07:33:54 Done.
137 { 137 {
138 return adoptRef(new ReadingContext(scriptState, stream)); 138 return adoptRef(new ReadingContext(scriptState, stream));
139 } 139 }
140 140
141 void attachReader(WebDataConsumerHandle::Client* client) 141 void attachReader(WebDataConsumerHandle::Client* client)
142 { 142 {
143 m_client = client; 143 m_client = client;
144 notifyLater(); 144 notifyLater();
145 } 145 }
146 146
(...skipping 10 matching lines...) Expand all
157 return WebDataConsumerHandle::UnexpectedError; 157 return WebDataConsumerHandle::UnexpectedError;
158 if (m_isDone) 158 if (m_isDone)
159 return WebDataConsumerHandle::Done; 159 return WebDataConsumerHandle::Done;
160 160
161 if (m_pendingBuffer) { 161 if (m_pendingBuffer) {
162 ASSERT(m_pendingOffset < m_pendingBuffer->length()); 162 ASSERT(m_pendingOffset < m_pendingBuffer->length());
163 *buffer = m_pendingBuffer->data() + m_pendingOffset; 163 *buffer = m_pendingBuffer->data() + m_pendingOffset;
164 *available = m_pendingBuffer->length() - m_pendingOffset; 164 *available = m_pendingBuffer->length() - m_pendingOffset;
165 return WebDataConsumerHandle::Ok; 165 return WebDataConsumerHandle::Ok;
166 } 166 }
167 ASSERT(!m_reader.isEmpty());
168 m_isInRecursion = true; 167 m_isInRecursion = true;
169 if (!m_isReading) { 168 if (!m_isReading) {
170 m_isReading = true; 169 m_isReading = true;
171 ScriptState::Scope scope(m_reader.scriptState()); 170 ScriptState::Scope scope(m_scriptState);
172 V8RecursionScope recursionScope(m_reader.isolate()); 171 v8::Local<v8::Object> reader = m_reader.newLocal(m_scriptState->isol ate());
173 ReadableStreamOperations::read(m_reader.scriptState(), m_reader.v8Va lue()).then( 172 if (reader.IsEmpty()) {
174 OnFulfilled::createFunction(m_reader.scriptState(), m_weakPtrFac tory.createWeakPtr()), 173 // The reader was collected.
175 OnRejected::createFunction(m_reader.scriptState(), m_weakPtrFact ory.createWeakPtr())); 174 m_hasError = true;
175 m_isReading = false;
176 return WebDataConsumerHandle::UnexpectedError;
177 }
178 V8RecursionScope recursionScope(m_scriptState->isolate());
179 ReadableStreamOperations::read(m_scriptState, reader).then(
180 OnFulfilled::createFunction(m_scriptState, this),
181 OnRejected::createFunction(m_scriptState, this));
176 // Note: Microtasks may run here. 182 // Note: Microtasks may run here.
177 } 183 }
178 m_isInRecursion = false; 184 m_isInRecursion = false;
179 return WebDataConsumerHandle::ShouldWait; 185 return WebDataConsumerHandle::ShouldWait;
180 } 186 }
181 187
182 Result endRead(size_t readSize) 188 Result endRead(size_t readSize)
183 { 189 {
184 ASSERT(m_pendingBuffer); 190 ASSERT(m_pendingBuffer);
185 ASSERT(m_pendingOffset + readSize <= m_pendingBuffer->length()); 191 ASSERT(m_pendingOffset + readSize <= m_pendingBuffer->length());
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
233 m_client->didGetReadable(); 239 m_client->didGetReadable();
234 } 240 }
235 241
236 void notifyLater() 242 void notifyLater()
237 { 243 {
238 ASSERT(m_client); 244 ASSERT(m_client);
239 Platform::current()->currentThread()->taskRunner()->postTask(BLINK_FROM_ HERE, bind(&ReadingContext::notify, PassRefPtr<ReadingContext>(this))); 245 Platform::current()->currentThread()->taskRunner()->postTask(BLINK_FROM_ HERE, bind(&ReadingContext::notify, PassRefPtr<ReadingContext>(this)));
240 } 246 }
241 247
242 private: 248 private:
243 ReadingContext(ScriptState* scriptState, v8::Local<v8::Value> stream) 249 ReadingContext(ScriptState* scriptState, v8::Local<v8::Object> streamReader)
244 : m_client(nullptr) 250 : m_reader(scriptState->isolate(), streamReader)
245 , m_weakPtrFactory(this) 251 , m_scriptState(scriptState)
252 , m_client(nullptr)
246 , m_pendingOffset(0) 253 , m_pendingOffset(0)
247 , m_isReading(false) 254 , m_isReading(false)
248 , m_isDone(false) 255 , m_isDone(false)
249 , m_hasError(false) 256 , m_hasError(false)
250 , m_isInRecursion(false) 257 , m_isInRecursion(false)
251 { 258 {
252 if (!ReadableStreamOperations::isLocked(scriptState, stream)) { 259 m_reader.setWeak(this, &ReadingContext::onCollected);
253 // Here the stream implementation must not throw an exception.
254 NonThrowableExceptionState es;
255 m_reader = ReadableStreamOperations::getReader(scriptState, stream, es);
256 }
257 m_hasError = m_reader.isEmpty();
258 } 260 }
259 261
260 // This ScriptValue is leaky because it stores a strong reference to a 262 void onCollected()
261 // JavaScript object. 263 {
262 // TODO(yhirano): Fix it. 264 m_reader.clear();
263 // 265 if (m_isDone || m_hasError)
264 // Holding a ScriptValue here is safe in terms of cross-world wrapper 266 return;
267 m_hasError = true;
268 if (m_client)
269 notifyLater();
270 }
271
272 static void onCollected(const v8::WeakCallbackInfo<ReadableStreamDataConsume rHandle::ReadingContext>& data)
273 {
274 data.GetParameter()->onCollected();
haraken 2016/01/06 05:49:06 ReadableStreamDataConsumerHandle.cpp uses a bunch
yhirano 2016/01/26 05:09:32 Is this point affected by the policy change curren
haraken 2016/01/26 06:09:06 Yes, I think our new policy is going to allow V8 A
275 }
276
277 // |m_reader| is a weak persistent. It should be kept alive by someone
278 // outside of ReadableStreamDataConsumerHandle.
279 // Holding a ScopedPersistent here is safe in terms of cross-world wrapper
265 // leakage because we read only Uint8Array chunks from the reader. 280 // leakage because we read only Uint8Array chunks from the reader.
266 ScriptValue m_reader; 281 ScopedPersistent<v8::Object> m_reader;
haraken 2016/01/06 05:49:06 Instead of holding a weak reference, how about hol
yhirano 2016/01/26 05:09:32 I would keep that a Response is constructed from a
282 ScriptState* m_scriptState;
haraken 2016/01/06 05:49:06 This must be RefPtr<ScriptState>.
yhirano 2016/01/26 05:09:32 Done.
267 WebDataConsumerHandle::Client* m_client; 283 WebDataConsumerHandle::Client* m_client;
268 RefPtr<DOMUint8Array> m_pendingBuffer; 284 RefPtr<DOMUint8Array> m_pendingBuffer;
269 WeakPtrFactory<ReadingContext> m_weakPtrFactory;
270 size_t m_pendingOffset; 285 size_t m_pendingOffset;
271 bool m_isReading; 286 bool m_isReading;
272 bool m_isDone; 287 bool m_isDone;
273 bool m_hasError; 288 bool m_hasError;
274 bool m_isInRecursion; 289 bool m_isInRecursion;
275 }; 290 };
276 291
277 ReadableStreamDataConsumerHandle::ReadableStreamDataConsumerHandle(ScriptState* scriptState, v8::Local<v8::Value> stream) 292 ReadableStreamDataConsumerHandle::ReadableStreamDataConsumerHandle(ScriptState* scriptState, v8::Local<v8::Object> streamReader)
278 : m_readingContext(ReadingContext::create(scriptState, stream)) 293 : m_readingContext(ReadingContext::create(scriptState, streamReader))
279 { 294 {
280 } 295 }
281 ReadableStreamDataConsumerHandle::~ReadableStreamDataConsumerHandle() = default; 296 ReadableStreamDataConsumerHandle::~ReadableStreamDataConsumerHandle() = default;
282 297
283 FetchDataConsumerHandle::Reader* ReadableStreamDataConsumerHandle::obtainReaderI nternal(Client* client) 298 FetchDataConsumerHandle::Reader* ReadableStreamDataConsumerHandle::obtainReaderI nternal(Client* client)
284 { 299 {
285 return new ReadingContext::ReaderImpl(m_readingContext, client); 300 return new ReadingContext::ReaderImpl(m_readingContext, client);
286 } 301 }
287 302
288 } // namespace blink 303 } // namespace blink
289 304
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698