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

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: Reflect comments. 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) and
48 // destructed (possibly on the calling thread or on the thread of
49 // |executionContext|) when |executionContext| is stopped.
50 void postTask(PassOwnPtr<WTF::Function<void(T*, ExecutionContext*)>> task)
51 {
52 m_peer->postTask(task);
53 }
54
55 ~CrossThreadHolder()
56 {
57 m_peer->clear();
58 }
59
60 private:
61 CrossThreadHolder(ExecutionContext* executionContext, PassOwnPtr<T> obj)
62 : m_peer(Peer::create())
63 {
64 m_peer->initialize(new Bridge(executionContext, m_peer, obj));
65 }
66
67 // Bridge and Peer have a reference cycle:
68 // T <-OwnPtr- |Bridge| ---------RefPtr--------> |Peer| <-(RefPtr)- CTH
69 // | | <-CrossThreadPersistent- | |
70 // CTH: CrossThreadHolder<T>
71 // The reference from Peer to Bridge is protected by |Peer::m_mutex| and
72 // cleared when Peer::clear() is called, i.e.:
73 // [1] when |executionContext| is stopped:
74 // T <-OwnPtr- |Bridge| ---------RefPtr--------> |Peer| <-(RefPtr)- CTH
75 // [2] or when CrossThreadHolder is destructed:
76 // T <-OwnPtr- |Bridge| ---------RefPtr--------> |Peer|
77 // in either case, Bridge is shortly garbage collected.
78
79 class Peer;
80
81 // All methods must be called on |executionContext|'s thread.
82 class Bridge
83 : public GarbageCollectedFinalized<Bridge>
84 , public ActiveDOMObject {
85 WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(Bridge);
86 public:
87 Bridge(ExecutionContext* executionContext, PassRefPtr<Peer> peer, PassOw nPtr<T> obj)
88 : ActiveDOMObject(executionContext)
89 , m_peer(peer)
90 , m_obj(obj)
91 {
92 suspendIfNeeded();
93 }
94
95 DEFINE_INLINE_TRACE()
96 {
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 static PassRefPtr<Peer> create()
118 {
119 return adoptRef(new Peer());
120 }
121
122 // initialize() must be called only just after construction to create a
123 // reference cycle.
124 void initialize(Bridge* bridge)
125 {
126 m_bridge = bridge;
127 }
128
129 void clear()
130 {
131 MutexLocker locker(m_mutex);
132 m_bridge.clear();
133 }
134
135 void postTask(PassOwnPtr<WTF::Function<void(T*, ExecutionContext*)>> tas k)
136 {
137 MutexLocker locker(m_mutex);
138 if (!m_bridge) {
139 // The bridge has already disappeared.
140 return;
141 }
142 m_bridge->executionContext()->postTask(FROM_HERE, createCrossThreadT ask(&Peer::runTask, this, task));
143 }
144
145 private:
146 Peer() = default;
147
148 // Must be called on the loader thread.
149 void runTask(PassOwnPtr<WTF::Function<void(T*, ExecutionContext*)>> task )
150 {
151 Bridge* bridge;
152 {
153 MutexLocker locker(m_mutex);
154 bridge = m_bridge.get();
155 if (!bridge) {
156 // LoaderContext has already disappeared.
157 return;
158 }
159 ASSERT(bridge->executionContext()->isContextThread());
160 }
161
162 // |bridge| can be set to nullptr by another thread after the
163 // mutex is unlocked, but we can still use |loaderContext| here
164 // because the LoaderContext belongs to the current thread
165 // (==the loader thread).
166 (*task)(bridge->getObject(), bridge->executionContext());
167 }
168
169 // |m_brige| is protected by |m_mutex|.
170 CrossThreadPersistent<Bridge> m_bridge;
171 Mutex m_mutex;
172 };
173
174 RefPtr<Peer> m_peer;
175 };
176
177 // LoaderContext and associated ThreadableLoader is created and
178 // destructed on the same thread (call this thread loader thread).
179 // All methods must be called on the loader thread.
180 // CommonContext and LoaderContext have a reference cycle:
181 // CTH <-----(OwnPtr)----+
182 // | |
183 // (CrossThreadPersistent) |
184 // v |
185 // LC ----(RefPtr)----> CC <-- RC
186 // LC: LoaderContext
187 // CC: CommonContext
188 // RC: ReaderContext
189 // CTH: CrossThreadHolder<LoaderContext>
190 // This cycle is broken when CommonContext::clearLoaderContext() is called
191 // (which clears the reference to CrossThreadHolder),
192 // or ExecutionContext of the loader thread is stopped
193 // (CrossThreadHolder clears the reference to LoaderContext).
194
195 class LoaderContext {
196 public:
197 virtual ~LoaderContext() { }
198 virtual void start(ExecutionContext*) = 0;
199 };
200
201 class CommonContext
202 : public ThreadSafeRefCounted<CommonContext> {
203 public:
204 CommonContext()
205 : m_handle(CompositeDataConsumerHandle::create(createWaitingDataConsumer Handle())) { }
206
207 // initialize() must be called only just after construction to create a
208 // reference cycle.
209 void initialize(ExecutionContext* executionContext, PassOwnPtr<LoaderContext > loaderContext)
210 {
211 m_loaderContextHolder = CrossThreadHolder<LoaderContext>::create(executi onContext, loaderContext);
212 }
213
214 CompositeDataConsumerHandle* handle() { return m_handle.get(); }
215
216 void startLoader()
217 {
218 MutexLocker locker(m_mutex);
219 if (m_loaderContextHolder)
220 m_loaderContextHolder->postTask(threadSafeBind<LoaderContext*, Execu tionContext*>(&LoaderContext::start));
221 }
222
223 void clearLoaderContext()
224 {
225 MutexLocker locker(m_mutex);
226 m_loaderContextHolder.clear();
227 }
228
229 // Must be called on the loader thread.
230 void error()
231 {
232 clearLoaderContext();
233 handle()->update(createUnexpectedErrorDataConsumerHandle());
234 }
235
236 private:
237 OwnPtr<CompositeDataConsumerHandle> m_handle;
238
239 // |m_loaderContextHolder| is protected by |m_mutex|.
240 OwnPtr<CrossThreadHolder<LoaderContext>> m_loaderContextHolder;
241 Mutex m_mutex;
242 };
243
244 // All methods must be called on the loader thread.
245 class BlobLoaderContext final
246 : public LoaderContext
247 , public ThreadableLoaderClient {
248 public:
249 BlobLoaderContext(PassRefPtr<CommonContext> commonContext, PassRefPtr<BlobDa taHandle> blobDataHandle, FetchBlobDataConsumerHandle::LoaderFactory* loaderFact ory)
250 : m_commonContext(commonContext)
251 , m_blobDataHandle(blobDataHandle)
252 , m_loaderFactory(loaderFactory)
253 , m_receivedResponse(false) { }
254
255 ~BlobLoaderContext() override
256 {
257 if (m_loader && !m_receivedResponse)
258 m_commonContext->handle()->update(createUnexpectedErrorDataConsumerH andle());
259 if (m_loader) {
260 m_loader->cancel();
261 m_loader.clear();
262 }
263 }
264
265 void start(ExecutionContext* executionContext) override
266 {
267 ASSERT(executionContext->isContextThread());
268 ASSERT(!m_loader);
269
270 m_loader = createLoader(executionContext, this);
271 if (!m_loader)
272 m_commonContext->error();
273 }
274
275 private:
276 virtual PassRefPtr<ThreadableLoader> createLoader(ExecutionContext* executio nContext, ThreadableLoaderClient* client) const
277 {
278 KURL url = BlobURL::createPublicURL(executionContext->securityOrigin());
279 if (url.isEmpty()) {
280 return nullptr;
281 }
282 BlobRegistry::registerPublicBlobURL(executionContext->securityOrigin(), url, m_blobDataHandle);
283
284 ResourceRequest request(url);
285 request.setRequestContext(WebURLRequest::RequestContextInternal);
286 request.setUseStreamOnResponse(true);
287
288 ThreadableLoaderOptions options;
289 options.preflightPolicy = ConsiderPreflight;
290 options.crossOriginRequestPolicy = DenyCrossOriginRequests;
291 options.contentSecurityPolicyEnforcement = DoNotEnforceContentSecurityPo licy;
292 options.initiator = FetchInitiatorTypeNames::internal;
293
294 ResourceLoaderOptions resourceLoaderOptions;
295 resourceLoaderOptions.dataBufferingPolicy = DoNotBufferData;
296
297 return m_loaderFactory->create(*executionContext, client, request, optio ns, resourceLoaderOptions);
298 }
299
300 // ThreadableLoaderClient
301 void didReceiveResponse(unsigned long, const ResourceResponse&, PassOwnPtr<W ebDataConsumerHandle> handle) override
302 {
303 ASSERT(!m_receivedResponse);
304 m_receivedResponse = true;
305 if (!handle) {
306 // Here we assume WebURLLoader must return the response body as
307 // |WebDataConsumerHandle| since we call
308 // request.setUseStreamOnResponse().
309 m_commonContext->handle()->update(createUnexpectedErrorDataConsumerH andle());
310 return;
311 }
312 m_commonContext->handle()->update(handle);
313 }
314 void didFinishLoading(unsigned long, double) override
315 {
316 m_commonContext->clearLoaderContext();
317 }
318 void didFail(const ResourceError&) override
319 {
320 if (m_receivedResponse)
321 m_commonContext->clearLoaderContext();
322 else
323 m_commonContext->error();
324 }
325 void didFailRedirectCheck() override
326 {
327 // We don't expect redirects for Blob loading.
328 ASSERT_NOT_REACHED();
329 }
330
331 RefPtr<CommonContext> m_commonContext;
332 RefPtr<BlobDataHandle> m_blobDataHandle;
333 Persistent<FetchBlobDataConsumerHandle::LoaderFactory> m_loaderFactory;
hiroshige 2015/06/23 08:38:59 I made this Persistent rather than CrossThreadPers
yhirano 2015/06/24 11:15:14 Acknowledged.
334 RefPtr<ThreadableLoader> m_loader;
335
336 bool m_receivedResponse;
337 };
338
339 class DefaultLoaderFactory final : public FetchBlobDataConsumerHandle::LoaderFac tory {
340 public:
341 PassRefPtr<ThreadableLoader> create(
342 ExecutionContext& executionContext,
343 ThreadableLoaderClient* client,
344 const ResourceRequest& request,
345 const ThreadableLoaderOptions& options,
346 const ResourceLoaderOptions& resourceLoaderOptions) override
347 {
348 return ThreadableLoader::create(executionContext, client, request, optio ns, resourceLoaderOptions);
349 }
350 };
351
352 } // namespace
353
354 // ReaderContext is referenced from FetchBlobDataConsumerHandle and
355 // ReaderContext::ReaderImpl.
356 // All functions/members must be called/accessed only on the reader thread,
357 // except for constructor, destructor, obtainReader() and |m_commonContext|
358 // accessed from them.
359 class FetchBlobDataConsumerHandle::ReaderContext final : public ThreadSafeRefCou nted<ReaderContext> {
360 public:
361 class ReaderImpl : public FetchDataConsumerHandle::Reader {
362 public:
363 ReaderImpl(Client* client, PassRefPtr<ReaderContext> readerContext, Pass OwnPtr<WebDataConsumerHandle::Reader> reader)
364 : m_readerContext(readerContext)
365 , m_reader(reader)
366 , m_notifier(client) { }
367 ~ReaderImpl() override { }
368
369 Result read(void* data, size_t size, Flags flags, size_t* readSize) over ride
370 {
371 if (m_readerContext->drained())
372 return Done;
373 m_readerContext->ensureStartLoader();
374 Result r = m_reader->read(data, size, flags, readSize);
375 if (r != ShouldWait) {
376 // We read non-empty data, so we cannot use the blob data
377 // handle which represents the whole data.
378 m_readerContext->clearBlobDataHandleForDrain();
379 }
380 return r;
381 }
382
383 Result beginRead(const void** buffer, Flags flags, size_t* available) ov erride
384 {
385 if (m_readerContext->drained())
386 return Done;
387 m_readerContext->ensureStartLoader();
388 Result r = m_reader->beginRead(buffer, flags, available);
389 if (r != ShouldWait) {
390 // We read non-empty data, so we cannot use the blob data
391 // handle which represents the whole data.
392 m_readerContext->clearBlobDataHandleForDrain();
393 }
394 return r;
395 }
396
397 Result endRead(size_t readSize) override
398 {
399 return m_reader->endRead(readSize);
400 }
401
402 PassRefPtr<BlobDataHandle> drainAsBlobDataHandle() override
403 {
404 if (!m_readerContext->m_blobDataHandleForDrain || m_readerContext->m _blobDataHandleForDrain->size() == kuint64max)
405 return nullptr;
406 RefPtr<BlobDataHandle> blobDataHandle = m_readerContext->m_blobDataH andleForDrain;
407 m_readerContext->setDrained();
408 m_readerContext->clearBlobDataHandleForDrain();
409 return blobDataHandle.release();
410 }
411
412 private:
413 RefPtr<ReaderContext> m_readerContext;
414 OwnPtr<WebDataConsumerHandle::Reader> m_reader;
415 NotifyOnReaderCreationHelper m_notifier;
416 };
417
418 ReaderContext(ExecutionContext* executionContext, PassRefPtr<BlobDataHandle> blobDataHandle, FetchBlobDataConsumerHandle::LoaderFactory* loaderFactory)
419 : m_commonContext(adoptRef(new CommonContext()))
420 , m_loaderStarted(false)
421 , m_drained(false)
422 , m_blobDataHandleForDrain(blobDataHandle)
423 {
424 m_commonContext->initialize(executionContext, adoptPtr(new BlobLoaderCon text(m_commonContext, m_blobDataHandleForDrain, loaderFactory)));
425 }
426
427 ~ReaderContext()
428 {
429 m_commonContext->clearLoaderContext();
430 }
431
432 PassOwnPtr<FetchDataConsumerHandle::Reader> obtainReader(WebDataConsumerHand le::Client* client)
433 {
434 return adoptPtr(new ReaderImpl(client, this, m_commonContext->handle()-> obtainReader(client)));
435 }
436
437 private:
438 void ensureStartLoader()
439 {
440 if (m_loaderStarted)
441 return;
442 m_loaderStarted = true;
443 m_commonContext->startLoader();
444 }
445
446 void clearBlobDataHandleForDrain()
447 {
448 m_blobDataHandleForDrain.clear();
449 }
450
451 bool drained() const { return m_drained; }
452 void setDrained() { m_drained = true; }
453
454 RefPtr<CommonContext> m_commonContext;
455
456 bool m_loaderStarted;
457 bool m_drained;
458 RefPtr<BlobDataHandle> m_blobDataHandleForDrain;
459 };
460
461 FetchBlobDataConsumerHandle::FetchBlobDataConsumerHandle(ExecutionContext* execu tionContext, PassRefPtr<BlobDataHandle> blobDataHandle, LoaderFactory* loaderFac tory)
462 : m_readerContext(adoptRef(new ReaderContext(executionContext, blobDataHandl e, loaderFactory)))
463 {
464 }
465
466 PassOwnPtr<FetchDataConsumerHandle> FetchBlobDataConsumerHandle::create(Executio nContext* executionContext, PassRefPtr<BlobDataHandle> blobDataHandle, LoaderFac tory* loaderFactory)
467 {
468 if (!blobDataHandle)
469 return createFetchDataConsumerHandleFromWebHandle(createDoneDataConsumer Handle());
470
471 return adoptPtr(new FetchBlobDataConsumerHandle(executionContext, blobDataHa ndle, loaderFactory));
472 }
473
474 PassOwnPtr<FetchDataConsumerHandle> FetchBlobDataConsumerHandle::create(Executio nContext* executionContext, PassRefPtr<BlobDataHandle> blobDataHandle)
475 {
476 if (!blobDataHandle)
477 return createFetchDataConsumerHandleFromWebHandle(createDoneDataConsumer Handle());
478
479 return adoptPtr(new FetchBlobDataConsumerHandle(executionContext, blobDataHa ndle, new DefaultLoaderFactory));
480 }
481
482 FetchDataConsumerHandle::Reader* FetchBlobDataConsumerHandle::obtainReaderIntern al(Client* client)
483 {
484 return m_readerContext->obtainReader(client).leakPtr();
485 }
486
487 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698