For Developers‎ > ‎Code Reading‎ > ‎

How HTML5's FileReader works

FileReader is an HTML5 API that allows you to read files from JavaScript. This document describes how the API  works. For how to use FileReader, Exploring the FileSystem APIs is a good tutorial.

Disclaimer: the document was originally written in the early January 2012. The contents will be deprecated as Chromium code evolves.


Entering the maze

We'll read through large pieces of code used to read files with FileReader, aiming to understand how the whole thing works. You may wonder how come it is difficult to read files. Shouldn't it be as easy as calling fread() or something? Not really. First of all, no file operations are allowed in the renderer process, so we'll need to forward file read requests to the browser process. This part, IPC, is fairly complex, so we'll skip the IPC part. We'll also skip JavaScript binding part.

In the renderer process

Suppose readAsText() is called from JavaScript code on a file in a HTML5 file system, what's gonna happen in the renderer process?

Lots of things happen in the JavaScript binding layer but we skip this part as mentioned. Eventually, we'll be calling to FileReader::readAsText(), the C++ counterpart of FileReader.readAsText(), in the WebKit code base:

http://trac.webkit.org/browser/trunk/Source/WebCore/fileapi/FileReader.cpp

void FileReader::readAsText(Blob* blob, const String& encoding, ExceptionCode& ec)
{
   ..

   m_encoding = encoding;
   
readInternal(blob, FileReaderLoader::ReadAsText, ec);
}


void FileReader::readInternal(Blob* blob, FileReaderLoader::ReadType type, ExceptionCode& ec)
{
  ..
   m_loader = adoptPtr(
new FileReaderLoader(m_readType, this));
   m_loader->setEncoding(m_encoding);
   m_loader->setDataType(m_blob->type());
   
m_loader->start(scriptExecutionContext(), m_blob.get());
}


m_loader here is a FileReaderLoader, so we are now at:

http://trac.webkit.org/browser/trunk/Source/WebCore/fileapi/FileReaderLoader.cpp

void FileReaderLoader::start(ScriptExecutionContext* scriptExecutionContext, Blob* blob)
{
   // The blob is read by routing through the request handling layer given a temporary public url.                                                                   
   m_urlForReading =
BlobURL::createPublicURL(scriptExecutionContext->securityOrigin());
   if (m_urlForReading.isEmpty()) {
       failed(FileError::SECURITY_ERR);
       return;
   }
   ..

   if (m_client)
       m_loader =
ThreadableLoader::create(scriptExecutionContext, this, request, options);
   ..

}

By the way, The blob URL looks like "blob:http%3A%2F%2Fwww.example.com/66504542-7552-45d3-abfa-7865235427be".

m_client here is not NULL, so we are calling to ThreadableLoader::create():

http://trac.webkit.org/browser/trunk/Source/WebCore/loader/ThreadableLoader.cpp

PassRefPtr ThreadableLoader::create(ScriptExecutionContext* context, ThreadableLoaderClient* client, const ResourceRequest& request, const ThreadableLoaderOptions& options)
{
   ..                                                                                                                                                                                     

   return
DocumentThreadableLoader::create(static_cast(context), client, request, options);
}


In the worker context (i.e. HTML5's Web Worker), a different code path is used, but here, we assume we are not in the worker context so DocumentThreadableLoader::create() is called:

http://trac.webkit.org/browser/trunk/Source/WebCore/loader/DocumentThreadableLoader.cpp

PassRefPtr DocumentThreadableLoader::create(Document* document, ThreadableLoaderClient* client, const ResourceRequest& request, const ThreadableLoaderOptions& options)
{
   RefPtr loader = adoptRef(
new DocumentThreadableLoader(document, client, LoadAsynchronously, request, options));
   if (!loader->m_resource)
       loader = 0;
   return loader.release();
}


Be sure to remember that LoadAsynchronously is set here. The constructor looks like:

DocumentThreadableLoader::DocumentThreadableLoader(Document* document, ThreadableLoaderClient* client, BlockingBehavior blockingBehavior, const ResourceRequest& request, const ThreadableLoaderOptions& options)
   : m_client(client)
   , m_document(document)
   , m_options(options)
   , m_sameOriginRequest(securityOrigin()->canRequest(request.url()))
   , m_async(blockingBehavior == LoadAsynchronously)
#if ENABLE(INSPECTOR)
   , m_preflightRequestIdentifier(0)
#endif
{
   ..
   if (m_sameOriginRequest || m_options.crossOriginRequestPolicy == AllowCrossOriginRequests) {
       
loadRequest(request, DoSecurityCheck);
       return;
   }
}


Lots of things happen in the constructor, but here, a call to loadRequest() is what we are looking for:

void DocumentThreadableLoader::loadRequest(const ResourceRequest& request, SecurityCheckPolicy securityCheck)
{
   ...
   if (
m_async) {
        m_resource =
m_document->cachedResourceLoader()->requestRawResource(newRequest, options);
       if (m_resource)
           m_resource->addClient(this);
       return;
   }

   ..

The code path for asynchronous operations is chosen here, as we set LoadAsynchronously before. Here, cachedResourceLoader() returns CachedResourceLoader, and the requestRawResource() looks like:

http://trac.webkit.org/browser/trunk/Source/WebCore/loader/cache/CachedResourceLoader.cpp

CachedRawResource* CachedResourceLoader::requestRawResource(ResourceRequest& request, const ResourceLoaderOptions& options)
{
   return static_cast(
requestResource(CachedResource::RawResource, request, String(), options, ResourceLoadPriorityUnresolved, false));
}

CachedResource* CachedResourceLoader::requestResource(CachedResource::Type type, ResourceRequest& request, const String& charset, const ResourceLoaderOptions& options, ResourceLoadPriority priority, bool forPreload)
{
   ..


   switch (determineRevalidationPolicy(type, request, forPreload, resource)) {
   
case Load:
       resource = loadResource(type, request, charset, priority, options);
       break;
   ..

}

This function does a lot of things, but we'll eventually be calling loadResource() as we are now trying to load a file. We are now at:

CachedResource*
CachedResourceLoader::loadResource(CachedResource::Type type, ResourceRequest& request, const String& charset, ResourceLoadPriority priority, const ResourceLoaderOptions& options)
{
   ..    
   CachedResource* resource = createResource(type, request, charset);
   ..    
   resource->setLoadPriority(priority);
   
resource->load(this, options);
   ..

}

resource here is a CachedResource, hence we are now at:

http://trac.webkit.org/browser/trunk/Source/WebCore/loader/cache/CachedResource.cpp

void CachedResource::load(CachedResourceLoader* cachedResourceLoader, const ResourceLoaderOptions& options)
{
   ..

   m_loader = resourceLoadScheduler()->scheduleSubresourceLoad(cachedResourceLoader->document()->frame(), this, m_resourceRequest, m_resourceRequest.priority(), options);
   ..

}

Lots of things happen again in this function, but eventually we'll be calling to resourceLoadScheduler():

http://trac.webkit.org/browser/trunk/Source/WebCore/loader/ResourceLoadScheduler.cpp

ResourceLoadScheduler*
resourceLoadScheduler()
{
   ASSERT(isMainThread());
   DEFINE_STATIC_LOCAL(ResourceLoadScheduler, resourceLoadScheduler, ());
   return &resourceLoadScheduler;
}


As shown, this just returns a single ton of ResourceLoadScheduler, and scheduleSubresourceLoad() looks like:

PassRefPtr ResourceLoadScheduler::scheduleSubresourceLoad(Frame* frame, CachedResource* resource, const ResourceRequest& request, ResourceLoadPriority priority, const ResourceLoaderOptions& options)
{
   RefPtr loader = SubresourceLoader::create(frame, resource, request, options);
   if (loader)
       
scheduleLoad(loader.get(), priority);
   return loader.release();
}


void
ResourceLoadScheduler::scheduleLoad(ResourceLoader* resourceLoader, ResourceLoadPriority priority)
{
   ..
   if (priority > ResourceLoadPriorityLow || !resourceLoader->url().protocolInHTTPFamily() || (priority == ResourceLoadPriorityLow && !hadRequests)) {
       // Try to request important resources immediately.                                                                                                                                                                                
       
servePendingRequests(host, priority);
       return;
   }

Here, priority is ResourceLoadPriorityMedium. Why? because the default priority for

http://trac.webkit.org/browser/trunk/Source/WebCore/loader/cache/CachedResource.cpp

static ResourceLoadPriority defaultPriorityForResourceType(CachedResource::Type type)
{
   switch (type) {
       ..
       case
CachedResource::RawResource:
           return
ResourceLoadPriorityMedium;

Anyway, we are now calling to servePendingRequests() which looks like:

void
ResourceLoadScheduler::servePendingRequests(HostInformation* host, ResourceLoadPriority minimumPriority)
{
   ..


   for (int priority = ResourceLoadPriorityHighest; priority >= minimumPriority; --priority) {
       ..


       while (!requestsPending.isEmpty()) {
           ..

           resourceLoader->start();
       }
   }
}

Again, lots of things happen in this function, eventually, we'll be calling to ResourceLoader::start():

http://trac.webkit.org/browser/trunk/Source/WebCore/loader/ResourceLoader.cpp

void ResourceLoader::start()
{
   ..
   if (!m_reachedTerminalState)
       m_handle =
ResourceHandle::create(m_frame->loader()->networkingContext(), m_request, this, m_defersLoading, m_options.sniffContent == SniffContent);
}


Now we are creating yet another indirection, which is ResourceHandle:

http://trac.webkit.org/browser/trunk/Source/WebKit/chromium/src/ResourceHandle.cpp

PassRefPtr ResourceHandle::create(NetworkingContext* context,
                                                 const ResourceRequest& request,
                                                 ResourceHandleClient* client,
                                                 bool defersLoading,
                                                 bool shouldContentSniff)
{
   RefPtr newHandle = adoptRef(
new ResourceHandle(
       request, client, defersLoading, shouldContentSniff));

   if (
newHandle->start(context))
       return newHandle.release();

   return 0;
}


The ResourceHandle::start() looks lke:

bool ResourceHandle::start(NetworkingContext* context)
{
   d->start();
   return true;
}


Wait! What's 'd'? Well, this is a member variable, which is a pointer to a ResourceHandleInternal object. Regardless of whether the variable name is good or not, we are now at:

void
ResourceHandleInternal::start()
{
   ..

   m_loader->loadAsynchronously(wrappedRequest, this);
}


Here, m_loader is an object of   WebURLLoaderImpl

http://src.chromium.org/viewvc/chrome/trunk/src/webkit/glue/weburlloader_impl.cc?view=log

void WebURLLoaderImpl::loadAsynchronously(const WebURLRequest& request,
                                         WebURLLoaderClient* client) {
 DCHECK(!context_->client());

 context_->set_client(client);
 
context_->Start(request, NULL, platform_);
}


Here, context_ is a WebURLLoaderImpl::Context object. By the way, did you noticed that we are now in Chromim's tree, not in WebKit? Start() looks like:

void
WebURLLoaderImpl::Context::Start(
   const WebURLRequest& request,
   ResourceLoaderBridge::SyncLoadResponse* sync_load_response,
   WebKitPlatformSupportImpl* platform) {
 ..


 return dispatcher_->message_sender()->Send(
     
new ResourceHostMsg_RequestResource(routing_id_, request_id_, request_));
}


This function is huge, but we'll be eventually issuing an IPC call named ResourceHostMsg_RequestResource. As mentioned before, we'll skip the IPC part. In case you are interested, message_sender() is an object of RenderThreadImpl. You can continue reading through the IPC code as a homework.

Now in the browser process

So far, we've seen lots of things happened in the renderer process. We are now in the browser proces. The ResourceHostMsg_RequestResource IPC request is handled here:

http://src.chromium.org/viewvc/chrome/trunk/src/content/browser/renderer_host/resource_dispatcher_host.h?view=markup

bool
ResourceDispatcherHost::OnMessageReceived(const IPC::Message& message,
                                              ResourceMessageFilter* filter,
                                              bool* message_was_ok) {
 filter_ = filter;
 bool handled = true;
 IPC_BEGIN_MESSAGE_MAP_EX(ResourceDispatcherHost, message, *message_was_ok)
   IPC_MESSAGE_HANDLER(
ResourceHostMsg_RequestResource, OnRequestResource)

Which dispatches the request to:

void
ResourceDispatcherHost::OnRequestResource(
   const IPC::Message& message,
   int request_id,
   const ResourceHostMsg_Request& request_data) {
 
BeginRequest(request_id, request_data, NULL, message.routing_id());
}


void ResourceDispatcherHost::BeginRequest(
   int request_id,
   const ResourceHostMsg_Request& request_data,
   IPC::Message* sync_result,  // only valid for sync                                                                                          
   int route_id) {
 ..

 if (deferred_request) {
   ..
 } else {
   BeginRequestInternal(request);
 }
}

BeginRequest() is more than 200-line long, and not only that, it's calling to a helper function which looks like:

void ResourceDispatcherHost::BeginRequestInternal(net::URLRequest* request) {
 ..

 if (!defer_start)
   
InsertIntoResourceQueue(request, *info);

Now we are inserting the request to a queue:

void ResourceDispatcherHost::InsertIntoResourceQueue(
   net::URLRequest* request,
   const ResourceDispatcherHostRequestInfo& request_info) {
 
resource_queue_.AddRequest(request, request_info);

 // Make sure we have the load state monitor running                                                                                                                      
 if (!update_load_states_timer_.IsRunning()) {
   update_load_states_timer_.Start(FROM_HERE,
       TimeDelta::FromMilliseconds(kUpdateLoadStatesIntervalMsec),
       this, &ResourceDispatcherHost::UpdateLoadStates);
 }
}

The requests in the queue will eventually be processed:

http://src.chromium.org/viewvc/chrome/trunk/src/content/browser/renderer_host/resource_queue.cc?view=markup

void ResourceQueue::AddRequest(
   net::URLRequest* request,
   const ResourceDispatcherHostRequestInfo& request_info) {
 ..


 if (interested_delegates.empty()) {
   
request->Start();
   return;
 }
 ..

}

Here, request is a URLRequest with StartJob() function looking like:

http://src.chromium.org/viewvc/chrome/trunk/src/net/url_request/url_request.cc?view=markup

void URLRequest::StartJob(URLRequestJob* job) {
 ..                                                                                                        
 
job_->Start();
}


Here, job_ is a BlobURLRequestJob object, and Start() looks like:

http://src.chromium.org/viewvc/chrome/trunk/src/webkit/blob/blob_url_request_job.cc?view=markup

void
BlobURLRequestJob::Start() {
 // Continue asynchronously.                                                                                                                   
 MessageLoop::current()->PostTask(
     FROM_HERE,
     base::Bind(&
BlobURLRequestJob::DidStart, weak_factory_.GetWeakPtr()));
}


void BlobURLRequestJob::DidStart() {
 // We only support GET request per the spec.                                                                                                                             
 if (request()->method() != "GET") {
   NotifyFailure(net::ERR_METHOD_NOT_SUPPORTED);
   return;
 }

 // If the blob data is not present, bail out.                                                                                                                            
 if (!blob_data_) {
   NotifyFailure(net::ERR_FILE_NOT_FOUND);
   return;
 }

 
CountSize();
}

CountSize() sounds like a simple function that just counts things, but in reality, it does a lot more than that:

void
BlobURLRequestJob::CountSize() {
 for (; item_index_ < blob_data_->items().size(); ++item_index_) {
   ..
   // If there is a file item, do the resolving.                                                                                               
   if (item.type ==
BlobData::TYPE_FILE) {
     
ResolveFile(item.file_path);
     return;
   }
 ..

}

Now we are reading with a File type blob, so ResolveFile() is called:

void
BlobURLRequestJob::ResolveFile(const FilePath& file_path) {
 
base::FileUtilProxy::GetFileInfo(
       file_thread_proxy_, file_path,
       base::Bind(&
BlobURLRequestJob::DidResolve, weak_factory_.GetWeakPtr()));
}


GetFileInfo() is called in the FILE thread, which looks like:

http://src.chromium.org/viewvc/chrome/trunk/src/base/file_util_proxy.cc?view=markup

// Retrieves the information about a file. It is invalid to pass NULL for the                                                                   
// callback.                                                                                                                                    
bool
FileUtilProxy::GetFileInfo(
   scoped_refptr message_loop_proxy,
   const FilePath& file_path,
   const GetFileInfoCallback& callback) {
 GetFileInfoHelper* helper = new GetFileInfoHelper;
 return message_loop_proxy->
PostTaskAndReply(
       FROM_HERE,
       Bind(&GetFileInfoHelper::
RunWorkForFilePath,
            Unretained(helper), file_path),
Bind(&GetFileInfoHelper::Reply, Owned(helper), callback));
}


class
GetFileInfoHelper {
public:
 void
RunWorkForFilePath(const FilePath& file_path) {
   if (!file_util::PathExists(file_path)) {
     error_ = PLATFORM_FILE_ERROR_NOT_FOUND;
     return;
   }
   if (!
file_util::GetFileInfo(file_path, &file_info_))
     error_ = PLATFORM_FILE_ERROR_FAILED;
 }
 void Reply(const FileUtilProxy::GetFileInfoCallback& callback) {
   if (!callback.is_null()) {
     
callback.Run(error_, file_info_);
   }
 }


Here, we get the file info with file_util::GetFileInfo() in the FILE thread, and calling the callback function in the original thread, which is IO thread. The callback is BlobURLRequestJob::DidResolve():

void BlobURLRequestJob::DidResolve(base::PlatformFileError rv,
                                  const base::PlatformFileInfo& file_info) {
 ..


 // Continue counting the size for the remaining items.                                                                                                                   
 item_index_++;
 
CountSize();
}


Which again calls CountSize() to continue the business, which ends with a call to NotifySuccess():

void BlobURLRequestJob::CountSize() {
 ..
 
NotifySuccess();
}


void BlobURLRequestJob::NotifySuccess() {
 ..

 HeadersCompleted(status_code, status_text);
}


void
BlobURLRequestJob::HeadersCompleted(int status_code,
                                        const std::string& status_text) {
 ..
 
NotifyHeadersComplete();
}

NotifyHeadersComplete is defined in the base class, which is URLRequestJob:


void URLRequestJob::NotifyHeadersComplete() {
 ..                                                                                              
 request_->
NotifyResponseStarted();
}

Here, request_ is a URLRequest,

void URLRequest::NotifyResponseStarted() {
 ..
     delegate_->
OnResponseStarted(this);
}

Here, delegate_ is an object of ResourceDispatcherHost and the function looks like:

void ResourceDispatcherHost::OnResponseStarted(net::URLRequest* request) {
  ..

  StartReading(request);
  ..
}


void
ResourceDispatcherHost::StartReading(net::URLRequest* request) {
 // Start reading.                                                                                                                             
 int bytes_read = 0;
 if (
Read(request, &bytes_read)) {
   ..

}

bool
ResourceDispatcherHost::Read(net::URLRequest* request, int* bytes_read) {
..
 return request->
Read(buf, buf_size, bytes_read);
}


We are now back to URLRequest again:

bool
URLRequest::Read(IOBuffer* dest, int dest_size, int* bytes_read) {
 ..

 bool rv = job_->Read(dest, dest_size, bytes_read);
 ..

 return rv;
}


Here, job_ is URLRequestObject, so we are back to again:

bool URLRequestJob::Read(IOBuffer* buf, int buf_size, int *bytes_read) {
 ..                                                                                                                                       
 if (!filter_.get()) {
   rv =
ReadRawDataHelper(buf, buf_size, bytes_read);


bool
URLRequestJob::ReadRawDataHelper(IOBuffer* buf, int buf_size,
                                     int* bytes_read) {
 ..

 bool rv = ReadRawData(buf, buf_size, bytes_read);
}


ReadRawData() is implemented in a sub class, which is BlobURLRequestJob here:

bool
BlobURLRequestJob::ReadRawData(net::IOBuffer* dest,
                                   int dest_size,
                                   int* bytes_read) {
 ..


 return
ReadLoop(bytes_read);
}


bool
BlobURLRequestJob::ReadLoop(int* bytes_read) {
 // Read until we encounter an error or could not get the data immediately.                                                                    
 while (remaining_bytes_ > 0 && read_buf_remaining_bytes_ > 0) {
   if (!
ReadItem())
     return false;
 }

 *bytes_read = ReadCompleted();
 return true;
}


Blobs are chunked to multiple items, so we read through all items with a loop, by calling to ReadItem() repeatedly:

bool
BlobURLRequestJob::ReadItem() {
 ..

 switch (item.type) {
   case BlobData::TYPE_DATA:
     return ReadBytes(item);
   case
BlobData::TYPE_FILE:
     return
DispatchReadFile(item);
   default:
     DCHECK(false);
     return false;
 }
}


bool
BlobURLRequestJob::DispatchReadFile(const BlobData::Item& item) {
 // If the stream already exists, keep reading from it.                                                                                        
 if (stream_ != NULL)
   return ReadFile(item);

 base::
FileUtilProxy::CreateOrOpen(
     file_thread_proxy_, item.file_path, kFileOpenFlags,
     base::Bind(&
BlobURLRequestJob::DidOpen, weak_factory_.GetWeakPtr()));
 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
 return false;
}


First time, DispatchReadFile() is called, stream_ is NULL, so FileUtilProxy::CreateOrOpen() is called to open a file via a callback function, and then read:

void
BlobURLRequestJob::DidOpen(base::PlatformFileError rv,
                               base::PassPlatformFile file,
                               bool created) {
 ..
 
ReadFile(item);
}

Finally we are reading that file!


bool
BlobURLRequestJob::ReadFile(const BlobData::Item& item) {
 ..


 // Start the asynchronous reading.                                                                                                            
 int rv =
stream_->Read(read_buf_->data() + read_buf_offset_,
                        bytes_to_read_,
                        base::Bind(&BlobURLRequestJob::DidRead,
                                   base::Unretained(this)));

Finally, we've reached at the point  that reads contents of the file! Here, we are using net::FileStream::Read() in an asynchronous fashion, on the IO thread.

Back to the renderer process

The browser process is now ready to return the response to the renderer process. We omit the details here, but the browser process returns the response by issuing three IPC calls:
  1. ResourceMsg_ReceivedResponse - notifies the result to the renderer
  2. ResourceMsg_DataReceived - transfers the data to the renderer
  3. ResourceMsg_RequestComplete - notifies that the request is complete.


Let's start from the step 2, as the step 1 is not so interesting. The message is first handled at:

http://src.chromium.org/viewvc/chrome/trunk/src/content/common/resource_dispatcher.cc?view=markup

void ResourceDispatcher::DispatchMessage(const IPC::Message& message) {
 IPC_BEGIN_MESSAGE_MAP(ResourceDispatcher, message)

   ..
   IPC_MESSAGE_HANDLER(ResourceMsg_DataReceived, OnReceivedData)
   ..
   IPC_END_MESSAGE_MAP()
}


Which calls to

void ResourceDispatcher::OnReceivedData(const IPC::Message& message,
                                       int request_id,
                                       base::SharedMemoryHandle shm_handle,
                                       int data_len,
                                       int encoded_data_length) {
 ..
 if (data_len > 0 && shared_mem.Map(data_len)) {
   
const char* data = static_cast(shared_mem.memory());
   request_info->peer->OnReceivedData(data, data_len, encoded_data_length);
 }
}


As shown, data is transferred via shared memory, pointed by shared_mem.memory(). Here, peer is WebURLLoaderImpl::Context, hence OnReceivedData() looks like:

void WebURLLoaderImpl::Context::OnReceivedData(const char* data,
                                              int data_length,
                                              int encoded_data_length) {
  ..

 if (ftp_listing_delegate_.get()) {
   // The FTP listing delegate will make the appropriate calls to                                                                                                         
   // client_->didReceiveData and client_->didReceiveResponse.                                                                                                            
   ftp_listing_delegate_->OnReceivedData(data, data_length);
 } else if (multipart_delegate_.get()) {
   // The multipart delegate will make the appropriate calls to                                                                                                           
   // client_->didReceiveData and client_->didReceiveResponse.                                                                                                            
   multipart_delegate_->OnReceivedData(data, data_length, encoded_data_length);
 } else {
   
client_->didReceiveData(loader_, data, data_length, encoded_data_length);
 }
}


Here, client is a ResourceLoader:

http://trac.webkit.org/browser/trunk/Source/WebCore/loader/ResourceLoader.cpp

void
ResourceLoader::didReceiveData(ResourceHandle*, const char* data, int length, int encodedDataLength)
{
   InspectorInstrumentationCookie cookie = InspectorInstrumentation::willReceiveResourceData(m_frame.get(), identifier());
   
didReceiveData(data, length, encodedDataLength, false);
   InspectorInstrumentation::didReceiveResourceData(cookie);
}


didReceiveData() is implemented in a sub class, which is SubresourceLoader:

http://trac.webkit.org/browser/trunk/Source/WebCore/loader/SubresourceLoader.cpp

void
SubresourceLoader::didReceiveData(const char* data, int length, long long encodedDataLength, bool allAtOnce)
{
   ..
   if (!m_loadingMultipartContent)
      
 sendDataToResource(data, length);
}


void
SubresourceLoader::sendDataToResource(const char* data, int length)
{
   ..                                                                                             
   if (m_loadingMultipartContent || !resourceData()) {
       RefPtr copiedData = SharedBuffer::create(data, length);
       
m_resource->data(copiedData.release(), m_loadingMultipartContent);
   } else
a


       m_resource->data(resourceData(), false);
}


Here, m_resource is CachedResource. Do you remember we saw this class before? data() looks like:

void
CachedResource::data(PassRefPtr, bool allDataReceived)
{
   if (!allDataReceived)
       return;
   
   setLoading(false);
   
checkNotify();
}


First time it's called, allDataReceived is false.The function is then called again, once the renderer receives ResourceMsg_RequestCompletel. Hence, checkNotify() will be called:

void CachedResource::checkNotify()
{
   if (isLoading())
       return;

   CachedResourceClientWalker w(m_clients);
   while (CachedResourceClient* c = w.next())
c->
notifyFinished(this);
}

Here, c is DocumentThreadableLoader (we saw this before btw), and notifyFinished() looks like:

void
DocumentThreadableLoader::notifyFinished(CachedResource* resource)
{
   ..
   if (m_actualRequest) {
       ASSERT(!m_sameOriginRequest);
       ASSERT(m_options.crossOriginRequestPolicy == UseAccessControl);
       preflightSuccess();
   } else
       
m_client->didFinishLoading(identifier, finishTime);
}


Here, m_client is a FileReaderLoader object:

void
FileReaderLoader::didFinishLoading(unsigned long, double)
{
   cleanup();
   if (m_client)
       
m_client->didFinishLoading();
}


And m_client here is a FileReader object. Do you remember that FileReader is where we started? The function looks like:

void FileReader::didFinishLoading
()
{
   m_state = DONE;

   
fireEvent(eventNames().loadEvent);
   
fireEvent(eventNames().loadendEvent);
}


void FileReader::fireEvent(const AtomicString& type)
{
   
dispatchEvent(ProgressEvent::create(type, true, m_loader ? m_loader->bytesLoaded() : 0, m_loader ? m_loader->totalBytes() : 0));
}


dispatchEvent() is implemented in the base class, which is EventTarget:

http://trac.webkit.org/browser/trunk/Source/WebCore/dom/EventTarget.cpp

bool
EventTarget::dispatchEvent(PassRefPtr event)
{
   event->setTarget(this);
   event->setCurrentTarget(this);
   event->setEventPhase(Event::AT_TARGET);
   bool defaultPrevented =
fireEventListeners(event.get());
   event->setEventPhase(0);
   return defaultPrevented;
}


bool EventTarget::fireEventListeners(Event* event)
{
   ..
   if (listenerVector)
       
fireEventListeners(event, d, *listenerVector);
}
       
void
EventTarget::fireEventListeners(Event* event, EventTargetData* d, EventListenerVector& entry)
{
   ..
   for ( ; i < end; ++i) {
       ..                                                                                  
       
registeredListener.listener->handleEvent(scriptExecutionContext(), event);
   }
   ..

}

Here, listener is a V8AbstractEventListener object, which looks like:

void V8AbstractEventListener::handleEvent(ScriptExecutionContext* context, Event* event)
{
   ..

  invokeEventHandler(context, event, jsEvent);
}


Woohoo, we are now  in V8, the entrance to the JavaScript world.

Back to the JavaScript world

Finally onload event is delivered to the onload handler associated with a FileReader object in JavaScript, and your JavaScript code receives the file contents, and we are done.
Comments