 Chromium Code Reviews
 Chromium Code Reviews Issue 399863002:
  Gracefully handle FileReader() read operations when in a detached state.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/blink.git@master
    
  
    Issue 399863002:
  Gracefully handle FileReader() read operations when in a detached state.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/blink.git@master| OLD | NEW | 
|---|---|
| 1 /* | 1 /* | 
| 2 * Copyright (C) 2010 Google Inc. All rights reserved. | 2 * Copyright (C) 2010 Google Inc. All rights reserved. | 
| 3 * | 3 * | 
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without | 
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are | 
| 6 * met: | 6 * met: | 
| 7 * | 7 * | 
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright | 
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. | 
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above | 
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 68 #endif | 68 #endif | 
| 69 | 69 | 
| 70 } // namespace | 70 } // namespace | 
| 71 | 71 | 
| 72 // Embedders like chromium limit the number of simultaneous requests to avoid | 72 // Embedders like chromium limit the number of simultaneous requests to avoid | 
| 73 // excessive IPC congestion. We limit this to 100 per thread to throttle the | 73 // excessive IPC congestion. We limit this to 100 per thread to throttle the | 
| 74 // requests (the value is arbitrarily chosen). | 74 // requests (the value is arbitrarily chosen). | 
| 75 static const size_t kMaxOutstandingRequestsPerThread = 100; | 75 static const size_t kMaxOutstandingRequestsPerThread = 100; | 
| 76 static const double progressNotificationIntervalMS = 50; | 76 static const double progressNotificationIntervalMS = 50; | 
| 77 | 77 | 
| 78 // FIXME: Oilpan: if ExecutionContext is moved to the heap, consider | |
| 79 // making this object an ExecutionContext supplement (only.) | |
| 78 class FileReader::ThrottlingController FINAL : public NoBaseWillBeGarbageCollect edFinalized<FileReader::ThrottlingController>, public WillBeHeapSupplement<Local Frame>, public WillBeHeapSupplement<WorkerClients> { | 80 class FileReader::ThrottlingController FINAL : public NoBaseWillBeGarbageCollect edFinalized<FileReader::ThrottlingController>, public WillBeHeapSupplement<Local Frame>, public WillBeHeapSupplement<WorkerClients> { | 
| 79 WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(FileReader::ThrottlingController); | 81 WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(FileReader::ThrottlingController); | 
| 80 public: | 82 public: | 
| 81 static ThrottlingController* from(ExecutionContext* context) | 83 static ThrottlingController* from(ExecutionContext* context) | 
| 82 { | 84 { | 
| 85 if (!context) | |
| 86 return 0; | |
| 87 | |
| 83 if (context->isDocument()) { | 88 if (context->isDocument()) { | 
| 84 Document* document = toDocument(context); | 89 Document* document = toDocument(context); | 
| 85 if (!document->frame()) | 90 if (!document->frame()) | 
| 86 return 0; | 91 return 0; | 
| 87 | 92 | 
| 88 ThrottlingController* controller = static_cast<ThrottlingController* >(WillBeHeapSupplement<LocalFrame>::from(document->frame(), supplementName())); | 93 ThrottlingController* controller = static_cast<ThrottlingController* >(WillBeHeapSupplement<LocalFrame>::from(document->frame(), supplementName())); | 
| 89 if (controller) | 94 if (controller) | 
| 90 return controller; | 95 return controller; | 
| 91 | 96 | 
| 92 controller = new ThrottlingController(); | 97 controller = new ThrottlingController(); | 
| 93 WillBeHeapSupplement<LocalFrame>::provideTo(*document->frame(), supp lementName(), adoptPtrWillBeNoop(controller)); | 98 WillBeHeapSupplement<LocalFrame>::provideTo(*document->frame(), supp lementName(), adoptPtrWillBeNoop(controller)); | 
| 94 return controller; | 99 return controller; | 
| 95 } | 100 } | 
| 96 ASSERT(!isMainThread()); | 101 ASSERT(!isMainThread()); | 
| 97 ASSERT(context->isWorkerGlobalScope()); | 102 ASSERT(context->isWorkerGlobalScope()); | 
| 98 WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); | 103 WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); | 
| 99 ThrottlingController* controller = static_cast<ThrottlingController*>(Wi llBeHeapSupplement<WorkerClients>::from(workerGlobalScope->clients(), supplement Name())); | 104 ThrottlingController* controller = static_cast<ThrottlingController*>(Wi llBeHeapSupplement<WorkerClients>::from(workerGlobalScope->clients(), supplement Name())); | 
| 100 if (controller) | 105 if (controller) | 
| 101 return controller; | 106 return controller; | 
| 102 | 107 | 
| 103 controller = new ThrottlingController(); | 108 controller = new ThrottlingController(); | 
| 104 WillBeHeapSupplement<WorkerClients>::provideTo(*workerGlobalScope->clien ts(), supplementName(), adoptPtrWillBeNoop(controller)); | 109 WillBeHeapSupplement<WorkerClients>::provideTo(*workerGlobalScope->clien ts(), supplementName(), adoptPtrWillBeNoop(controller)); | 
| 105 return controller; | 110 return controller; | 
| 106 } | 111 } | 
| 107 | 112 | 
| 108 ~ThrottlingController() { } | 113 ~ThrottlingController() { } | 
| 109 | 114 | 
| 110 enum FinishReaderType { DoNotRunPendingReaders, RunPendingReaders }; | 115 enum FinishReaderType { DoNotRunPendingReaders, RunPendingReaders }; | 
| 111 | 116 | 
| 117 static void pushReader(ExecutionContext* context, FileReader* reader) | |
| 118 { | |
| 119 ThrottlingController* controller = from(context); | |
| 120 if (!controller) | |
| 121 return; | |
| 122 | |
| 123 controller->pushReader(reader); | |
| 124 } | |
| 125 | |
| 126 static FinishReaderType removeReader(ExecutionContext* context, FileReader* reader) | |
| 127 { | |
| 128 ThrottlingController* controller = from(context); | |
| 129 if (!controller) | |
| 130 return DoNotRunPendingReaders; | |
| 131 | |
| 132 return controller->removeReader(reader); | |
| 133 } | |
| 134 | |
| 135 static void finishReader(ExecutionContext* context, FileReader* reader, Fini shReaderType nextStep) | |
| 136 { | |
| 137 ThrottlingController* controller = from(context); | |
| 138 if (!controller) | |
| 139 return; | |
| 140 | |
| 141 controller->finishReader(reader, nextStep); | |
| 142 } | |
| 143 | |
| 144 void trace(Visitor* visitor) | |
| 145 { | |
| 146 #if ENABLE(OILPAN) | |
| 147 visitor->trace(m_pendingReaders); | |
| 148 visitor->trace(m_runningReaders); | |
| 149 #endif | |
| 150 WillBeHeapSupplement<LocalFrame>::trace(visitor); | |
| 151 WillBeHeapSupplement<WorkerClients>::trace(visitor); | |
| 152 } | |
| 153 | |
| 154 private: | |
| 155 ThrottlingController() | |
| 156 : m_maxRunningReaders(kMaxOutstandingRequestsPerThread) | |
| 157 { | |
| 158 } | |
| 159 | |
| 112 void pushReader(FileReader* reader) | 160 void pushReader(FileReader* reader) | 
| 113 { | 161 { | 
| 114 if (m_pendingReaders.isEmpty() | 162 if (m_pendingReaders.isEmpty() | 
| 115 && m_runningReaders.size() < m_maxRunningReaders) { | 163 && m_runningReaders.size() < m_maxRunningReaders) { | 
| 116 reader->executePendingRead(); | 164 reader->executePendingRead(); | 
| 117 ASSERT(!m_runningReaders.contains(reader)); | 165 ASSERT(!m_runningReaders.contains(reader)); | 
| 118 m_runningReaders.add(reader); | 166 m_runningReaders.add(reader); | 
| 119 return; | 167 return; | 
| 120 } | 168 } | 
| 121 m_pendingReaders.append(reader); | 169 m_pendingReaders.append(reader); | 
| (...skipping 16 matching lines...) Expand all Loading... | |
| 138 } | 186 } | 
| 139 return DoNotRunPendingReaders; | 187 return DoNotRunPendingReaders; | 
| 140 } | 188 } | 
| 141 | 189 | 
| 142 void finishReader(FileReader* reader, FinishReaderType nextStep) | 190 void finishReader(FileReader* reader, FinishReaderType nextStep) | 
| 143 { | 191 { | 
| 144 if (nextStep == RunPendingReaders) | 192 if (nextStep == RunPendingReaders) | 
| 145 executeReaders(); | 193 executeReaders(); | 
| 146 } | 194 } | 
| 147 | 195 | 
| 148 void trace(Visitor* visitor) | |
| 149 { | |
| 150 #if ENABLE(OILPAN) | |
| 151 visitor->trace(m_pendingReaders); | |
| 152 visitor->trace(m_runningReaders); | |
| 153 #endif | |
| 154 WillBeHeapSupplement<LocalFrame>::trace(visitor); | |
| 155 WillBeHeapSupplement<WorkerClients>::trace(visitor); | |
| 156 } | |
| 157 | |
| 158 private: | |
| 159 ThrottlingController() | |
| 160 : m_maxRunningReaders(kMaxOutstandingRequestsPerThread) | |
| 161 { | |
| 162 } | |
| 163 | |
| 164 void executeReaders() | 196 void executeReaders() | 
| 165 { | 197 { | 
| 166 while (m_runningReaders.size() < m_maxRunningReaders) { | 198 while (m_runningReaders.size() < m_maxRunningReaders) { | 
| 167 if (m_pendingReaders.isEmpty()) | 199 if (m_pendingReaders.isEmpty()) | 
| 168 return; | 200 return; | 
| 169 FileReader* reader = m_pendingReaders.takeFirst(); | 201 FileReader* reader = m_pendingReaders.takeFirst(); | 
| 170 reader->executePendingRead(); | 202 reader->executePendingRead(); | 
| 171 m_runningReaders.add(reader); | 203 m_runningReaders.add(reader); | 
| 172 } | 204 } | 
| 173 } | 205 } | 
| (...skipping 28 matching lines...) Expand all Loading... | |
| 202 } | 234 } | 
| 203 | 235 | 
| 204 const AtomicString& FileReader::interfaceName() const | 236 const AtomicString& FileReader::interfaceName() const | 
| 205 { | 237 { | 
| 206 return EventTargetNames::FileReader; | 238 return EventTargetNames::FileReader; | 
| 207 } | 239 } | 
| 208 | 240 | 
| 209 void FileReader::stop() | 241 void FileReader::stop() | 
| 210 { | 242 { | 
| 211 if (hasPendingActivity()) | 243 if (hasPendingActivity()) | 
| 212 throttlingController()->finishReader(this, throttlingController()->remov eReader(this)); | 244 ThrottlingController::finishReader(executionContext(), this, ThrottlingC ontroller::removeReader(executionContext(), this)); | 
| 213 terminate(); | 245 terminate(); | 
| 214 } | 246 } | 
| 215 | 247 | 
| 216 bool FileReader::hasPendingActivity() const | 248 bool FileReader::hasPendingActivity() const | 
| 217 { | 249 { | 
| 218 return m_state == LOADING; | 250 return m_state == LOADING; | 
| 219 } | 251 } | 
| 220 | 252 | 
| 221 void FileReader::readAsArrayBuffer(Blob* blob, ExceptionState& exceptionState) | 253 void FileReader::readAsArrayBuffer(Blob* blob, ExceptionState& exceptionState) | 
| 222 { | 254 { | 
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 278 if (m_state == LOADING) { | 310 if (m_state == LOADING) { | 
| 279 exceptionState.throwDOMException(InvalidStateError, "The object is alrea dy busy reading Blobs."); | 311 exceptionState.throwDOMException(InvalidStateError, "The object is alrea dy busy reading Blobs."); | 
| 280 return; | 312 return; | 
| 281 } | 313 } | 
| 282 | 314 | 
| 283 if (blob->hasBeenClosed()) { | 315 if (blob->hasBeenClosed()) { | 
| 284 exceptionState.throwDOMException(InvalidStateError, String(blob->isFile( ) ? "File" : "Blob") + " has been closed."); | 316 exceptionState.throwDOMException(InvalidStateError, String(blob->isFile( ) ? "File" : "Blob") + " has been closed."); | 
| 285 return; | 317 return; | 
| 286 } | 318 } | 
| 287 | 319 | 
| 320 if (!ThrottlingController::from(executionContext())) { | |
| 321 exceptionState.throwDOMException(InvalidStateError, "Reading from a Docu ment-detached FileReader is not supported."); | |
| 
haraken
2014/07/18 01:01:53
Looks like kinuko-san prefers AbortError?
 
nhiroki
2014/07/18 01:54:42
I'd prefer AbortError, too.
 
sof
2014/07/18 05:26:37
ok, let's throw that instead. Switched over.
 | |
| 322 return; | |
| 323 } | |
| 324 | |
| 288 // "Snapshot" the Blob data rather than the Blob itself as ongoing | 325 // "Snapshot" the Blob data rather than the Blob itself as ongoing | 
| 289 // read operations should not be affected if close() is called on | 326 // read operations should not be affected if close() is called on | 
| 290 // the Blob being read. | 327 // the Blob being read. | 
| 291 m_blobDataHandle = blob->blobDataHandle(); | 328 m_blobDataHandle = blob->blobDataHandle(); | 
| 292 m_blobType = blob->type(); | 329 m_blobType = blob->type(); | 
| 293 m_readType = type; | 330 m_readType = type; | 
| 294 m_state = LOADING; | 331 m_state = LOADING; | 
| 295 m_loadingState = LoadingStatePending; | 332 m_loadingState = LoadingStatePending; | 
| 296 m_error = nullptr; | 333 m_error = nullptr; | 
| 297 throttlingController()->pushReader(this); | 334 ThrottlingController::pushReader(executionContext(), this); | 
| 298 } | 335 } | 
| 299 | 336 | 
| 300 void FileReader::executePendingRead() | 337 void FileReader::executePendingRead() | 
| 301 { | 338 { | 
| 302 ASSERT(m_loadingState == LoadingStatePending); | 339 ASSERT(m_loadingState == LoadingStatePending); | 
| 303 m_loadingState = LoadingStateLoading; | 340 m_loadingState = LoadingStateLoading; | 
| 304 | 341 | 
| 305 m_loader = adoptPtr(new FileReaderLoader(m_readType, this)); | 342 m_loader = adoptPtr(new FileReaderLoader(m_readType, this)); | 
| 306 m_loader->setEncoding(m_encoding); | 343 m_loader->setEncoding(m_encoding); | 
| 307 m_loader->setDataType(m_blobType); | 344 m_loader->setDataType(m_blobType); | 
| (...skipping 23 matching lines...) Expand all Loading... | |
| 331 | 368 | 
| 332 void FileReader::doAbort() | 369 void FileReader::doAbort() | 
| 333 { | 370 { | 
| 334 ASSERT(m_state != DONE); | 371 ASSERT(m_state != DONE); | 
| 335 | 372 | 
| 336 terminate(); | 373 terminate(); | 
| 337 | 374 | 
| 338 m_error = FileError::create(FileError::ABORT_ERR); | 375 m_error = FileError::create(FileError::ABORT_ERR); | 
| 339 | 376 | 
| 340 // Unregister the reader. | 377 // Unregister the reader. | 
| 341 ThrottlingController::FinishReaderType finalStep = throttlingController()->r emoveReader(this); | 378 ThrottlingController::FinishReaderType finalStep = ThrottlingController::rem oveReader(executionContext(), this); | 
| 342 | 379 | 
| 343 fireEvent(EventTypeNames::error); | 380 fireEvent(EventTypeNames::error); | 
| 344 fireEvent(EventTypeNames::abort); | 381 fireEvent(EventTypeNames::abort); | 
| 345 fireEvent(EventTypeNames::loadend); | 382 fireEvent(EventTypeNames::loadend); | 
| 346 | 383 | 
| 347 // All possible events have fired and we're done, no more pending activity. | 384 // All possible events have fired and we're done, no more pending activity. | 
| 348 throttlingController()->finishReader(this, finalStep); | 385 ThrottlingController::finishReader(executionContext(), this, finalStep); | 
| 349 } | 386 } | 
| 350 | 387 | 
| 351 void FileReader::terminate() | 388 void FileReader::terminate() | 
| 352 { | 389 { | 
| 353 if (m_loader) { | 390 if (m_loader) { | 
| 354 m_loader->cancel(); | 391 m_loader->cancel(); | 
| 355 m_loader = nullptr; | 392 m_loader = nullptr; | 
| 356 } | 393 } | 
| 357 m_state = DONE; | 394 m_state = DONE; | 
| 358 m_loadingState = LoadingStateNone; | 395 m_loadingState = LoadingStateNone; | 
| (...skipping 26 matching lines...) Expand all Loading... | |
| 385 // since any of the events could call abort(), which internally checks | 422 // since any of the events could call abort(), which internally checks | 
| 386 // if we're still loading (therefore we need abort process) or not. | 423 // if we're still loading (therefore we need abort process) or not. | 
| 387 m_loadingState = LoadingStateNone; | 424 m_loadingState = LoadingStateNone; | 
| 388 | 425 | 
| 389 fireEvent(EventTypeNames::progress); | 426 fireEvent(EventTypeNames::progress); | 
| 390 | 427 | 
| 391 ASSERT(m_state != DONE); | 428 ASSERT(m_state != DONE); | 
| 392 m_state = DONE; | 429 m_state = DONE; | 
| 393 | 430 | 
| 394 // Unregister the reader. | 431 // Unregister the reader. | 
| 395 ThrottlingController::FinishReaderType finalStep = throttlingController()->r emoveReader(this); | 432 ThrottlingController::FinishReaderType finalStep = ThrottlingController::rem oveReader(executionContext(), this); | 
| 396 | 433 | 
| 397 fireEvent(EventTypeNames::load); | 434 fireEvent(EventTypeNames::load); | 
| 398 fireEvent(EventTypeNames::loadend); | 435 fireEvent(EventTypeNames::loadend); | 
| 399 | 436 | 
| 400 // All possible events have fired and we're done, no more pending activity. | 437 // All possible events have fired and we're done, no more pending activity. | 
| 401 throttlingController()->finishReader(this, finalStep); | 438 ThrottlingController::finishReader(executionContext(), this, finalStep); | 
| 402 } | 439 } | 
| 403 | 440 | 
| 404 void FileReader::didFail(FileError::ErrorCode errorCode) | 441 void FileReader::didFail(FileError::ErrorCode errorCode) | 
| 405 { | 442 { | 
| 406 if (m_loadingState == LoadingStateAborted) | 443 if (m_loadingState == LoadingStateAborted) | 
| 407 return; | 444 return; | 
| 408 ASSERT(m_loadingState == LoadingStateLoading); | 445 ASSERT(m_loadingState == LoadingStateLoading); | 
| 409 m_loadingState = LoadingStateNone; | 446 m_loadingState = LoadingStateNone; | 
| 410 | 447 | 
| 411 ASSERT(m_state != DONE); | 448 ASSERT(m_state != DONE); | 
| 412 m_state = DONE; | 449 m_state = DONE; | 
| 413 | 450 | 
| 414 m_error = FileError::create(static_cast<FileError::ErrorCode>(errorCode)); | 451 m_error = FileError::create(static_cast<FileError::ErrorCode>(errorCode)); | 
| 415 | 452 | 
| 416 // Unregister the reader. | 453 // Unregister the reader. | 
| 417 ThrottlingController::FinishReaderType finalStep = throttlingController()->r emoveReader(this); | 454 ThrottlingController::FinishReaderType finalStep = ThrottlingController::rem oveReader(executionContext(), this); | 
| 418 | 455 | 
| 419 fireEvent(EventTypeNames::error); | 456 fireEvent(EventTypeNames::error); | 
| 420 fireEvent(EventTypeNames::loadend); | 457 fireEvent(EventTypeNames::loadend); | 
| 421 | 458 | 
| 422 // All possible events have fired and we're done, no more pending activity. | 459 // All possible events have fired and we're done, no more pending activity. | 
| 423 throttlingController()->finishReader(this, finalStep); | 460 ThrottlingController::finishReader(executionContext(), this, finalStep); | 
| 424 } | 461 } | 
| 425 | 462 | 
| 426 void FileReader::fireEvent(const AtomicString& type) | 463 void FileReader::fireEvent(const AtomicString& type) | 
| 427 { | 464 { | 
| 428 if (!m_loader) { | 465 if (!m_loader) { | 
| 429 dispatchEvent(ProgressEvent::create(type, false, 0, 0)); | 466 dispatchEvent(ProgressEvent::create(type, false, 0, 0)); | 
| 430 return; | 467 return; | 
| 431 } | 468 } | 
| 432 | 469 | 
| 433 if (m_loader->totalBytes() >= 0) | 470 if (m_loader->totalBytes() >= 0) | 
| 434 dispatchEvent(ProgressEvent::create(type, true, m_loader->bytesLoaded(), m_loader->totalBytes())); | 471 dispatchEvent(ProgressEvent::create(type, true, m_loader->bytesLoaded(), m_loader->totalBytes())); | 
| 435 else | 472 else | 
| 436 dispatchEvent(ProgressEvent::create(type, false, m_loader->bytesLoaded() , 0)); | 473 dispatchEvent(ProgressEvent::create(type, false, m_loader->bytesLoaded() , 0)); | 
| 437 } | 474 } | 
| 438 | 475 | 
| 439 FileReader::ThrottlingController* FileReader::throttlingController() | |
| 440 { | |
| 441 return FileReader::ThrottlingController::from(executionContext()); | |
| 442 } | |
| 443 | |
| 444 PassRefPtr<ArrayBuffer> FileReader::arrayBufferResult() const | 476 PassRefPtr<ArrayBuffer> FileReader::arrayBufferResult() const | 
| 445 { | 477 { | 
| 446 if (!m_loader || m_error) | 478 if (!m_loader || m_error) | 
| 447 return nullptr; | 479 return nullptr; | 
| 448 return m_loader->arrayBufferResult(); | 480 return m_loader->arrayBufferResult(); | 
| 449 } | 481 } | 
| 450 | 482 | 
| 451 String FileReader::stringResult() | 483 String FileReader::stringResult() | 
| 452 { | 484 { | 
| 453 if (!m_loader || m_error) | 485 if (!m_loader || m_error) | 
| 454 return String(); | 486 return String(); | 
| 455 return m_loader->stringResult(); | 487 return m_loader->stringResult(); | 
| 456 } | 488 } | 
| 457 | 489 | 
| 458 void FileReader::trace(Visitor* visitor) | 490 void FileReader::trace(Visitor* visitor) | 
| 459 { | 491 { | 
| 460 visitor->trace(m_error); | 492 visitor->trace(m_error); | 
| 461 EventTargetWithInlineData::trace(visitor); | 493 EventTargetWithInlineData::trace(visitor); | 
| 462 } | 494 } | 
| 463 | 495 | 
| 464 } // namespace WebCore | 496 } // namespace WebCore | 
| OLD | NEW |