1// Copyright 2013 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 "content/browser/loader/upload_data_stream_builder.h"
6
7#include "base/logging.h"
8#include "content/browser/fileapi/upload_file_system_file_element_reader.h"
9#include "content/common/resource_request_body.h"
10#include "net/base/upload_bytes_element_reader.h"
11#include "net/base/upload_data_stream.h"
12#include "net/base/upload_file_element_reader.h"
13#include "storage/browser/blob/blob_data_handle.h"
14#include "storage/browser/blob/blob_storage_context.h"
15
16using storage::BlobData;
17using storage::BlobDataHandle;
18using storage::BlobStorageContext;
19
20namespace content {
21namespace {
22
23// A subclass of net::UploadBytesElementReader which owns ResourceRequestBody.
24class BytesElementReader : public net::UploadBytesElementReader {
25 public:
26  BytesElementReader(ResourceRequestBody* resource_request_body,
27                     const ResourceRequestBody::Element& element)
28      : net::UploadBytesElementReader(element.bytes(), element.length()),
29        resource_request_body_(resource_request_body) {
30    DCHECK_EQ(ResourceRequestBody::Element::TYPE_BYTES, element.type());
31  }
32
33  virtual ~BytesElementReader() {}
34
35 private:
36  scoped_refptr<ResourceRequestBody> resource_request_body_;
37
38  DISALLOW_COPY_AND_ASSIGN(BytesElementReader);
39};
40
41// A subclass of net::UploadFileElementReader which owns ResourceRequestBody.
42// This class is necessary to ensure the BlobData and any attached shareable
43// files survive until upload completion.
44class FileElementReader : public net::UploadFileElementReader {
45 public:
46  FileElementReader(ResourceRequestBody* resource_request_body,
47                    base::TaskRunner* task_runner,
48                    const ResourceRequestBody::Element& element)
49      : net::UploadFileElementReader(task_runner,
50                                     element.path(),
51                                     element.offset(),
52                                     element.length(),
53                                     element.expected_modification_time()),
54        resource_request_body_(resource_request_body) {
55    DCHECK_EQ(ResourceRequestBody::Element::TYPE_FILE, element.type());
56  }
57
58  virtual ~FileElementReader() {}
59
60 private:
61  scoped_refptr<ResourceRequestBody> resource_request_body_;
62
63  DISALLOW_COPY_AND_ASSIGN(FileElementReader);
64};
65
66void ResolveBlobReference(
67    storage::BlobStorageContext* blob_context,
68    const ResourceRequestBody::Element& element,
69    std::vector<const ResourceRequestBody::Element*>* resolved_elements) {
70  DCHECK(blob_context);
71  scoped_ptr<storage::BlobDataHandle> handle =
72      blob_context->GetBlobDataFromUUID(element.blob_uuid());
73  DCHECK(handle);
74  if (!handle)
75    return;
76
77  // If there is no element in the referred blob data, just return.
78  if (handle->data()->items().empty())
79    return;
80
81  // Append the elements in the referenced blob data.
82  for (size_t i = 0; i < handle->data()->items().size(); ++i) {
83    const BlobData::Item& item = handle->data()->items().at(i);
84    DCHECK_NE(BlobData::Item::TYPE_BLOB, item.type());
85    resolved_elements->push_back(&item);
86  }
87}
88
89}  // namespace
90
91scoped_ptr<net::UploadDataStream> UploadDataStreamBuilder::Build(
92    ResourceRequestBody* body,
93    BlobStorageContext* blob_context,
94    storage::FileSystemContext* file_system_context,
95    base::TaskRunner* file_task_runner) {
96  // Resolve all blob elements.
97  std::vector<const ResourceRequestBody::Element*> resolved_elements;
98  for (size_t i = 0; i < body->elements()->size(); ++i) {
99    const ResourceRequestBody::Element& element = (*body->elements())[i];
100    if (element.type() == ResourceRequestBody::Element::TYPE_BLOB)
101      ResolveBlobReference(blob_context, element, &resolved_elements);
102    else
103      resolved_elements.push_back(&element);
104  }
105
106  ScopedVector<net::UploadElementReader> element_readers;
107  for (size_t i = 0; i < resolved_elements.size(); ++i) {
108    const ResourceRequestBody::Element& element = *resolved_elements[i];
109    switch (element.type()) {
110      case ResourceRequestBody::Element::TYPE_BYTES:
111        element_readers.push_back(new BytesElementReader(body, element));
112        break;
113      case ResourceRequestBody::Element::TYPE_FILE:
114        element_readers.push_back(
115            new FileElementReader(body, file_task_runner, element));
116        break;
117      case ResourceRequestBody::Element::TYPE_FILE_FILESYSTEM:
118        element_readers.push_back(
119            new content::UploadFileSystemFileElementReader(
120                file_system_context,
121                element.filesystem_url(),
122                element.offset(),
123                element.length(),
124                element.expected_modification_time()));
125        break;
126      case ResourceRequestBody::Element::TYPE_BLOB:
127        // Blob elements should be resolved beforehand.
128        NOTREACHED();
129        break;
130      case ResourceRequestBody::Element::TYPE_UNKNOWN:
131        NOTREACHED();
132        break;
133    }
134  }
135
136  return make_scoped_ptr(
137      new net::UploadDataStream(element_readers.Pass(), body->identifier()));
138}
139
140}  // namespace content
141