OLD | NEW |
| (Empty) |
1 // Copyright 2016 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 "ios/chrome/browser/reading_list/reading_list_entry.h" | |
6 | |
7 #include "base/json/json_string_value_serializer.h" | |
8 #include "base/memory/ptr_util.h" | |
9 #include "components/sync/protocol/reading_list_specifics.pb.h" | |
10 #include "ios/chrome/browser/reading_list/offline_url_utils.h" | |
11 #include "ios/chrome/browser/reading_list/proto/reading_list.pb.h" | |
12 #include "net/base/backoff_entry_serializer.h" | |
13 | |
14 // The backoff time is the following: 10min, 10min, 1h, 2h, 2h..., starting | |
15 // after the first failure. | |
16 const net::BackoffEntry::Policy ReadingListEntry::kBackoffPolicy = { | |
17 // Number of initial errors (in sequence) to ignore before applying | |
18 // exponential back-off rules. | |
19 2, | |
20 | |
21 // Initial delay for exponential back-off in ms. | |
22 10 * 60 * 1000, // 10 minutes. | |
23 | |
24 // Factor by which the waiting time will be multiplied. | |
25 6, | |
26 | |
27 // Fuzzing percentage. ex: 10% will spread requests randomly | |
28 // between 90%-100% of the calculated time. | |
29 0.1, // 10%. | |
30 | |
31 // Maximum amount of time we are willing to delay our request in ms. | |
32 2 * 3600 * 1000, // 2 hours. | |
33 | |
34 // Time to keep an entry from being discarded even when it | |
35 // has no significant state, -1 to never discard. | |
36 -1, | |
37 | |
38 true, // Don't use initial delay unless the last request was an error. | |
39 }; | |
40 | |
41 ReadingListEntry::ReadingListEntry(const GURL& url, const std::string& title) | |
42 : ReadingListEntry(url, title, nullptr){}; | |
43 | |
44 ReadingListEntry::ReadingListEntry(const GURL& url, | |
45 const std::string& title, | |
46 std::unique_ptr<net::BackoffEntry> backoff) | |
47 : ReadingListEntry(url, | |
48 title, | |
49 0, | |
50 0, | |
51 WAITING, | |
52 base::FilePath(), | |
53 0, | |
54 std::move(backoff)) {} | |
55 | |
56 ReadingListEntry::ReadingListEntry( | |
57 const GURL& url, | |
58 const std::string& title, | |
59 int64_t creation_time, | |
60 int64_t update_time, | |
61 ReadingListEntry::DistillationState distilled_state, | |
62 const base::FilePath& distilled_path, | |
63 int failed_download_counter, | |
64 std::unique_ptr<net::BackoffEntry> backoff) | |
65 : url_(url), | |
66 title_(title), | |
67 distilled_path_(distilled_path), | |
68 distilled_state_(distilled_state), | |
69 failed_download_counter_(failed_download_counter), | |
70 creation_time_us_(creation_time), | |
71 update_time_us_(update_time) { | |
72 if (backoff) { | |
73 backoff_ = std::move(backoff); | |
74 } else { | |
75 backoff_ = base::MakeUnique<net::BackoffEntry>(&kBackoffPolicy); | |
76 } | |
77 if (creation_time_us_ == 0) { | |
78 DCHECK(update_time_us_ == 0); | |
79 creation_time_us_ = | |
80 (base::Time::Now() - base::Time::UnixEpoch()).InMicroseconds(); | |
81 update_time_us_ = creation_time_us_; | |
82 } | |
83 DCHECK(!url.is_empty()); | |
84 DCHECK(url.is_valid()); | |
85 } | |
86 | |
87 ReadingListEntry::ReadingListEntry(ReadingListEntry&& entry) | |
88 : url_(std::move(entry.url_)), | |
89 title_(std::move(entry.title_)), | |
90 distilled_path_(std::move(entry.distilled_path_)), | |
91 distilled_state_(std::move(entry.distilled_state_)), | |
92 backoff_(std::move(entry.backoff_)), | |
93 failed_download_counter_(std::move(entry.failed_download_counter_)), | |
94 creation_time_us_(std::move(entry.creation_time_us_)), | |
95 update_time_us_(std::move(entry.update_time_us_)) {} | |
96 | |
97 ReadingListEntry::~ReadingListEntry() {} | |
98 | |
99 const GURL& ReadingListEntry::URL() const { | |
100 return url_; | |
101 } | |
102 | |
103 const std::string& ReadingListEntry::Title() const { | |
104 return title_; | |
105 } | |
106 | |
107 ReadingListEntry::DistillationState ReadingListEntry::DistilledState() const { | |
108 return distilled_state_; | |
109 } | |
110 | |
111 const base::FilePath& ReadingListEntry::DistilledPath() const { | |
112 return distilled_path_; | |
113 } | |
114 | |
115 const GURL ReadingListEntry::DistilledURL() const { | |
116 return reading_list::DistilledURLForPath(distilled_path_); | |
117 } | |
118 | |
119 base::TimeDelta ReadingListEntry::TimeUntilNextTry() const { | |
120 return backoff_->GetTimeUntilRelease(); | |
121 } | |
122 | |
123 int ReadingListEntry::FailedDownloadCounter() const { | |
124 return failed_download_counter_; | |
125 } | |
126 | |
127 ReadingListEntry& ReadingListEntry::operator=(ReadingListEntry&& other) { | |
128 url_ = std::move(other.url_); | |
129 title_ = std::move(other.title_); | |
130 distilled_path_ = std::move(other.distilled_path_); | |
131 distilled_state_ = std::move(other.distilled_state_); | |
132 backoff_ = std::move(other.backoff_); | |
133 failed_download_counter_ = std::move(other.failed_download_counter_); | |
134 creation_time_us_ = std::move(other.creation_time_us_); | |
135 update_time_us_ = std::move(other.update_time_us_); | |
136 return *this; | |
137 } | |
138 | |
139 bool ReadingListEntry::operator==(const ReadingListEntry& other) const { | |
140 return url_ == other.url_; | |
141 } | |
142 | |
143 void ReadingListEntry::SetTitle(const std::string& title) { | |
144 title_ = title; | |
145 } | |
146 | |
147 void ReadingListEntry::SetDistilledPath(const base::FilePath& path) { | |
148 DCHECK(!path.empty()); | |
149 distilled_path_ = path; | |
150 distilled_state_ = PROCESSED; | |
151 backoff_->Reset(); | |
152 failed_download_counter_ = 0; | |
153 } | |
154 | |
155 void ReadingListEntry::SetDistilledState(DistillationState distilled_state) { | |
156 DCHECK(distilled_state != PROCESSED); // use SetDistilledPath instead. | |
157 DCHECK(distilled_state != WAITING); | |
158 // Increase time until next retry exponentially if the state change from a | |
159 // non-error state to an error state. | |
160 if ((distilled_state == WILL_RETRY || distilled_state == ERROR) && | |
161 distilled_state_ != WILL_RETRY && distilled_state_ != ERROR) { | |
162 backoff_->InformOfRequest(false); | |
163 failed_download_counter_++; | |
164 } | |
165 | |
166 distilled_state_ = distilled_state; | |
167 distilled_path_ = base::FilePath(); | |
168 } | |
169 | |
170 int64_t ReadingListEntry::UpdateTime() const { | |
171 return update_time_us_; | |
172 } | |
173 | |
174 int64_t ReadingListEntry::CreationTime() const { | |
175 return creation_time_us_; | |
176 } | |
177 | |
178 void ReadingListEntry::MarkEntryUpdated() { | |
179 update_time_us_ = | |
180 (base::Time::Now() - base::Time::UnixEpoch()).InMicroseconds(); | |
181 } | |
182 | |
183 // static | |
184 std::unique_ptr<ReadingListEntry> ReadingListEntry::FromReadingListLocal( | |
185 const reading_list::ReadingListLocal& pb_entry) { | |
186 if (!pb_entry.has_url()) { | |
187 return nullptr; | |
188 } | |
189 GURL url(pb_entry.url()); | |
190 if (url.is_empty() || !url.is_valid()) { | |
191 return nullptr; | |
192 } | |
193 std::string title; | |
194 if (pb_entry.has_title()) { | |
195 title = pb_entry.title(); | |
196 } | |
197 | |
198 int64_t creation_time_us = 0; | |
199 if (pb_entry.has_creation_time_us()) { | |
200 creation_time_us = pb_entry.creation_time_us(); | |
201 } | |
202 | |
203 int64_t update_time_us = 0; | |
204 if (pb_entry.has_update_time_us()) { | |
205 update_time_us = pb_entry.update_time_us(); | |
206 } | |
207 | |
208 ReadingListEntry::DistillationState distillation_state = | |
209 ReadingListEntry::WAITING; | |
210 if (pb_entry.has_distillation_state()) { | |
211 switch (pb_entry.distillation_state()) { | |
212 case reading_list::ReadingListLocal::WAITING: | |
213 distillation_state = ReadingListEntry::WAITING; | |
214 break; | |
215 case reading_list::ReadingListLocal::PROCESSING: | |
216 distillation_state = ReadingListEntry::PROCESSING; | |
217 break; | |
218 case reading_list::ReadingListLocal::PROCESSED: | |
219 distillation_state = ReadingListEntry::PROCESSED; | |
220 break; | |
221 case reading_list::ReadingListLocal::WILL_RETRY: | |
222 distillation_state = ReadingListEntry::WILL_RETRY; | |
223 break; | |
224 case reading_list::ReadingListLocal::ERROR: | |
225 distillation_state = ReadingListEntry::ERROR; | |
226 break; | |
227 } | |
228 } | |
229 | |
230 base::FilePath distilled_path; | |
231 if (pb_entry.has_distilled_path()) { | |
232 distilled_path = base::FilePath(pb_entry.distilled_path()); | |
233 } | |
234 | |
235 int64_t failed_download_counter = 0; | |
236 if (pb_entry.has_failed_download_counter()) { | |
237 failed_download_counter = pb_entry.failed_download_counter(); | |
238 } | |
239 | |
240 std::unique_ptr<net::BackoffEntry> backoff; | |
241 if (pb_entry.has_backoff()) { | |
242 JSONStringValueDeserializer deserializer(pb_entry.backoff()); | |
243 std::unique_ptr<base::Value> value( | |
244 deserializer.Deserialize(nullptr, nullptr)); | |
245 if (value) { | |
246 backoff = net::BackoffEntrySerializer::DeserializeFromValue( | |
247 *value, &kBackoffPolicy, nullptr, base::Time::Now()); | |
248 } | |
249 } | |
250 | |
251 return base::WrapUnique<ReadingListEntry>(new ReadingListEntry( | |
252 url, title, creation_time_us, update_time_us, distillation_state, | |
253 distilled_path, failed_download_counter, std::move(backoff))); | |
254 } | |
255 | |
256 // static | |
257 std::unique_ptr<ReadingListEntry> ReadingListEntry::FromReadingListSpecifics( | |
258 const sync_pb::ReadingListSpecifics& pb_entry) { | |
259 if (!pb_entry.has_url()) { | |
260 return nullptr; | |
261 } | |
262 GURL url(pb_entry.url()); | |
263 if (url.is_empty() || !url.is_valid()) { | |
264 return nullptr; | |
265 } | |
266 std::string title; | |
267 if (pb_entry.has_title()) { | |
268 title = pb_entry.title(); | |
269 } | |
270 | |
271 int64_t creation_time_us = 0; | |
272 if (pb_entry.has_creation_time_us()) { | |
273 creation_time_us = pb_entry.creation_time_us(); | |
274 } | |
275 | |
276 int64_t update_time_us = 0; | |
277 if (pb_entry.has_update_time_us()) { | |
278 update_time_us = pb_entry.update_time_us(); | |
279 } | |
280 | |
281 return base::WrapUnique<ReadingListEntry>( | |
282 new ReadingListEntry(url, title, creation_time_us, update_time_us, | |
283 WAITING, base::FilePath(), 0, nullptr)); | |
284 } | |
285 | |
286 void ReadingListEntry::MergeLocalStateFrom(ReadingListEntry& other) { | |
287 distilled_path_ = std::move(other.distilled_path_); | |
288 distilled_state_ = std::move(other.distilled_state_); | |
289 backoff_ = std::move(other.backoff_); | |
290 failed_download_counter_ = std::move(other.failed_download_counter_); | |
291 } | |
292 | |
293 std::unique_ptr<reading_list::ReadingListLocal> | |
294 ReadingListEntry::AsReadingListLocal(bool read) const { | |
295 std::unique_ptr<reading_list::ReadingListLocal> pb_entry = | |
296 base::MakeUnique<reading_list::ReadingListLocal>(); | |
297 | |
298 // URL is used as the key for the database and sync as there is only one entry | |
299 // per URL. | |
300 pb_entry->set_entry_id(URL().spec()); | |
301 pb_entry->set_title(Title()); | |
302 pb_entry->set_url(URL().spec()); | |
303 pb_entry->set_creation_time_us(CreationTime()); | |
304 pb_entry->set_update_time_us(UpdateTime()); | |
305 | |
306 if (read) { | |
307 pb_entry->set_status(reading_list::ReadingListLocal::READ); | |
308 } else { | |
309 pb_entry->set_status(reading_list::ReadingListLocal::UNREAD); | |
310 } | |
311 | |
312 reading_list::ReadingListLocal::DistillationState distilation_state; | |
313 switch (DistilledState()) { | |
314 case ReadingListEntry::WAITING: | |
315 distilation_state = reading_list::ReadingListLocal::WAITING; | |
316 break; | |
317 case ReadingListEntry::PROCESSING: | |
318 distilation_state = reading_list::ReadingListLocal::PROCESSING; | |
319 break; | |
320 case ReadingListEntry::PROCESSED: | |
321 distilation_state = reading_list::ReadingListLocal::PROCESSED; | |
322 break; | |
323 case ReadingListEntry::WILL_RETRY: | |
324 distilation_state = reading_list::ReadingListLocal::WILL_RETRY; | |
325 break; | |
326 case ReadingListEntry::ERROR: | |
327 distilation_state = reading_list::ReadingListLocal::ERROR; | |
328 break; | |
329 } | |
330 pb_entry->set_distillation_state(distilation_state); | |
331 if (!DistilledPath().empty()) { | |
332 pb_entry->set_distilled_path(DistilledPath().value()); | |
333 } | |
334 pb_entry->set_failed_download_counter(failed_download_counter_); | |
335 | |
336 if (backoff_) { | |
337 std::unique_ptr<base::Value> backoff = | |
338 net::BackoffEntrySerializer::SerializeToValue(*backoff_, | |
339 base::Time::Now()); | |
340 | |
341 std::string output; | |
342 JSONStringValueSerializer serializer(&output); | |
343 serializer.Serialize(*backoff); | |
344 pb_entry->set_backoff(output); | |
345 } | |
346 return pb_entry; | |
347 } | |
348 | |
349 std::unique_ptr<sync_pb::ReadingListSpecifics> | |
350 ReadingListEntry::AsReadingListSpecifics(bool read) const { | |
351 std::unique_ptr<sync_pb::ReadingListSpecifics> pb_entry = | |
352 base::MakeUnique<sync_pb::ReadingListSpecifics>(); | |
353 | |
354 // URL is used as the key for the database and sync as there is only one entry | |
355 // per URL. | |
356 pb_entry->set_entry_id(URL().spec()); | |
357 pb_entry->set_title(Title()); | |
358 pb_entry->set_url(URL().spec()); | |
359 pb_entry->set_creation_time_us(CreationTime()); | |
360 pb_entry->set_update_time_us(UpdateTime()); | |
361 | |
362 if (read) { | |
363 pb_entry->set_status(sync_pb::ReadingListSpecifics::READ); | |
364 } else { | |
365 pb_entry->set_status(sync_pb::ReadingListSpecifics::UNREAD); | |
366 } | |
367 | |
368 return pb_entry; | |
369 } | |
370 | |
371 bool ReadingListEntry::CompareEntryUpdateTime(const ReadingListEntry& lhs, | |
372 const ReadingListEntry& rhs) { | |
373 return lhs.UpdateTime() > rhs.UpdateTime(); | |
374 } | |
OLD | NEW |