| Index: ppapi/proxy/url_loader_resource.cc
|
| diff --git a/ppapi/proxy/url_loader_resource.cc b/ppapi/proxy/url_loader_resource.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..5ee8d950d5646bdc044fb560e19ed25c0151c12f
|
| --- /dev/null
|
| +++ b/ppapi/proxy/url_loader_resource.cc
|
| @@ -0,0 +1,392 @@
|
| +// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "ppapi/proxy/url_loader_resource.h"
|
| +
|
| +#include "base/logging.h"
|
| +#include "ppapi/c/pp_completion_callback.h"
|
| +#include "ppapi/c/pp_errors.h"
|
| +#include "ppapi/c/ppb_url_loader.h"
|
| +#include "ppapi/proxy/dispatch_reply_message.h"
|
| +#include "ppapi/proxy/ppapi_messages.h"
|
| +#include "ppapi/proxy/ppb_file_ref_proxy.h"
|
| +#include "ppapi/proxy/url_request_info_resource.h"
|
| +#include "ppapi/proxy/url_response_info_resource.h"
|
| +#include "ppapi/shared_impl/ppapi_globals.h"
|
| +#include "ppapi/shared_impl/url_response_info_data.h"
|
| +#include "ppapi/thunk/enter.h"
|
| +#include "ppapi/thunk/resource_creation_api.h"
|
| +
|
| +using ppapi::thunk::EnterResourceNoLock;
|
| +using ppapi::thunk::PPB_URLLoader_API;
|
| +using ppapi::thunk::PPB_URLRequestInfo_API;
|
| +
|
| +#ifdef _MSC_VER
|
| +// Do not warn about use of std::copy with raw pointers.
|
| +#pragma warning(disable : 4996)
|
| +#endif
|
| +
|
| +namespace ppapi {
|
| +namespace proxy {
|
| +
|
| +URLLoaderResource::URLLoaderResource(Connection connection,
|
| + PP_Instance instance)
|
| + : PluginResource(connection, instance),
|
| + mode_(MODE_WAITING_TO_OPEN),
|
| + status_callback_(NULL),
|
| + bytes_sent_(0),
|
| + total_bytes_to_be_sent_(-1),
|
| + bytes_received_(0),
|
| + total_bytes_to_be_received_(-1),
|
| + user_buffer_(NULL),
|
| + user_buffer_size_(0),
|
| + done_status_(PP_OK_COMPLETIONPENDING),
|
| + is_streaming_to_file_(false),
|
| + is_asynchronous_load_suspended_(false) {
|
| + SendCreate(RENDERER, PpapiHostMsg_URLLoader_Create());
|
| +}
|
| +
|
| +URLLoaderResource::URLLoaderResource(Connection connection,
|
| + PP_Instance instance,
|
| + int pending_main_document_loader_id,
|
| + const ppapi::URLResponseInfoData& data)
|
| + : PluginResource(connection, instance),
|
| + mode_(MODE_OPENING),
|
| + status_callback_(NULL),
|
| + bytes_sent_(0),
|
| + total_bytes_to_be_sent_(-1),
|
| + bytes_received_(0),
|
| + total_bytes_to_be_received_(-1),
|
| + user_buffer_(NULL),
|
| + user_buffer_size_(0),
|
| + done_status_(PP_OK_COMPLETIONPENDING),
|
| + is_streaming_to_file_(false),
|
| + is_asynchronous_load_suspended_(false) {
|
| + AttachToPendingHost(RENDERER, pending_main_document_loader_id);
|
| + SaveResponseInfo(data);
|
| +}
|
| +
|
| +URLLoaderResource::~URLLoaderResource() {
|
| +}
|
| +
|
| +PPB_URLLoader_API* URLLoaderResource::AsPPB_URLLoader_API() {
|
| + return this;
|
| +}
|
| +
|
| +int32_t URLLoaderResource::Open(PP_Resource request_id,
|
| + scoped_refptr<TrackedCallback> callback) {
|
| + EnterResourceNoLock<PPB_URLRequestInfo_API> enter_request(request_id, true);
|
| + if (enter_request.failed()) {
|
| + Log(PP_LOGLEVEL_ERROR,
|
| + "PPB_URLLoader.Open: invalid request resource ID. (Hint to C++ wrapper"
|
| + " users: use the ResourceRequest constructor that takes an instance or"
|
| + " else the request will be null.)");
|
| + return PP_ERROR_BADARGUMENT;
|
| + }
|
| + return Open(enter_request.object()->GetData(), 0, callback);
|
| +}
|
| +
|
| +int32_t URLLoaderResource::Open(
|
| + const ::ppapi::URLRequestInfoData& request_data,
|
| + int requestor_pid,
|
| + scoped_refptr<TrackedCallback> callback) {
|
| + int32_t rv = ValidateCallback(callback);
|
| + if (rv != PP_OK)
|
| + return rv;
|
| + if (mode_ != MODE_WAITING_TO_OPEN)
|
| + return PP_ERROR_INPROGRESS;
|
| +
|
| + request_data_ = request_data;
|
| +
|
| + mode_ = MODE_OPENING;
|
| + is_asynchronous_load_suspended_ = false;
|
| +
|
| + RegisterCallback(callback);
|
| + Post(RENDERER, PpapiHostMsg_URLLoader_Open(request_data));
|
| + return PP_OK_COMPLETIONPENDING;
|
| +}
|
| +
|
| +int32_t URLLoaderResource::FollowRedirect(
|
| + scoped_refptr<TrackedCallback> callback) {
|
| + int32_t rv = ValidateCallback(callback);
|
| + if (rv != PP_OK)
|
| + return rv;
|
| + if (mode_ != MODE_OPENING)
|
| + return PP_ERROR_INPROGRESS;
|
| +
|
| + SetDefersLoading(false); // Allow the redirect to continue.
|
| + RegisterCallback(callback);
|
| + return PP_OK_COMPLETIONPENDING;
|
| +}
|
| +
|
| +PP_Bool URLLoaderResource::GetUploadProgress(int64_t* bytes_sent,
|
| + int64_t* total_bytes_to_be_sent) {
|
| + if (!request_data_.record_upload_progress) {
|
| + *bytes_sent = 0;
|
| + *total_bytes_to_be_sent = 0;
|
| + return PP_FALSE;
|
| + }
|
| + *bytes_sent = bytes_sent_;
|
| + *total_bytes_to_be_sent = total_bytes_to_be_sent_;
|
| + return PP_TRUE;
|
| +}
|
| +
|
| +PP_Bool URLLoaderResource::GetDownloadProgress(
|
| + int64_t* bytes_received,
|
| + int64_t* total_bytes_to_be_received) {
|
| + if (!request_data_.record_download_progress) {
|
| + *bytes_received = 0;
|
| + *total_bytes_to_be_received = 0;
|
| + return PP_FALSE;
|
| + }
|
| + *bytes_received = bytes_received_;
|
| + *total_bytes_to_be_received = total_bytes_to_be_received_;
|
| + return PP_TRUE;
|
| +}
|
| +
|
| +PP_Resource URLLoaderResource::GetResponseInfo() {
|
| + if (response_info_.get())
|
| + return response_info_->GetReference();
|
| + return 0;
|
| +}
|
| +
|
| +int32_t URLLoaderResource::ReadResponseBody(
|
| + void* buffer,
|
| + int32_t bytes_to_read,
|
| + scoped_refptr<TrackedCallback> callback) {
|
| + int32_t rv = ValidateCallback(callback);
|
| + if (rv != PP_OK)
|
| + return rv;
|
| + if (!response_info_.get() ||
|
| + !response_info_->data().body_as_file_ref.resource.is_null())
|
| + return PP_ERROR_FAILED;
|
| + if (bytes_to_read <= 0 || !buffer)
|
| + return PP_ERROR_BADARGUMENT;
|
| +
|
| + user_buffer_ = static_cast<char*>(buffer);
|
| + user_buffer_size_ = bytes_to_read;
|
| +
|
| + if (!buffer_.empty())
|
| + return FillUserBuffer();
|
| +
|
| + // We may have already reached EOF.
|
| + if (done_status_ != PP_OK_COMPLETIONPENDING) {
|
| + user_buffer_ = NULL;
|
| + user_buffer_size_ = 0;
|
| + return done_status_;
|
| + }
|
| +
|
| + RegisterCallback(callback);
|
| + return PP_OK_COMPLETIONPENDING;
|
| +}
|
| +
|
| +int32_t URLLoaderResource::FinishStreamingToFile(
|
| + scoped_refptr<TrackedCallback> callback) {
|
| + int32_t rv = ValidateCallback(callback);
|
| + if (rv != PP_OK)
|
| + return rv;
|
| + if (!response_info_.get() ||
|
| + response_info_->data().body_as_file_ref.resource.is_null())
|
| + return PP_ERROR_FAILED;
|
| +
|
| + // We may have already reached EOF.
|
| + if (done_status_ != PP_OK_COMPLETIONPENDING)
|
| + return done_status_;
|
| +
|
| + is_streaming_to_file_ = true;
|
| + if (is_asynchronous_load_suspended_)
|
| + SetDefersLoading(false);
|
| +
|
| + // Wait for didFinishLoading / didFail.
|
| + RegisterCallback(callback);
|
| + return PP_OK_COMPLETIONPENDING;
|
| +}
|
| +
|
| +void URLLoaderResource::Close() {
|
| + mode_ = MODE_LOAD_COMPLETE;
|
| + done_status_ = PP_ERROR_ABORTED;
|
| +
|
| + Post(RENDERER, PpapiHostMsg_URLLoader_Close());
|
| +
|
| + // Abort the callbacks, the plugin doesn't want to be called back after this.
|
| + // TODO(brettw) this should fix bug 69457, mark it fixed. ============
|
| + if (TrackedCallback::IsPending(pending_callback_))
|
| + pending_callback_->PostAbort();
|
| +}
|
| +
|
| +void URLLoaderResource::GrantUniversalAccess() {
|
| + Post(RENDERER, PpapiHostMsg_URLLoader_GrantUniversalAccess());
|
| +}
|
| +
|
| +void URLLoaderResource::RegisterStatusCallback(
|
| + PP_URLLoaderTrusted_StatusCallback callback) {
|
| + status_callback_ = callback;
|
| +}
|
| +
|
| +void URLLoaderResource::OnReplyReceived(
|
| + const ResourceMessageReplyParams& params,
|
| + const IPC::Message& msg) {
|
| + IPC_BEGIN_MESSAGE_MAP(URLLoaderResource, msg)
|
| + case PpapiPluginMsg_URLLoader_SendData::ID:
|
| + // Special message, manually dispatch since we don't want the automatic
|
| + // unpickling.
|
| + OnPluginMsgSendData(params, msg);
|
| + break;
|
| +
|
| + PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
|
| + PpapiPluginMsg_URLLoader_ReceivedResponse,
|
| + OnPluginMsgReceivedResponse)
|
| + PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
|
| + PpapiPluginMsg_URLLoader_FinishedLoading,
|
| + OnPluginMsgFinishedLoading)
|
| + PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
|
| + PpapiPluginMsg_URLLoader_UpdateProgress,
|
| + OnPluginMsgUpdateProgress)
|
| + IPC_END_MESSAGE_MAP()
|
| +}
|
| +
|
| +void URLLoaderResource::OnPluginMsgReceivedResponse(
|
| + const ResourceMessageReplyParams& params,
|
| + const URLResponseInfoData& data) {
|
| + SaveResponseInfo(data);
|
| + RunCallback(PP_OK);
|
| +}
|
| +
|
| +void URLLoaderResource::OnPluginMsgSendData(
|
| + const ResourceMessageReplyParams& params,
|
| + const IPC::Message& message) {
|
| + PickleIterator iter(message);
|
| + const char* data;
|
| + int data_length;
|
| + if (!iter.ReadData(&data, &data_length)) {
|
| + NOTREACHED() << "Expecting data";
|
| + return;
|
| + }
|
| +
|
| + mode_ = MODE_STREAMING_DATA;
|
| + buffer_.insert(buffer_.end(), data, data + data_length);
|
| +
|
| + // To avoid letting the network stack download an entire stream all at once,
|
| + // defer loading when we have enough buffer.
|
| + // Check for this before we run the callback, even though that could move
|
| + // data out of the buffer. Doing anything after the callback is unsafe.
|
| + DCHECK(request_data_.prefetch_buffer_lower_threshold <
|
| + request_data_.prefetch_buffer_upper_threshold);
|
| + if (!is_streaming_to_file_ &&
|
| + !is_asynchronous_load_suspended_ &&
|
| + (buffer_.size() >= static_cast<size_t>(
|
| + request_data_.prefetch_buffer_upper_threshold))) {
|
| + DVLOG(1) << "Suspending async load - buffer size: " << buffer_.size();
|
| + SetDefersLoading(true);
|
| + }
|
| +
|
| + if (user_buffer_)
|
| + RunCallback(FillUserBuffer());
|
| + else
|
| + DCHECK(!TrackedCallback::IsPending(pending_callback_));
|
| +}
|
| +
|
| +void URLLoaderResource::OnPluginMsgFinishedLoading(
|
| + const ResourceMessageReplyParams& params,
|
| + int32_t result) {
|
| + mode_ = MODE_LOAD_COMPLETE;
|
| + done_status_ = result;
|
| + user_buffer_ = NULL;
|
| + user_buffer_size_ = 0;
|
| +
|
| + // If the client hasn't called any function that takes a callback since
|
| + // the initial call to Open, or called ReadResponseBody and got a
|
| + // synchronous return, then the callback will be NULL.
|
| + if (TrackedCallback::IsPending(pending_callback_))
|
| + RunCallback(done_status_);
|
| +}
|
| +
|
| +void URLLoaderResource::OnPluginMsgUpdateProgress(
|
| + const ResourceMessageReplyParams& params,
|
| + int64_t bytes_sent,
|
| + int64_t total_bytes_to_be_sent,
|
| + int64_t bytes_received,
|
| + int64_t total_bytes_to_be_received) {
|
| + bytes_sent_ = bytes_sent;
|
| + total_bytes_to_be_sent_ = total_bytes_to_be_sent;
|
| + bytes_received_ = bytes_received;
|
| + total_bytes_to_be_received_ = total_bytes_to_be_received;
|
| +
|
| + if (status_callback_)
|
| + status_callback_(pp_instance(), pp_resource(),
|
| + bytes_sent_, total_bytes_to_be_sent_,
|
| + bytes_received_, total_bytes_to_be_received_);
|
| +}
|
| +
|
| +void URLLoaderResource::SetDefersLoading(bool defers_loading) {
|
| + Post(RENDERER, PpapiHostMsg_URLLoader_SetDeferLoading(defers_loading));
|
| +}
|
| +
|
| +int32_t URLLoaderResource::ValidateCallback(
|
| + scoped_refptr<TrackedCallback> callback) {
|
| + DCHECK(callback);
|
| + if (TrackedCallback::IsPending(pending_callback_))
|
| + return PP_ERROR_INPROGRESS;
|
| + return PP_OK;
|
| +}
|
| +
|
| +void URLLoaderResource::RegisterCallback(
|
| + scoped_refptr<TrackedCallback> callback) {
|
| + DCHECK(!TrackedCallback::IsPending(pending_callback_));
|
| + pending_callback_ = callback;
|
| +}
|
| +
|
| +void URLLoaderResource::RunCallback(int32_t result) {
|
| + // This may be null when this is a main document loader.
|
| + if (!pending_callback_.get())
|
| + return;
|
| +
|
| + // If |user_buffer_| was set as part of registering a callback, the paths
|
| + // which trigger that callack must have cleared it since the callback is now
|
| + // free to delete it.
|
| + DCHECK(!user_buffer_);
|
| +
|
| + // As a second line of defense, clear the |user_buffer_| in case the
|
| + // callbacks get called in an unexpected order.
|
| + user_buffer_ = NULL;
|
| + user_buffer_size_ = 0;
|
| + pending_callback_->Run(result);
|
| +}
|
| +
|
| +void URLLoaderResource::SaveResponseInfo(const URLResponseInfoData& data) {
|
| + // Create a proxy resource for the the file ref host resource if needed.
|
| + PP_Resource body_as_file_ref = 0;
|
| + if (!data.body_as_file_ref.resource.is_null()) {
|
| + thunk::EnterResourceCreationNoLock enter(pp_instance());
|
| + body_as_file_ref =
|
| + enter.functions()->CreateFileRef(data.body_as_file_ref);
|
| + }
|
| + response_info_ = new URLResponseInfoResource(
|
| + connection(), pp_instance(), data, body_as_file_ref);
|
| +}
|
| +
|
| +size_t URLLoaderResource::FillUserBuffer() {
|
| + DCHECK(user_buffer_);
|
| + DCHECK(user_buffer_size_);
|
| +
|
| + size_t bytes_to_copy = std::min(buffer_.size(), user_buffer_size_);
|
| + std::copy(buffer_.begin(), buffer_.begin() + bytes_to_copy, user_buffer_);
|
| + buffer_.erase(buffer_.begin(), buffer_.begin() + bytes_to_copy);
|
| +
|
| + // If the buffer is getting too empty, resume asynchronous loading.
|
| + if (is_asynchronous_load_suspended_ &&
|
| + buffer_.size() <= static_cast<size_t>(
|
| + request_data_.prefetch_buffer_lower_threshold)) {
|
| + DVLOG(1) << "Resuming async load - buffer size: " << buffer_.size();
|
| + SetDefersLoading(false);
|
| + }
|
| +
|
| + // Reset for next time.
|
| + user_buffer_ = NULL;
|
| + user_buffer_size_ = 0;
|
| + return bytes_to_copy;
|
| +}
|
| +
|
| +} // namespace proxy
|
| +} // namespace ppapi
|
|
|