chromium/ppapi/examples/url_loader/stream_to_file.cc

// 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 "stream to file" mode where
// the browser writes incoming data to a file, which you can read out via the
// file I/O APIs.
//
// 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.

#include <stdint.h>

#include "ppapi/c/ppb_file_io.h"
#include "ppapi/cpp/file_io.h"
#include "ppapi/cpp/file_ref.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);

  // Callback for when the file is completely filled with the download
  void OnStreamComplete(int32_t result);

  void OnOpenFileComplete(int32_t result);
  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_;
  pp::FileRef dest_file_;
  pp::FileIO file_io_;

  // 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");
  request.SetStreamToFile(true);

  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;
  }

  loader_.FinishStreamingToFile(
      factory_.NewCallback(&MyInstance::OnStreamComplete));
  response_ = loader_.GetResponseInfo();
  dest_file_ = response_.GetBodyAsFileRef();
}

void MyInstance::OnStreamComplete(int32_t result) {
  if (result == PP_OK) {
    file_io_ = pp::FileIO(this);
    file_io_.Open(dest_file_, PP_FILEOPENFLAG_READ,
        factory_.NewCallback(&MyInstance::OnOpenFileComplete));
  } else {
    ReportResponse("Could not stream to file");
  }
}

void MyInstance::OnOpenFileComplete(int32_t result) {
  if (result == PP_OK) {
    // Note we only read the first 1024 bytes from the file in this example
    // to keep things simple. Please see a file I/O example for more details
    // on reading files.
    file_io_.Read(0, buf_, kBufSize,
        factory_.NewCallback(&MyInstance::OnReadComplete));
  } else {
    ReportResponse("Could not open file");
  }
}

void MyInstance::OnReadComplete(int32_t result) {
  if (result >= 0) {
    content_.append(buf_, result);
    ReportResponse(buf_);
  } else {
    ReportResponse("Could not read file");
  }

  // Release everything.
  loader_ = pp::URLLoader();
  response_ = pp::URLResponseInfo();
  dest_file_ = pp::FileRef();
  file_io_ = pp::FileIO();
}

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