// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This example shows how to use the URLLoader in streaming mode (reading to
// memory as data comes over the network). This example uses PostMessage between
// the plugin and the url_loader.html page in this directory to start the load
// and to communicate the result.
//
// The other mode is to stream to a file instead. See stream_to_file.cc
#include <stdint.h>
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/url_loader.h"
#include "ppapi/cpp/url_request_info.h"
#include "ppapi/cpp/url_response_info.h"
#include "ppapi/utility/completion_callback_factory.h"
// When compiling natively on Windows, PostMessage can be #define-d to
// something else.
#ifdef PostMessage
#undef PostMessage
#endif
// Buffer size for reading network data.
const int kBufSize = 1024;
class MyInstance : public pp::Instance {
public:
explicit MyInstance(PP_Instance instance)
: pp::Instance(instance) {
factory_.Initialize(this);
}
virtual ~MyInstance() {
// Make sure to explicitly close the loader. If somebody else is holding a
// reference to the URLLoader object when this class goes out of scope (so
// the URLLoader outlives "this"), and you have an outstanding read
// request, the URLLoader will write into invalid memory.
loader_.Close();
}
// Handler for the page sending us messages.
virtual void HandleMessage(const pp::Var& message_data);
private:
// Called to initiate the request.
void StartRequest(const std::string& url);
// Callback for the URLLoader to tell us it finished opening the connection.
void OnOpenComplete(int32_t result);
// Starts streaming data.
void ReadMore();
// Callback for the URLLoader to tell us when it finished a read.
void OnReadComplete(int32_t result);
// Forwards the given string to the page.
void ReportResponse(const std::string& data);
// Generates completion callbacks scoped to this class.
pp::CompletionCallbackFactory<MyInstance> factory_;
pp::URLLoader loader_;
pp::URLResponseInfo response_;
// The buffer used for the current read request. This is filled and then
// copied into content_ to build up the entire document.
char buf_[kBufSize];
// All the content loaded so far.
std::string content_;
};
void MyInstance::HandleMessage(const pp::Var& message_data) {
if (message_data.is_string() && message_data.AsString() == "go")
StartRequest("./fetched_content.html");
}
void MyInstance::StartRequest(const std::string& url) {
content_.clear();
pp::URLRequestInfo request(this);
request.SetURL(url);
request.SetMethod("GET");
loader_ = pp::URLLoader(this);
loader_.Open(request,
factory_.NewCallback(&MyInstance::OnOpenComplete));
}
void MyInstance::OnOpenComplete(int32_t result) {
if (result != PP_OK) {
ReportResponse("URL could not be requested");
return;
}
response_ = loader_.GetResponseInfo();
// Here you would process the headers. A real program would want to at least
// check the HTTP code and potentially cancel the request.
// Start streaming.
ReadMore();
}
void MyInstance::ReadMore() {
// Note that you specifically want an "optional" callback here. This will
// allow Read() to return synchronously, ignoring your completion callback,
// if data is available. For fast connections and large files, reading as
// fast as we can will make a large performance difference. However, in the
// case of a synchronous return, we need to be sure to run the callback we
// created since the loader won't do anything with it.
pp::CompletionCallback cc =
factory_.NewOptionalCallback(&MyInstance::OnReadComplete);
int32_t result = PP_OK;
do {
result = loader_.ReadResponseBody(buf_, kBufSize, cc);
// Handle streaming data directly. Note that we *don't* want to call
// OnReadComplete here, since in the case of result > 0 it will schedule
// another call to this function. If the network is very fast, we could
// end up with a deeply recursive stack.
if (result > 0)
content_.append(buf_, result);
} while (result > 0);
if (result != PP_OK_COMPLETIONPENDING) {
// Either we reached the end of the stream (result == PP_OK) or there was
// an error. We want OnReadComplete to get called no matter what to handle
// that case, whether the error is synchronous or asynchronous. If the
// result code *is* COMPLETIONPENDING, our callback will be called
// asynchronously.
cc.Run(result);
}
}
void MyInstance::OnReadComplete(int32_t result) {
if (result == PP_OK) {
// Streaming the file is complete.
ReportResponse(content_);
} else if (result > 0) {
// The URLLoader just filled "result" number of bytes into our buffer.
// Save them and perform another read.
content_.append(buf_, result);
ReadMore();
} else {
// A read error occurred.
ReportResponse("A read error occurred");
}
}
void MyInstance::ReportResponse(const std::string& data) {
PostMessage(pp::Var(data));
}
// This object is the global object representing this plugin library as long
// as it is loaded.
class MyModule : public pp::Module {
public:
MyModule() : pp::Module() {}
virtual ~MyModule() {}
// Override CreateInstance to create your customized Instance object.
virtual pp::Instance* CreateInstance(PP_Instance instance) {
return new MyInstance(instance);
}
};
namespace pp {
// Factory function for your specialization of the Module object.
Module* CreateModule() {
return new MyModule();
}
} // namespace pp