summaryrefslogtreecommitdiff
path: root/chromium/services/data_decoder/web_bundler.cc
blob: 0d715a146432c6d07c7b953c7e4adcda10e6b97a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// Copyright 2020 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.

#include "services/data_decoder/web_bundler.h"

#include "base/big_endian.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/string_piece.h"
#include "web_bundle_builder.h"

namespace data_decoder {

// WebBundler does not permit body size larder than ~1GB.
const uint64_t kMaxBodySize = (1 << 30);

WebBundler::WebBundler() = default;
WebBundler::~WebBundler() = default;

void WebBundler::Generate(
    std::vector<mojo::PendingRemote<mojom::ResourceSnapshotForWebBundle>>
        snapshots,
    base::File file,
    GenerateCallback callback) {
  DCHECK(snapshots_.empty());
  DCHECK(!snapshots.empty());
  for (auto& pending_snapshot : snapshots) {
    mojo::Remote<mojom::ResourceSnapshotForWebBundle> snapshot(
        std::move(pending_snapshot));
    snapshot.set_disconnect_handler(
        base::BindOnce(&WebBundler::OnConnectionError, base::Unretained(this)));
    snapshots_.emplace_back(std::move(snapshot));
  }
  file_ = std::move(file);
  callback_ = std::move(callback);
  GetNextResourceCount();
}

void WebBundler::OnConnectionError() {
  if (callback_) {
    std::move(callback_).Run(0, mojom::WebBundlerError::kConnectionError);
  }
}

void WebBundler::GetNextResourceCount() {
  if (snapshots_.size() == resources_.size()) {
    WriteWebBundleIndex();
    return;
  }
  snapshots_[resources_.size()]->GetResourceCount(
      base::BindOnce(&WebBundler::OnGetResourceCount, base::Unretained(this)));
}

void WebBundler::OnGetResourceCount(uint64_t count) {
  pending_resource_count_ = count;
  resources_.emplace_back();
  bodies_.emplace_back();
  GetNextResourceInfo();
}

void WebBundler::GetNextResourceInfo() {
  if (pending_resource_count_ == 0) {
    GetNextResourceCount();
    return;
  }
  snapshots_[resources_.size() - 1]->GetResourceInfo(
      resources_.rbegin()->size(),
      base::BindOnce(&WebBundler::OnGetResourceInfo, base::Unretained(this)));
}

void WebBundler::OnGetResourceInfo(mojom::SerializedResourceInfoPtr info) {
  resources_.rbegin()->emplace_back(std::move(info));
  snapshots_[bodies_.size() - 1]->GetResourceBody(
      bodies_.rbegin()->size(),
      base::BindOnce(&WebBundler::OnGetResourceBody, base::Unretained(this)));
}

void WebBundler::OnGetResourceBody(absl::optional<mojo_base::BigBuffer> body) {
  if (body->size() > kMaxBodySize) {
    std::move(callback_).Run(0, mojom::WebBundlerError::kInvalidInput);
    return;
  }
  bodies_.rbegin()->emplace_back(std::move(body));
  --pending_resource_count_;
  GetNextResourceInfo();
}

void WebBundler::WriteWebBundleIndex() {
  if (!callback_) {
    return;
  }
  GURL url = resources_[0][0]->url;
  GURL::Replacements replacements;
  replacements.ClearRef();
  url = url.ReplaceComponents(replacements);
  WebBundleBuilder builder(url.spec());
  std::set<GURL> url_set;
  CHECK_EQ(resources_.size(), bodies_.size());
  std::vector<mojom::SerializedResourceInfoPtr> resources;
  std::vector<absl::optional<mojo_base::BigBuffer>> bodies;
  for (size_t i = 0; i < resources_.size(); ++i) {
    auto& info_list = resources_[i];
    auto& body_list = bodies_[i];
    CHECK_EQ(info_list.size(), body_list.size());
    for (size_t j = 0; j < info_list.size(); ++j) {
      auto& info = info_list[j];
      auto& body = body_list[j];
      if (url_set.find(info->url) == url_set.end() && info->url.is_valid() &&
          info->url.SchemeIsHTTPOrHTTPS()) {
        url_set.insert(info->url);
        resources.emplace_back(std::move(info));
        bodies.emplace_back(std::move(body));
      }
    }
  }
  std::vector<uint8_t> bundle =
      builder.CreateBundle(std::move(resources), std::move(bodies));
  int written_size = file_.WriteAtCurrentPos(
      reinterpret_cast<const char*>(bundle.data()), bundle.size());
  DCHECK_EQ(static_cast<int>(bundle.size()), written_size);
  std::move(callback_).Run(written_size, mojom::WebBundlerError::kOK);
}

}  // namespace data_decoder