1 // Copyright 2017 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 "components/download/database/download_db_conversions.h"
6 
7 #include <utility>
8 
9 #include "base/notreached.h"
10 #include "base/pickle.h"
11 #include "components/download/public/common/download_features.h"
12 
13 namespace download {
14 namespace {
15 
16 // Converts base::Time to a timpstamp in milliseconds.
FromTimeToMilliseconds(base::Time time)17 int64_t FromTimeToMilliseconds(base::Time time) {
18   return time.ToDeltaSinceWindowsEpoch().InMilliseconds();
19 }
20 
21 // Converts a time stamp in milliseconds to base::Time.
FromMillisecondsToTime(int64_t time_ms)22 base::Time FromMillisecondsToTime(int64_t time_ms) {
23   return base::Time::FromDeltaSinceWindowsEpoch(
24       base::TimeDelta::FromMilliseconds(time_ms));
25 }
26 
27 }  // namespace
28 
DownloadEntryFromProto(const download_pb::DownloadEntry & proto)29 DownloadEntry DownloadDBConversions::DownloadEntryFromProto(
30     const download_pb::DownloadEntry& proto) {
31   DownloadEntry entry;
32   entry.guid = proto.guid();
33   entry.request_origin = proto.request_origin();
34   entry.download_source = DownloadSourceFromProto(proto.download_source());
35   entry.ukm_download_id = proto.ukm_download_id();
36   entry.bytes_wasted = proto.bytes_wasted();
37   entry.fetch_error_body = proto.fetch_error_body();
38   for (const auto& header : proto.request_headers()) {
39     entry.request_headers.emplace_back(HttpRequestHeaderFromProto(header));
40   }
41   return entry;
42 }
43 
DownloadEntryToProto(const DownloadEntry & entry)44 download_pb::DownloadEntry DownloadDBConversions::DownloadEntryToProto(
45     const DownloadEntry& entry) {
46   download_pb::DownloadEntry proto;
47   proto.set_guid(entry.guid);
48   proto.set_request_origin(entry.request_origin);
49   proto.set_download_source(DownloadSourceToProto(entry.download_source));
50   proto.set_ukm_download_id(entry.ukm_download_id);
51   proto.set_bytes_wasted(entry.bytes_wasted);
52   proto.set_fetch_error_body(entry.fetch_error_body);
53   for (const auto& header : entry.request_headers) {
54     auto* proto_header = proto.add_request_headers();
55     *proto_header = HttpRequestHeaderToProto(header);
56   }
57   return proto;
58 }
59 
60 // static
DownloadSourceFromProto(download_pb::DownloadSource download_source)61 DownloadSource DownloadDBConversions::DownloadSourceFromProto(
62     download_pb::DownloadSource download_source) {
63   switch (download_source) {
64     case download_pb::DownloadSource::UNKNOWN:
65       return DownloadSource::UNKNOWN;
66     case download_pb::DownloadSource::NAVIGATION:
67       return DownloadSource::NAVIGATION;
68     case download_pb::DownloadSource::DRAG_AND_DROP:
69       return DownloadSource::DRAG_AND_DROP;
70     case download_pb::DownloadSource::FROM_RENDERER:
71       return DownloadSource::FROM_RENDERER;
72     case download_pb::DownloadSource::EXTENSION_API:
73       return DownloadSource::EXTENSION_API;
74     case download_pb::DownloadSource::EXTENSION_INSTALLER:
75       return DownloadSource::EXTENSION_INSTALLER;
76     case download_pb::DownloadSource::INTERNAL_API:
77       return DownloadSource::INTERNAL_API;
78     case download_pb::DownloadSource::WEB_CONTENTS_API:
79       return DownloadSource::WEB_CONTENTS_API;
80     case download_pb::DownloadSource::OFFLINE_PAGE:
81       return DownloadSource::OFFLINE_PAGE;
82     case download_pb::DownloadSource::CONTEXT_MENU:
83       return DownloadSource::CONTEXT_MENU;
84     case download_pb::DownloadSource::RETRY:
85       return DownloadSource::RETRY;
86   }
87   NOTREACHED();
88   return DownloadSource::UNKNOWN;
89 }
90 
91 // static
DownloadSourceToProto(DownloadSource download_source)92 download_pb::DownloadSource DownloadDBConversions::DownloadSourceToProto(
93     DownloadSource download_source) {
94   switch (download_source) {
95     case DownloadSource::UNKNOWN:
96       return download_pb::DownloadSource::UNKNOWN;
97     case DownloadSource::NAVIGATION:
98       return download_pb::DownloadSource::NAVIGATION;
99     case DownloadSource::DRAG_AND_DROP:
100       return download_pb::DownloadSource::DRAG_AND_DROP;
101     case DownloadSource::FROM_RENDERER:
102       return download_pb::DownloadSource::FROM_RENDERER;
103     case DownloadSource::EXTENSION_API:
104       return download_pb::DownloadSource::EXTENSION_API;
105     case DownloadSource::EXTENSION_INSTALLER:
106       return download_pb::DownloadSource::EXTENSION_INSTALLER;
107     case DownloadSource::INTERNAL_API:
108       return download_pb::DownloadSource::INTERNAL_API;
109     case DownloadSource::WEB_CONTENTS_API:
110       return download_pb::DownloadSource::WEB_CONTENTS_API;
111     case DownloadSource::OFFLINE_PAGE:
112       return download_pb::DownloadSource::OFFLINE_PAGE;
113     case DownloadSource::CONTEXT_MENU:
114       return download_pb::DownloadSource::CONTEXT_MENU;
115     case DownloadSource::RETRY:
116       return download_pb::DownloadSource::RETRY;
117   }
118   NOTREACHED();
119   return download_pb::DownloadSource::UNKNOWN;
120 }
121 
DownloadEntriesFromProto(const download_pb::DownloadEntries & proto)122 std::vector<DownloadEntry> DownloadDBConversions::DownloadEntriesFromProto(
123     const download_pb::DownloadEntries& proto) {
124   std::vector<DownloadEntry> entries;
125   for (int i = 0; i < proto.entries_size(); i++)
126     entries.push_back(DownloadEntryFromProto(proto.entries(i)));
127   return entries;
128 }
129 
DownloadEntriesToProto(const std::vector<DownloadEntry> & entries)130 download_pb::DownloadEntries DownloadDBConversions::DownloadEntriesToProto(
131     const std::vector<DownloadEntry>& entries) {
132   download_pb::DownloadEntries proto;
133   for (size_t i = 0; i < entries.size(); i++) {
134     download_pb::DownloadEntry* proto_entry = proto.add_entries();
135     *proto_entry = DownloadEntryToProto(entries[i]);
136   }
137   return proto;
138 }
139 
140 // static
HttpRequestHeaderToProto(const std::pair<std::string,std::string> & header)141 download_pb::HttpRequestHeader DownloadDBConversions::HttpRequestHeaderToProto(
142     const std::pair<std::string, std::string>& header) {
143   download_pb::HttpRequestHeader proto;
144   if (header.first.empty())
145     return proto;
146 
147   proto.set_key(header.first);
148   proto.set_value(header.second);
149   return proto;
150 }
151 
152 // static
153 std::pair<std::string, std::string>
HttpRequestHeaderFromProto(const download_pb::HttpRequestHeader & proto)154 DownloadDBConversions::HttpRequestHeaderFromProto(
155     const download_pb::HttpRequestHeader& proto) {
156   if (proto.key().empty())
157     return std::pair<std::string, std::string>();
158 
159   return std::make_pair(proto.key(), proto.value());
160 }
161 
162 // static
InProgressInfoToProto(const InProgressInfo & in_progress_info)163 download_pb::InProgressInfo DownloadDBConversions::InProgressInfoToProto(
164     const InProgressInfo& in_progress_info) {
165   download_pb::InProgressInfo proto;
166   for (size_t i = 0; i < in_progress_info.url_chain.size(); ++i)
167     proto.add_url_chain(in_progress_info.url_chain[i].spec());
168   proto.set_referrer_url(in_progress_info.referrer_url.spec());
169   proto.set_site_url(in_progress_info.site_url.spec());
170   proto.set_tab_url(in_progress_info.tab_url.spec());
171   proto.set_tab_referrer_url(in_progress_info.tab_referrer_url.spec());
172   proto.set_fetch_error_body(in_progress_info.fetch_error_body);
173   for (const auto& header : in_progress_info.request_headers) {
174     auto* proto_header = proto.add_request_headers();
175     *proto_header = HttpRequestHeaderToProto(header);
176   }
177   proto.set_etag(in_progress_info.etag);
178   proto.set_last_modified(in_progress_info.last_modified);
179   proto.set_mime_type(in_progress_info.mime_type);
180   proto.set_original_mime_type(in_progress_info.original_mime_type);
181   proto.set_total_bytes(in_progress_info.total_bytes);
182   base::Pickle current_path;
183   in_progress_info.current_path.WriteToPickle(&current_path);
184   proto.set_current_path(current_path.data(), current_path.size());
185   base::Pickle target_path;
186   in_progress_info.target_path.WriteToPickle(&target_path);
187   proto.set_target_path(target_path.data(), target_path.size());
188   proto.set_received_bytes(in_progress_info.received_bytes);
189   proto.set_start_time(
190       in_progress_info.start_time.is_null()
191           ? -1
192           : FromTimeToMilliseconds(in_progress_info.start_time));
193   proto.set_end_time(in_progress_info.end_time.is_null()
194                          ? -1
195                          : FromTimeToMilliseconds(in_progress_info.end_time));
196   for (size_t i = 0; i < in_progress_info.received_slices.size(); ++i) {
197     download_pb::ReceivedSlice* slice = proto.add_received_slices();
198     slice->set_received_bytes(
199         in_progress_info.received_slices[i].received_bytes);
200     slice->set_offset(in_progress_info.received_slices[i].offset);
201     slice->set_finished(in_progress_info.received_slices[i].finished);
202   }
203   proto.set_hash(in_progress_info.hash);
204   proto.set_transient(in_progress_info.transient);
205   proto.set_state(in_progress_info.state);
206   proto.set_danger_type(in_progress_info.danger_type);
207   proto.set_interrupt_reason(in_progress_info.interrupt_reason);
208   proto.set_paused(in_progress_info.paused);
209   proto.set_metered(in_progress_info.metered);
210   proto.set_bytes_wasted(in_progress_info.bytes_wasted);
211   proto.set_auto_resume_count(in_progress_info.auto_resume_count);
212   if (base::FeatureList::IsEnabled(download::features::kDownloadLater) &&
213       in_progress_info.download_schedule.has_value()) {
214     DCHECK_NE(in_progress_info.download_schedule->only_on_wifi(),
215               in_progress_info.metered);
216     auto download_schedule_proto =
217         std::make_unique<download_pb::DownloadSchedule>(DownloadScheduleToProto(
218             in_progress_info.download_schedule.value()));
219     proto.set_allocated_download_schedule(download_schedule_proto.release());
220   }
221 
222   return proto;
223 }
224 
225 // static
InProgressInfoFromProto(const download_pb::InProgressInfo & proto)226 InProgressInfo DownloadDBConversions::InProgressInfoFromProto(
227     const download_pb::InProgressInfo& proto) {
228   InProgressInfo info;
229   for (const auto& url : proto.url_chain())
230     info.url_chain.emplace_back(url);
231   info.referrer_url = GURL(proto.referrer_url());
232   info.site_url = GURL(proto.site_url());
233   info.tab_url = GURL(proto.tab_url());
234   info.tab_referrer_url = GURL(proto.tab_referrer_url());
235   info.fetch_error_body = proto.fetch_error_body();
236   for (const auto& header : proto.request_headers())
237     info.request_headers.emplace_back(HttpRequestHeaderFromProto(header));
238   info.etag = proto.etag();
239   info.last_modified = proto.last_modified();
240   info.mime_type = proto.mime_type();
241   info.original_mime_type = proto.original_mime_type();
242   info.total_bytes = proto.total_bytes();
243   base::PickleIterator current_path(
244       base::Pickle(proto.current_path().data(), proto.current_path().size()));
245   info.current_path.ReadFromPickle(&current_path);
246   base::PickleIterator target_path(
247       base::Pickle(proto.target_path().data(), proto.target_path().size()));
248   info.target_path.ReadFromPickle(&target_path);
249   info.received_bytes = proto.received_bytes();
250   info.start_time = proto.start_time() == -1
251                         ? base::Time()
252                         : FromMillisecondsToTime(proto.start_time());
253   info.end_time = proto.end_time() == -1
254                       ? base::Time()
255                       : FromMillisecondsToTime(proto.end_time());
256 
257   for (int i = 0; i < proto.received_slices_size(); ++i) {
258     info.received_slices.emplace_back(proto.received_slices(i).offset(),
259                                       proto.received_slices(i).received_bytes(),
260                                       proto.received_slices(i).finished());
261   }
262   info.hash = proto.hash();
263   info.transient = proto.transient();
264   info.state = static_cast<DownloadItem::DownloadState>(proto.state());
265   info.danger_type = static_cast<DownloadDangerType>(proto.danger_type());
266   info.interrupt_reason =
267       static_cast<DownloadInterruptReason>(proto.interrupt_reason());
268   info.paused = proto.paused();
269   info.metered = proto.metered();
270   info.bytes_wasted = proto.bytes_wasted();
271   info.auto_resume_count = proto.auto_resume_count();
272   if (base::FeatureList::IsEnabled(download::features::kDownloadLater) &&
273       proto.has_download_schedule()) {
274     info.download_schedule = DownloadScheduleFromProto(
275         proto.download_schedule(), !proto.metered() /*only_on_wifi*/);
276     DCHECK_NE(info.download_schedule->only_on_wifi(), info.metered);
277   }
278 
279   return info;
280 }
281 
UkmInfoFromProto(const download_pb::UkmInfo & proto)282 UkmInfo DownloadDBConversions::UkmInfoFromProto(
283     const download_pb::UkmInfo& proto) {
284   UkmInfo info;
285   info.download_source = DownloadSourceFromProto(proto.download_source());
286   info.ukm_download_id = proto.ukm_download_id();
287   return info;
288 }
289 
UkmInfoToProto(const UkmInfo & info)290 download_pb::UkmInfo DownloadDBConversions::UkmInfoToProto(
291     const UkmInfo& info) {
292   download_pb::UkmInfo proto;
293   proto.set_download_source(DownloadSourceToProto(info.download_source));
294   proto.set_ukm_download_id(info.ukm_download_id);
295   return proto;
296 }
297 
DownloadScheduleToProto(const DownloadSchedule & download_schedule)298 download_pb::DownloadSchedule DownloadDBConversions::DownloadScheduleToProto(
299     const DownloadSchedule& download_schedule) {
300   // download::DownloadSchedule.only_on_wifi is not persisted, use
301   // InProgressInfo.metered instead.
302   download_pb::DownloadSchedule proto;
303   if (download_schedule.start_time().has_value()) {
304     proto.set_start_time(
305         FromTimeToMilliseconds(download_schedule.start_time().value()));
306   }
307   return proto;
308 }
309 
DownloadScheduleFromProto(const download_pb::DownloadSchedule & proto,bool only_on_wifi)310 DownloadSchedule DownloadDBConversions::DownloadScheduleFromProto(
311     const download_pb::DownloadSchedule& proto,
312     bool only_on_wifi) {
313   base::Optional<base::Time> start_time;
314   if (proto.has_start_time())
315     start_time = FromMillisecondsToTime(proto.start_time());
316   return DownloadSchedule(only_on_wifi, std::move(start_time));
317 }
318 
DownloadInfoFromProto(const download_pb::DownloadInfo & proto)319 DownloadInfo DownloadDBConversions::DownloadInfoFromProto(
320     const download_pb::DownloadInfo& proto) {
321   DownloadInfo info;
322   info.guid = proto.guid();
323   info.id = proto.id();
324   if (proto.has_ukm_info())
325     info.ukm_info = UkmInfoFromProto(proto.ukm_info());
326   if (proto.has_in_progress_info())
327     info.in_progress_info = InProgressInfoFromProto(proto.in_progress_info());
328   return info;
329 }
330 
DownloadInfoToProto(const DownloadInfo & info)331 download_pb::DownloadInfo DownloadDBConversions::DownloadInfoToProto(
332     const DownloadInfo& info) {
333   download_pb::DownloadInfo proto;
334   proto.set_guid(info.guid);
335   proto.set_id(info.id);
336   if (info.ukm_info.has_value()) {
337     auto ukm_info = std::make_unique<download_pb::UkmInfo>(
338         UkmInfoToProto(info.ukm_info.value()));
339     proto.set_allocated_ukm_info(ukm_info.release());
340   }
341   if (info.in_progress_info.has_value()) {
342     auto in_progress_info = std::make_unique<download_pb::InProgressInfo>(
343         InProgressInfoToProto(info.in_progress_info.value()));
344     proto.set_allocated_in_progress_info(in_progress_info.release());
345   }
346   return proto;
347 }
348 
DownloadDBEntryFromProto(const download_pb::DownloadDBEntry & proto)349 DownloadDBEntry DownloadDBConversions::DownloadDBEntryFromProto(
350     const download_pb::DownloadDBEntry& proto) {
351   DownloadDBEntry entry;
352   if (proto.has_download_info())
353     entry.download_info = DownloadInfoFromProto(proto.download_info());
354   return entry;
355 }
356 
DownloadDBEntryToProto(const DownloadDBEntry & info)357 download_pb::DownloadDBEntry DownloadDBConversions::DownloadDBEntryToProto(
358     const DownloadDBEntry& info) {
359   download_pb::DownloadDBEntry proto;
360   if (info.download_info.has_value()) {
361     auto download_info = std::make_unique<download_pb::DownloadInfo>(
362         DownloadInfoToProto(info.download_info.value()));
363     proto.set_allocated_download_info(download_info.release());
364   }
365   return proto;
366 }
367 
DownloadDBEntryFromDownloadEntry(const DownloadEntry & entry)368 DownloadDBEntry DownloadDBConversions::DownloadDBEntryFromDownloadEntry(
369     const DownloadEntry& entry) {
370   DownloadDBEntry db_entry;
371   DownloadInfo download_info;
372   download_info.guid = entry.guid;
373 
374   UkmInfo ukm_info(entry.download_source, entry.ukm_download_id);
375 
376   InProgressInfo in_progress_info;
377   in_progress_info.fetch_error_body = entry.fetch_error_body;
378   in_progress_info.request_headers = entry.request_headers;
379 
380   download_info.ukm_info = ukm_info;
381   download_info.in_progress_info = in_progress_info;
382   db_entry.download_info = download_info;
383   return db_entry;
384 }
385 
386 }  // namespace download
387