1// Copyright 2014 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#include "mojo/bindings/js/drain_data.h"
6
7#include "gin/array_buffer.h"
8#include "gin/converter.h"
9#include "gin/dictionary.h"
10#include "gin/per_context_data.h"
11#include "gin/per_isolate_data.h"
12#include "mojo/public/cpp/environment/environment.h"
13#include "mojo/public/cpp/system/core.h"
14
15namespace mojo {
16namespace js {
17
18DrainData::DrainData(v8::Isolate* isolate, mojo::Handle handle)
19    : isolate_(isolate),
20      handle_(DataPipeConsumerHandle(handle.value())),
21      wait_id_(0) {
22
23  v8::Handle<v8::Context> context(isolate_->GetCurrentContext());
24  runner_ = gin::PerContextData::From(context)->runner()->GetWeakPtr();
25
26  WaitForData();
27}
28
29v8::Handle<v8::Value> DrainData::GetPromise() {
30  CHECK(resolver_.IsEmpty());
31  v8::Handle<v8::Promise::Resolver> resolver(
32      v8::Promise::Resolver::New(isolate_));
33  resolver_.Reset(isolate_, resolver);
34  return resolver->GetPromise();
35}
36
37DrainData::~DrainData() {
38  if (wait_id_)
39    Environment::GetDefaultAsyncWaiter()->CancelWait(wait_id_);
40  resolver_.Reset();
41}
42
43void DrainData::WaitForData() {
44  wait_id_ = Environment::GetDefaultAsyncWaiter()->AsyncWait(
45      handle_.get().value(),
46      MOJO_HANDLE_SIGNAL_READABLE,
47      MOJO_DEADLINE_INDEFINITE,
48      &DrainData::WaitCompleted,
49      this);
50}
51
52void DrainData::DataReady(MojoResult result) {
53  wait_id_ = 0;
54  if (result != MOJO_RESULT_OK) {
55    DeliverData(result);
56    return;
57  }
58  while (result == MOJO_RESULT_OK) {
59    result = ReadData();
60    if (result == MOJO_RESULT_SHOULD_WAIT)
61      WaitForData();
62    else if (result != MOJO_RESULT_OK)
63      DeliverData(result);
64  }
65}
66
67MojoResult DrainData::ReadData() {
68  const void* buffer;
69  uint32_t num_bytes = 0;
70  MojoResult result = BeginReadDataRaw(
71      handle_.get(), &buffer, &num_bytes, MOJO_READ_DATA_FLAG_NONE);
72  if (result != MOJO_RESULT_OK)
73    return result;
74  const char* p = static_cast<const char*>(buffer);
75  DataBuffer* data_buffer = new DataBuffer(p, p + num_bytes);
76  data_buffers_.push_back(data_buffer);
77  return EndReadDataRaw(handle_.get(), num_bytes);
78}
79
80void DrainData::DeliverData(MojoResult result) {
81  if (!runner_) {
82    delete this;
83    return;
84  }
85
86  size_t total_bytes = 0;
87  for (unsigned i = 0; i < data_buffers_.size(); i++)
88    total_bytes += data_buffers_[i]->size();
89
90  // Create a total_bytes length ArrayBuffer return value.
91  gin::Runner::Scope scope(runner_.get());
92  v8::Handle<v8::ArrayBuffer> array_buffer =
93      v8::ArrayBuffer::New(isolate_, total_bytes);
94  gin::ArrayBuffer buffer;
95  ConvertFromV8(isolate_, array_buffer, &buffer);
96  CHECK_EQ(total_bytes, buffer.num_bytes());
97
98  // Copy the data_buffers into the ArrayBuffer.
99  char* array_buffer_ptr = static_cast<char*>(buffer.bytes());
100  size_t offset = 0;
101  for (size_t i = 0; i < data_buffers_.size(); i++) {
102    size_t num_bytes = data_buffers_[i]->size();
103    if (num_bytes == 0)
104      continue;
105    const char* data_buffer_ptr = &((*data_buffers_[i])[0]);
106    memcpy(array_buffer_ptr + offset, data_buffer_ptr, num_bytes);
107    offset += num_bytes;
108  }
109
110  // The "settled" value of the promise always includes all of the data
111  // that was read before either an error occurred or the remote pipe handle
112  // was closed. The latter is indicated by MOJO_RESULT_FAILED_PRECONDITION.
113
114  v8::Handle<v8::Promise::Resolver> resolver(
115      v8::Local<v8::Promise::Resolver>::New(isolate_, resolver_));
116
117  gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(isolate_);
118  dictionary.Set("result", result);
119  dictionary.Set("buffer", array_buffer);
120  v8::Handle<v8::Value> settled_value(ConvertToV8(isolate_, dictionary));
121
122  if (result == MOJO_RESULT_FAILED_PRECONDITION)
123    resolver->Resolve(settled_value);
124  else
125    resolver->Reject(settled_value);
126
127  delete this;
128}
129
130}  // namespace js
131}  // namespace mojo
132