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

Side by Side Diff: Source/modules/fetch/FetchBlobDataConsumerHandle.cpp

Issue 1176403006: [1b] Implement FetchBlobDataConsumerHandle (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Rebase. Created 5 years, 6 months 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
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "config.h"
6 #include "modules/fetch/FetchBlobDataConsumerHandle.h"
7
8 #include "core/dom/ActiveDOMObject.h"
9 #include "core/dom/CrossThreadTask.h"
10 #include "core/dom/ExecutionContext.h"
11 #include "core/fetch/FetchInitiatorTypeNames.h"
12 #include "core/loader/ThreadableLoaderClient.h"
13 #include "modules/fetch/CompositeDataConsumerHandle.h"
14 #include "modules/fetch/DataConsumerHandleUtil.h"
15 #include "platform/Task.h"
16 #include "platform/blob/BlobRegistry.h"
17 #include "platform/blob/BlobURL.h"
18 #include "platform/network/ResourceRequest.h"
19 #include "public/platform/Platform.h"
20 #include "public/platform/WebTraceLocation.h"
21 #include "wtf/Locker.h"
22 #include "wtf/ThreadingPrimitives.h"
23
24 namespace blink {
25
26 using Result = FetchBlobDataConsumerHandle::Result;
27
28 namespace {
29
30 // CrossThreadHolder<T> provides cross-thread access to |obj| of class T.
31 // CrossThreadHolder<T> can be passed across threads while |obj|'s methods
32 // are called on the fixed thread of |executionContext| via
33 // CrossThreadHolder<T>::postTask().
34 // |obj| is destructed when |executionContext| is stopped or
35 // CrossThreadHolder is destructed (earlier of them).
36 template<typename T>
37 class CrossThreadHolder {
38 public:
39 static PassOwnPtr<CrossThreadHolder<T>> create(ExecutionContext* executionCo ntext, PassOwnPtr<T> obj)
40 {
41 return adoptPtr(new CrossThreadHolder(executionContext, obj));
42 }
43
44 // Can be called from any thread.
45 // Executes |task| with |obj| and |executionContext| on the thread of
46 // |executionContext|.
47 // NOTE: |task| might be silently ignored (i.e. not executed) when
48 // |executionContext| is stopped.
yhirano 2015/06/23 07:32:05 It would be worth noting that |task| will be destr
hiroshige 2015/06/23 08:34:22 Done.
49 void postTask(PassOwnPtr<WTF::Function<void(T*, ExecutionContext*)>> task)
50 {
51 m_peer->postTask(task);
52 }
53
54 ~CrossThreadHolder()
55 {
56 m_peer->clear();
57 }
58
59 private:
60 CrossThreadHolder(ExecutionContext* executionContext, PassOwnPtr<T> obj)
61 : m_peer(adoptRef(new Peer()))
62 {
63 m_peer->initialize(new Bridge(executionContext, m_peer, obj));
64 }
65
66 // Bridge and Peer have a reference cycle:
67 // T <-OwnPtr- |Bridge| ---------RefPtr--------> |Peer| <-(RefPtr)- CTH
68 // | | <-CrossThreadPersistent- | |
69 // CTH: CrossThreadHolder<T>
70 // The reference from Peer to Bridge is protected by |Peer::m_mutex| and
71 // cleared when Peer::clear() is called, i.e.:
72 // [1] when |executionContext| is stopped:
73 // T <-OwnPtr- |Bridge| ---------RefPtr--------> |Peer| <-(RefPtr)- CTH
74 // [2] or when CrossThreadHolder is destructed:
75 // T <-OwnPtr- |Bridge| ---------RefPtr--------> |Peer|
76 // in either case, Bridge is shortly garbage collected.
77
78 class Peer;
79
80 // All methods must be called on |executionContext|'s thread.
81 class Bridge
82 : public GarbageCollectedFinalized<Bridge>
83 , public ActiveDOMObject {
84 WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(Bridge);
85 public:
86 Bridge(ExecutionContext* executionContext, PassRefPtr<Peer> peer, PassOw nPtr<T> obj)
87 : ActiveDOMObject(executionContext)
88 , m_peer(peer)
89 , m_obj(obj)
90 {
91 suspendIfNeeded();
92 }
93
94 DEFINE_INLINE_TRACE()
95 {
96 visitor->trace(m_obj);
97 ActiveDOMObject::trace(visitor);
98 }
99
100 T* getObject() const { return m_obj.get(); }
101
102 private:
103 // ActiveDOMObject
104 void stop() override
105 {
106 ASSERT(executionContext()->isContextThread());
107 m_obj.clear();
108 m_peer->clear();
109 }
110
111 RefPtr<Peer> m_peer;
112 OwnPtr<T> m_obj;
113 };
114
115 class Peer : public ThreadSafeRefCounted<Peer> {
116 public:
117 // initialize() must be called only just after construction to create a
118 // reference cycle.
119 void initialize(Bridge* bridge)
120 {
121 m_bridge = bridge;
122 }
123
124 void clear()
125 {
126 MutexLocker locker(m_mutex);
127 m_bridge.clear();
128 }
129
130 void postTask(PassOwnPtr<WTF::Function<void(T*, ExecutionContext*)>> tas k)
131 {
132 MutexLocker locker(m_mutex);
133 if (!m_bridge) {
134 // The bridge has already disappeared.
135 return;
136 }
137 m_bridge->executionContext()->postTask(FROM_HERE, createCrossThreadT ask(&Peer::runTask, this, task));
138 }
139
140 private:
141 // Must be called on the loader thread.
142 void runTask(PassOwnPtr<WTF::Function<void(T*, ExecutionContext*)>> task )
143 {
144 Bridge* bridge;
145 {
146 MutexLocker locker(m_mutex);
147 bridge = m_bridge.get();
148 if (!bridge) {
149 // LoaderContext has already disappeared.
150 return;
151 }
152 ASSERT(bridge->executionContext()->isContextThread());
153 }
154
155 // |bridge| can be set to nullptr by another thread after the
156 // mutex is unlocked, but we can still use |loaderContext| here beca use
yhirano 2015/06/23 07:32:05 Please wrap comments in 80 columns. Ditto for belo
hiroshige 2015/06/23 08:34:22 Done.
157 // the LoaderContext belongs to the current thread (== the loader th read).
158 (*task)(bridge->getObject(), bridge->executionContext());
159 }
160
161 // |m_brige| is protected by |m_mutex|.
162 CrossThreadPersistent<Bridge> m_bridge;
163 Mutex m_mutex;
164 };
165
166 RefPtr<Peer> m_peer;
167 };
168
169 // LoaderContext and associated ThreadableLoader is created and
170 // destructed on the same thread (call this thread loader thread).
171 // All methods must be called on the loader thread.
172 // CommonContext and LoaderContext have a reference cycle:
173 // CTH <-----(OwnPtr)----+
174 // | |
175 // (CrossThreadPersistent) |
176 // v |
177 // LC ----(RefPtr)----> CC <-- RC
178 // LC: LoaderContext
179 // CC: CommonContext
180 // RC: ReaderContext
181 // CTH: CrossThreadHolder<LoaderContext>
182 // This cycle is broken when CommonContext::clearLoaderContext() is called
183 // (which clears the reference to CrossThreadHolder),
184 // or ExecutionContext of the loader thread is stopped
185 // (CrossThreadHolder clears the reference to LoaderContext).
186
187 class LoaderContext {
hiroshige 2015/06/23 06:17:08 I used OwnPtr<LoaderContext> in this Patch Set. A
yhirano 2015/06/23 07:32:06 I don't have a strong preference at this moment, t
hiroshige 2015/06/23 08:34:22 Fine with me.
188 public:
189 virtual ~LoaderContext() { }
190 virtual void start(ExecutionContext*) = 0;
191 };
192
193 class CommonContext
194 : public ThreadSafeRefCounted<CommonContext> {
195 public:
196 CommonContext()
197 : m_handle(CompositeDataConsumerHandle::create(createWaitingDataConsumer Handle())) { }
198
199 // initialize() must be called only just after construction to create a
200 // reference cycle.
201 void initialize(ExecutionContext* executionContext, PassOwnPtr<LoaderContext > loaderContext)
202 {
203 m_loaderContextHolder = CrossThreadHolder<LoaderContext>::create(executi onContext, loaderContext);
204 }
205
206 CompositeDataConsumerHandle* handle() { return m_handle.get(); }
207
208 void startLoader()
209 {
210 MutexLocker locker(m_mutex);
211 if (m_loaderContextHolder)
212 m_loaderContextHolder->postTask(threadSafeBind<LoaderContext*, Execu tionContext*>(&LoaderContext::start));
213 }
214
215 void clearLoaderContext()
216 {
217 MutexLocker locker(m_mutex);
218 m_loaderContextHolder.clear();
219 }
220
221 // Must be called on the loader thread.
222 void error()
223 {
224 clearLoaderContext();
225 handle()->update(createUnexpectedErrorDataConsumerHandle());
226 }
227
228 private:
229 OwnPtr<CompositeDataConsumerHandle> m_handle;
230
231 // |m_loaderContextHolder| is protected by |m_mutex|.
232 OwnPtr<CrossThreadHolder<LoaderContext>> m_loaderContextHolder;
233 Mutex m_mutex;
234 };
235
236 // All methods must be called on the loader thread.
237 // This class is not final for testing.
238 class BlobLoaderContext
239 : public LoaderContext
240 , public ThreadableLoaderClient {
241 WTF_MAKE_FAST_ALLOCATED_WILL_BE_REMOVED(LoaderContext);
242 public:
243 BlobLoaderContext(PassRefPtr<CommonContext> commonContext, PassRefPtr<BlobDa taHandle> blobDataHandle)
244 : m_commonContext(commonContext)
245 , m_blobDataHandle(blobDataHandle)
246 , m_receivedResponse(false) { }
247
248 ~BlobLoaderContext() override
249 {
250 if (m_loader && !m_receivedResponse)
251 m_commonContext->handle()->update(createUnexpectedErrorDataConsumerH andle());
252 if (m_loader) {
253 m_loader->cancel();
254 m_loader.clear();
255 }
256 }
257
258 void start(ExecutionContext* executionContext) override
259 {
260 ASSERT(executionContext->isContextThread());
261 ASSERT(!m_loader);
262
263 m_loader = createLoader(executionContext, this);
264 if (!m_loader)
265 m_commonContext->error();
266 }
267
268 private:
269 virtual PassRefPtr<ThreadableLoader> createLoader(ExecutionContext* executio nContext, ThreadableLoaderClient* client) const
270 {
271 KURL url = BlobURL::createPublicURL(executionContext->securityOrigin());
272 if (url.isEmpty()) {
273 return nullptr;
274 }
275 BlobRegistry::registerPublicBlobURL(executionContext->securityOrigin(), url, m_blobDataHandle);
276
277 ResourceRequest request(url);
278 request.setRequestContext(WebURLRequest::RequestContextInternal);
279 request.setUseStreamOnResponse(true);
280
281 ThreadableLoaderOptions options;
282 options.preflightPolicy = ConsiderPreflight;
283 options.crossOriginRequestPolicy = DenyCrossOriginRequests;
284 options.contentSecurityPolicyEnforcement = DoNotEnforceContentSecurityPo licy;
285 options.initiator = FetchInitiatorTypeNames::internal;
286
287 ResourceLoaderOptions resourceLoaderOptions;
288 resourceLoaderOptions.dataBufferingPolicy = DoNotBufferData;
289
290 return ThreadableLoader::create(*executionContext, client, request, opti ons, resourceLoaderOptions);
291 }
292
293 // ThreadableLoaderClient
294 void didReceiveResponse(unsigned long, const ResourceResponse&, PassOwnPtr<W ebDataConsumerHandle> handle) override
295 {
296 ASSERT(!m_receivedResponse);
297 m_receivedResponse = true;
298 if (!handle) {
299 // Here we assume WebURLLoader must return the response body as
300 // |WebDataConsumerHandle| since we call
301 // request.setUseStreamOnResponse().
302 m_commonContext->handle()->update(createUnexpectedErrorDataConsumerH andle());
303 return;
304 }
305 m_commonContext->handle()->update(handle);
306 }
307 void didFinishLoading(unsigned long, double) override
308 {
309 m_commonContext->clearLoaderContext();
310 }
311 void didFail(const ResourceError&) override
312 {
313 if (m_receivedResponse)
314 m_commonContext->clearLoaderContext();
315 else
316 m_commonContext->error();
317 }
318 void didFailRedirectCheck() override
319 {
320 if (m_receivedResponse)
yhirano 2015/06/23 07:32:06 Is just ASSERT(!m_receivedResponse) fine? Or just
hiroshige 2015/06/23 08:34:22 Done.
321 m_commonContext->clearLoaderContext();
322 else
323 m_commonContext->error();
324 }
325
326 RefPtr<CommonContext> m_commonContext;
327 RefPtr<BlobDataHandle> m_blobDataHandle;
328 RefPtr<ThreadableLoader> m_loader;
329
330 bool m_receivedResponse;
331 };
332
333 } // namespace
334
335 // ReaderContext is referenced from FetchBlobDataConsumerHandle and
336 // FetchDataConsumerHandle::Reader.
337 class FetchBlobDataConsumerHandle::ReaderContext : public ThreadSafeRefCounted<R eaderContext> {
yhirano 2015/06/23 07:32:06 +final
hiroshige 2015/06/23 08:34:22 Done.
338 public:
339 class ReaderImpl : public FetchDataConsumerHandle::Reader {
340 public:
341 ReaderImpl(Client* client, PassRefPtr<ReaderContext> readerContext, Pass OwnPtr<WebDataConsumerHandle::Reader> reader)
342 : m_readerContext(readerContext)
343 , m_reader(reader)
344 , m_notifier(client) { }
345 ~ReaderImpl() override { }
346
347 Result read(void* data, size_t size, Flags flags, size_t* readSize) over ride
348 {
349 if (m_readerContext->drained())
350 return Done;
351 m_readerContext->ensureStartLoader();
352 Result r = m_reader->read(data, size, flags, readSize);
353 if (r != ShouldWait) {
354 // We read non-empty data, so we cannot use the blob data handle which
355 // represents the whole data.
356 m_readerContext->clearBlobDataHandleForDrain();
357 }
358 return r;
359 }
360
361 Result beginRead(const void** buffer, Flags flags, size_t* available) ov erride
362 {
363 if (m_readerContext->drained())
364 return Done;
365 m_readerContext->ensureStartLoader();
366 Result r = m_reader->beginRead(buffer, flags, available);
367 if (r != ShouldWait) {
368 // We read non-empty data, so we cannot use the blob data handle which
369 // represents the whole data.
370 m_readerContext->clearBlobDataHandleForDrain();
371 }
372 return r;
373 }
374
375 Result endRead(size_t readSize) override
376 {
377 return m_reader->endRead(readSize);
378 }
379
380 PassRefPtr<BlobDataHandle> drainAsBlobDataHandle() override
381 {
382 if (!m_readerContext->m_blobDataHandleForDrain || m_readerContext->m _blobDataHandleForDrain->size() == kuint64max)
383 return nullptr;
384 RefPtr<BlobDataHandle> blobDataHandle = m_readerContext->m_blobDataH andleForDrain;
385 m_readerContext->setDrained();
386 m_readerContext->clearBlobDataHandleForDrain();
387 return blobDataHandle.release();
388 }
389
390 private:
391 RefPtr<ReaderContext> m_readerContext;
392 OwnPtr<WebDataConsumerHandle::Reader> m_reader;
393 NotifyOnReaderCreationHelper m_notifier;
yhirano 2015/06/23 07:32:05 Is this member used?
hiroshige 2015/06/23 08:34:22 Yes, just having as a member and its initializatio
394 };
395
396 ReaderContext(ExecutionContext* executionContext, PassRefPtr<BlobDataHandle> blobDataHandle)
397 : m_commonContext(adoptRef(new CommonContext()))
398 , m_loaderStarted(false)
399 , m_drained(false)
400 , m_blobDataHandleForDrain(blobDataHandle)
401 {
402 m_commonContext->initialize(executionContext, adoptPtr(new BlobLoaderCon text(m_commonContext, m_blobDataHandleForDrain)));
403 }
404
405 ~ReaderContext()
406 {
407 m_commonContext->clearLoaderContext();
408 }
409
410 PassOwnPtr<FetchDataConsumerHandle::Reader> obtainReader(WebDataConsumerHand le::Client* client)
411 {
412 return adoptPtr(new ReaderImpl(client, this, m_commonContext->handle()-> obtainReader(client)));
413 }
414
415 void ensureStartLoader()
416 {
417 if (m_loaderStarted)
418 return;
419 m_loaderStarted = true;
420 m_commonContext->startLoader();
421 }
422
423 void clearBlobDataHandleForDrain()
424 {
425 m_blobDataHandleForDrain.clear();
426 }
427
428 private:
429 bool drained() const { return m_drained; }
430 void setDrained() { m_drained = true; }
431
432 RefPtr<CommonContext> m_commonContext;
433
434 // Following members must be accessed only on the reader thread.
435 bool m_loaderStarted;
436 bool m_drained;
437 RefPtr<BlobDataHandle> m_blobDataHandleForDrain;
438 };
439
440 FetchBlobDataConsumerHandle::FetchBlobDataConsumerHandle(ExecutionContext* execu tionContext, PassRefPtr<BlobDataHandle> blobDataHandle)
441 : m_readerContext(adoptRef(new ReaderContext(executionContext, blobDataHandl e)))
442 {
443 }
444
445 PassOwnPtr<FetchDataConsumerHandle> FetchBlobDataConsumerHandle::create(Executio nContext* executionContext, PassRefPtr<BlobDataHandle> blobDataHandle)
446 {
447 if (!blobDataHandle)
448 return createFetchDataConsumerHandleFromWebHandle(createDoneDataConsumer Handle());
449
450 return adoptPtr(new FetchBlobDataConsumerHandle(executionContext, blobDataHa ndle));
451 }
452
453 FetchDataConsumerHandle::Reader* FetchBlobDataConsumerHandle::obtainReaderIntern al(Client* client)
454 {
455 return m_readerContext->obtainReader(client).leakPtr();
456 }
457
458 } // namespace blink
OLDNEW
« no previous file with comments | « Source/modules/fetch/FetchBlobDataConsumerHandle.h ('k') | Source/modules/fetch/FetchBlobDataConsumerHandleTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698