diff options
-rw-r--r-- | ppapi/cpp/url_loader.h | 59 | ||||
-rw-r--r-- | ppapi/examples/url_loader/README_chrome_developer.txt | 22 | ||||
-rw-r--r-- | ppapi/examples/url_loader/fetched_content.html | 4 | ||||
-rw-r--r-- | ppapi/examples/url_loader/streaming.cc | 172 | ||||
-rw-r--r-- | ppapi/examples/url_loader/url_loader.html | 53 | ||||
-rw-r--r-- | ppapi/ppapi_tests.gypi | 10 |
6 files changed, 262 insertions, 58 deletions
diff --git a/ppapi/cpp/url_loader.h b/ppapi/cpp/url_loader.h index 0cfe5ea..e090fb3 100644 --- a/ppapi/cpp/url_loader.h +++ b/ppapi/cpp/url_loader.h @@ -17,64 +17,7 @@ class URLResponseInfo; // URLLoader provides an API to download URLs. // -// EXAMPLE USAGE: -// -// class MyHandler { -// public: -// MyHandler(Instance* instance) -// : factory_(this), -// loader_(instance), -// did_open_(false) { -// } -// void ProcessURL(const char* url) { -// CompletionCallback* cc = NewCallback(); -// int32_t rv = loader_.Open(MakeRequest(url), cc); -// if (rv != PP_OK_COMPLETIONPENDING) -// cc->Run(rv); -// } -// private: -// CompletionCallback* NewCallback() { -// return factory_.NewOptionalCallback(&MyHandler::DidCompleteIO); -// } -// URLRequestInfo MakeRequest(const char* url) { -// URLRequestInfo request; -// request.SetURL(url); -// request.SetMethod("GET"); -// request.SetFollowRedirects(true); -// return request; -// } -// void DidCompleteIO(int32_t result) { -// if (result > 0) { -// // buf_ now contains 'result' number of bytes from the URL. -// ProcessBytes(buf_, result); -// ReadMore(); -// } else if (result == PP_OK && !did_open_) { -// // Headers are available, and we can start reading the body. -// did_open_ = true; -// ProcessResponseInfo(loader_.GetResponseInfo()); -// ReadMore(); -// } else { -// // Done reading (possibly with an error given by 'result'). -// } -// } -// void ReadMore() { -// CompletionCallback* cc = NewCallback(); -// int32_t rv = fio_.Read(offset_, buf_, sizeof(buf_), cc); -// if (rv != PP_OK_COMPLETIONPENDING) -// cc->Run(rv); -// } -// void ProcessResponseInfo(const URLResponseInfo& response_info) { -// // Read response headers, etc. -// } -// void ProcessBytes(const char* bytes, int32_t length) { -// // Do work ... -// } -// pp::CompletionCallbackFactory<MyHandler> factory_; -// pp::URLLoader loader_; -// char buf_[4096]; -// bool did_open_; -// }; -// +// Please see the example in ppapi/examples/url_loader/url_loader.cc. class URLLoader : public Resource { public: // Creates an is_null() URLLoader object. diff --git a/ppapi/examples/url_loader/README_chrome_developer.txt b/ppapi/examples/url_loader/README_chrome_developer.txt new file mode 100644 index 0000000..c9a6721 --- /dev/null +++ b/ppapi/examples/url_loader/README_chrome_developer.txt @@ -0,0 +1,22 @@ +If you're a Chrome developer, here's how to run this example: + +Build the example, i.e. "make ppapi_example_url_loader" on Linux. + +In the "src" directory, start the test server: + + On Linux: + export PYTHONPATH=third_party/pyftpdlib:third_party/tlslite:third_party + python net/tools/testserver/testserver.py --port=1337 --data-dir=ppapi/examples/url_loader + + On Windows: + set PYTHONPATH=third_party\pyftpdlib;third_party\tlslite:third_party + python net/tools/testserver/testserver.py --port=1337 --data-dir=ppapi/examples/url_loader + +Then load the page: + + On Linux: + out/Debug/chrome --register-pepper-plugins="[[[ YOUR SOURCE DIR ]]]/out/Debug/lib.target/libppapi_example_url_loader.so;application/x-ppapi-url-loader-example" http://127.0.0.1:1337/files/url_loader.html + + On Windows just substitute the generated .dll name for the .so. + +And click the button! diff --git a/ppapi/examples/url_loader/fetched_content.html b/ppapi/examples/url_loader/fetched_content.html new file mode 100644 index 0000000..16ab8ec --- /dev/null +++ b/ppapi/examples/url_loader/fetched_content.html @@ -0,0 +1,4 @@ +This is the <b>content</b> of the page that was fetched. +<p> +<h1>If you see this</h1> +Everything worked great. diff --git a/ppapi/examples/url_loader/streaming.cc b/ppapi/examples/url_loader/streaming.cc new file mode 100644 index 0000000..b6df6cb --- /dev/null +++ b/ppapi/examples/url_loader/streaming.cc @@ -0,0 +1,172 @@ +// Copyright (c) 2011 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. + +// 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. For that mode, call +// URLLoader.FinishSthreamingToFile once the "Open" callback is complete, and +// then call URLResponseInfo.GetBodyAsFileRef once the file stream is complete. + +#include "ppapi/cpp/completion_callback.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" + +// 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_.NewRequiredCallback(&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 diff --git a/ppapi/examples/url_loader/url_loader.html b/ppapi/examples/url_loader/url_loader.html new file mode 100644 index 0000000..39f4494 --- /dev/null +++ b/ppapi/examples/url_loader/url_loader.html @@ -0,0 +1,53 @@ +<!DOCTYPE html> +<html> + <!-- + Copyright (c) 2011 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. + + This example just uses postMessage to tell the plugin to fetch a file. + The plugin will echo the contents of that file back to us and we'll display + it on the page. + --> +<head> + <title>URLLoader Example</title> +</head> + +<body> + <script> + + function StartRequest() { + var plugin = document.getElementById("plugin"); + var inputBox = document.getElementById("inputBox"); + plugin.postMessage("go"); + } + + </script> + + <p>This test must be run over HTTP. If you're a Chrome developer, see the + README_chrome_developer.txt in this directory for how to run.</p> + + <button onclick="StartRequest()">Start request</button> + <object id="plugin" type="application/x-ppapi-url-loader-example" + width="1" height="1"> + </object> + <hr> + <div id="log_result" style="background-color:#EEE; border:1px solid black;"> + <i>Result will go here...</i> + </div> + + <script> + + function HandleMessage(message_event) { + document.getElementById("log_result").innerHTML = message_event.data; + } + + // Attach a listener for the message event. This must happen after the plugin + // object was created. + var plugin = document.getElementById("plugin"); + plugin.addEventListener("message", HandleMessage, false); + + </script> +</body> +</html> + diff --git a/ppapi/ppapi_tests.gypi b/ppapi/ppapi_tests.gypi index 79cfaa4..93f9c88 100644 --- a/ppapi/ppapi_tests.gypi +++ b/ppapi/ppapi_tests.gypi @@ -314,6 +314,16 @@ ], }, { + 'target_name': 'ppapi_example_url_loader', + 'dependencies': [ + 'ppapi_example_skeleton', + 'ppapi.gyp:ppapi_cpp', + ], + 'sources': [ + 'examples/url_loader/streaming.cc', + ], + }, + { 'target_name': 'ppapi_example_gles2', 'dependencies': [ 'ppapi_example_skeleton', |