1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// This example shows how to use the URLLoader in "stream to file" mode where
6// the browser writes incoming data to a file, which you can read out via the
7// file I/O APIs.
8//
9// This example uses PostMessage between the plugin and the url_loader.html
10// page in this directory to start the load and to communicate the result.
11
12#include "ppapi/c/ppb_file_io.h"
13#include "ppapi/cpp/file_io.h"
14#include "ppapi/cpp/file_ref.h"
15#include "ppapi/cpp/instance.h"
16#include "ppapi/cpp/module.h"
17#include "ppapi/cpp/url_loader.h"
18#include "ppapi/cpp/url_request_info.h"
19#include "ppapi/cpp/url_response_info.h"
20#include "ppapi/utility/completion_callback_factory.h"
21
22// When compiling natively on Windows, PostMessage can be #define-d to
23// something else.
24#ifdef PostMessage
25#undef PostMessage
26#endif
27
28// Buffer size for reading network data.
29const int kBufSize = 1024;
30
31class MyInstance : public pp::Instance {
32 public:
33  explicit MyInstance(PP_Instance instance)
34      : pp::Instance(instance) {
35    factory_.Initialize(this);
36  }
37  virtual ~MyInstance() {
38    // Make sure to explicitly close the loader. If somebody else is holding a
39    // reference to the URLLoader object when this class goes out of scope (so
40    // the URLLoader outlives "this"), and you have an outstanding read
41    // request, the URLLoader will write into invalid memory.
42    loader_.Close();
43  }
44
45  // Handler for the page sending us messages.
46  virtual void HandleMessage(const pp::Var& message_data);
47
48 private:
49  // Called to initiate the request.
50  void StartRequest(const std::string& url);
51
52  // Callback for the URLLoader to tell us it finished opening the connection.
53  void OnOpenComplete(int32_t result);
54
55  // Callback for when the file is completely filled with the download
56  void OnStreamComplete(int32_t result);
57
58  void OnOpenFileComplete(int32_t result);
59  void OnReadComplete(int32_t result);
60
61  // Forwards the given string to the page.
62  void ReportResponse(const std::string& data);
63
64  // Generates completion callbacks scoped to this class.
65  pp::CompletionCallbackFactory<MyInstance> factory_;
66
67  pp::URLLoader loader_;
68  pp::URLResponseInfo response_;
69  pp::FileRef dest_file_;
70  pp::FileIO file_io_;
71
72  // The buffer used for the current read request. This is filled and then
73  // copied into content_ to build up the entire document.
74  char buf_[kBufSize];
75
76  // All the content loaded so far.
77  std::string content_;
78};
79
80void MyInstance::HandleMessage(const pp::Var& message_data) {
81  if (message_data.is_string() && message_data.AsString() == "go")
82    StartRequest("./fetched_content.html");
83}
84
85void MyInstance::StartRequest(const std::string& url) {
86  content_.clear();
87
88  pp::URLRequestInfo request(this);
89  request.SetURL(url);
90  request.SetMethod("GET");
91  request.SetStreamToFile(true);
92
93  loader_ = pp::URLLoader(this);
94  loader_.Open(request,
95               factory_.NewCallback(&MyInstance::OnOpenComplete));
96}
97
98void MyInstance::OnOpenComplete(int32_t result) {
99  if (result != PP_OK) {
100    ReportResponse("URL could not be requested");
101    return;
102  }
103
104  loader_.FinishStreamingToFile(
105      factory_.NewCallback(&MyInstance::OnStreamComplete));
106  response_ = loader_.GetResponseInfo();
107  dest_file_ = response_.GetBodyAsFileRef();
108}
109
110void MyInstance::OnStreamComplete(int32_t result) {
111  if (result == PP_OK) {
112    file_io_ = pp::FileIO(this);
113    file_io_.Open(dest_file_, PP_FILEOPENFLAG_READ,
114        factory_.NewCallback(&MyInstance::OnOpenFileComplete));
115  } else {
116    ReportResponse("Could not stream to file");
117  }
118}
119
120void MyInstance::OnOpenFileComplete(int32_t result) {
121  if (result == PP_OK) {
122    // Note we only read the first 1024 bytes from the file in this example
123    // to keep things simple. Please see a file I/O example for more details
124    // on reading files.
125    file_io_.Read(0, buf_, kBufSize,
126        factory_.NewCallback(&MyInstance::OnReadComplete));
127  } else {
128    ReportResponse("Could not open file");
129  }
130}
131
132void MyInstance::OnReadComplete(int32_t result) {
133  if (result >= 0) {
134    content_.append(buf_, result);
135    ReportResponse(buf_);
136  } else {
137    ReportResponse("Could not read file");
138  }
139
140  // Release everything.
141  loader_ = pp::URLLoader();
142  response_ = pp::URLResponseInfo();
143  dest_file_ = pp::FileRef();
144  file_io_ = pp::FileIO();
145}
146
147void MyInstance::ReportResponse(const std::string& data) {
148  PostMessage(pp::Var(data));
149}
150
151// This object is the global object representing this plugin library as long
152// as it is loaded.
153class MyModule : public pp::Module {
154 public:
155  MyModule() : pp::Module() {}
156  virtual ~MyModule() {}
157
158  // Override CreateInstance to create your customized Instance object.
159  virtual pp::Instance* CreateInstance(PP_Instance instance) {
160    return new MyInstance(instance);
161  }
162};
163
164namespace pp {
165
166// Factory function for your specialization of the Module object.
167Module* CreateModule() {
168  return new MyModule();
169}
170
171}  // namespace pp
172