diff options
author | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-12 18:03:14 +0000 |
---|---|---|
committer | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-12 18:03:14 +0000 |
commit | 417d2d1f242f7a5256ab7c7437f7ed6e060b95f3 (patch) | |
tree | b1df6bf7b044028eb24f339e57b60070a5e0e016 /ppapi/examples | |
parent | 6695341f93d2f2b46db6c4c69550e3bbe7996999 (diff) | |
download | chromium_src-417d2d1f242f7a5256ab7c7437f7ed6e060b95f3.zip chromium_src-417d2d1f242f7a5256ab7c7437f7ed6e060b95f3.tar.gz chromium_src-417d2d1f242f7a5256ab7c7437f7ed6e060b95f3.tar.bz2 |
Add an example for using the URLLoader in streaming mode. This also avoids the
infinite recursion bug of the old example (and actually compiles, the old
example had a lot of typos).
BUG=69203
Review URL: http://codereview.chromium.org/7330027
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@92196 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ppapi/examples')
-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 |
4 files changed, 251 insertions, 0 deletions
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> + |