Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(247)

Side by Side Diff: components/reading_list/ios/reading_list_entry.cc

Issue 2763233003: Move ReadingList model to components/reading_list/core (Closed)
Patch Set: feedback Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 "components/reading_list/ios/reading_list_entry.h"
6
7 #include "base/json/json_string_value_serializer.h"
8 #include "base/memory/ptr_util.h"
9 #include "components/reading_list/ios/offline_url_utils.h"
10 #include "components/reading_list/ios/proto/reading_list.pb.h"
11 #include "components/reading_list/ios/reading_list_store.h"
12 #include "components/sync/protocol/reading_list_specifics.pb.h"
13 #include "net/base/backoff_entry_serializer.h"
14
15 namespace {
16 // Converts |time| to the number of microseconds since Jan 1st 1970.
17 int64_t TimeToUS(const base::Time& time) {
18 return (time - base::Time::UnixEpoch()).InMicroseconds();
19 }
20 }
21
22 // The backoff time is the following: 10min, 10min, 1h, 2h, 2h..., starting
23 // after the first failure.
24 const net::BackoffEntry::Policy ReadingListEntry::kBackoffPolicy = {
25 // Number of initial errors (in sequence) to ignore before applying
26 // exponential back-off rules.
27 2,
28
29 // Initial delay for exponential back-off in ms.
30 10 * 60 * 1000, // 10 minutes.
31
32 // Factor by which the waiting time will be multiplied.
33 6,
34
35 // Fuzzing percentage. ex: 10% will spread requests randomly
36 // between 90%-100% of the calculated time.
37 0.1, // 10%.
38
39 // Maximum amount of time we are willing to delay our request in ms.
40 2 * 3600 * 1000, // 2 hours.
41
42 // Time to keep an entry from being discarded even when it
43 // has no significant state, -1 to never discard.
44 -1,
45
46 true, // Don't use initial delay unless the last request was an error.
47 };
48
49 ReadingListEntry::ReadingListEntry(const GURL& url,
50 const std::string& title,
51 const base::Time& now)
52 : ReadingListEntry(url, title, now, nullptr){};
53
54 ReadingListEntry::ReadingListEntry(const GURL& url,
55 const std::string& title,
56 const base::Time& now,
57 std::unique_ptr<net::BackoffEntry> backoff)
58 : ReadingListEntry(url,
59 title,
60 UNSEEN,
61 TimeToUS(now),
62 0,
63 TimeToUS(now),
64 TimeToUS(now),
65 WAITING,
66 base::FilePath(),
67 GURL(),
68 0,
69 0,
70 0,
71 std::move(backoff)) {}
72
73 ReadingListEntry::ReadingListEntry(
74 const GURL& url,
75 const std::string& title,
76 State state,
77 int64_t creation_time,
78 int64_t first_read_time,
79 int64_t update_time,
80 int64_t update_title_time,
81 ReadingListEntry::DistillationState distilled_state,
82 const base::FilePath& distilled_path,
83 const GURL& distilled_url,
84 int64_t distillation_time,
85 int64_t distillation_size,
86 int failed_download_counter,
87 std::unique_ptr<net::BackoffEntry> backoff)
88 : url_(url),
89 title_(title),
90 state_(state),
91 distilled_path_(distilled_path),
92 distilled_url_(distilled_url),
93 distilled_state_(distilled_state),
94 failed_download_counter_(failed_download_counter),
95 creation_time_us_(creation_time),
96 first_read_time_us_(first_read_time),
97 update_time_us_(update_time),
98 update_title_time_us_(update_title_time),
99 distillation_time_us_(distillation_time),
100 distillation_size_(distillation_size) {
101 if (backoff) {
102 backoff_ = std::move(backoff);
103 } else {
104 backoff_ = base::MakeUnique<net::BackoffEntry>(&kBackoffPolicy);
105 }
106 DCHECK(creation_time_us_);
107 DCHECK(update_time_us_);
108 DCHECK(update_title_time_us_);
109 DCHECK(!url.is_empty());
110 DCHECK(url.is_valid());
111 }
112
113 ReadingListEntry::ReadingListEntry(ReadingListEntry&& entry)
114 : url_(std::move(entry.url_)),
115 title_(std::move(entry.title_)),
116 state_(std::move(entry.state_)),
117 distilled_path_(std::move(entry.distilled_path_)),
118 distilled_url_(std::move(entry.distilled_url_)),
119 distilled_state_(std::move(entry.distilled_state_)),
120 backoff_(std::move(entry.backoff_)),
121 failed_download_counter_(std::move(entry.failed_download_counter_)),
122 creation_time_us_(std::move(entry.creation_time_us_)),
123 first_read_time_us_(std::move(entry.first_read_time_us_)),
124 update_time_us_(std::move(entry.update_time_us_)),
125 update_title_time_us_(std::move(entry.update_title_time_us_)),
126 distillation_time_us_(std::move(entry.distillation_time_us_)),
127 distillation_size_(std::move(entry.distillation_size_)) {}
128
129 ReadingListEntry::~ReadingListEntry() {}
130
131 const GURL& ReadingListEntry::URL() const {
132 return url_;
133 }
134
135 const std::string& ReadingListEntry::Title() const {
136 return title_;
137 }
138
139 ReadingListEntry::DistillationState ReadingListEntry::DistilledState() const {
140 return distilled_state_;
141 }
142
143 const base::FilePath& ReadingListEntry::DistilledPath() const {
144 return distilled_path_;
145 }
146
147 const GURL& ReadingListEntry::DistilledURL() const {
148 return distilled_url_;
149 }
150
151 int64_t ReadingListEntry::DistillationTime() const {
152 return distillation_time_us_;
153 }
154
155 int64_t ReadingListEntry::DistillationSize() const {
156 return distillation_size_;
157 }
158
159 base::TimeDelta ReadingListEntry::TimeUntilNextTry() const {
160 return backoff_->GetTimeUntilRelease();
161 }
162
163 int ReadingListEntry::FailedDownloadCounter() const {
164 return failed_download_counter_;
165 }
166
167 ReadingListEntry& ReadingListEntry::operator=(ReadingListEntry&& other) {
168 url_ = std::move(other.url_);
169 title_ = std::move(other.title_);
170 distilled_path_ = std::move(other.distilled_path_);
171 distilled_url_ = std::move(other.distilled_url_);
172 distilled_state_ = std::move(other.distilled_state_);
173 backoff_ = std::move(other.backoff_);
174 state_ = std::move(other.state_);
175 failed_download_counter_ = std::move(other.failed_download_counter_);
176 creation_time_us_ = std::move(other.creation_time_us_);
177 first_read_time_us_ = std::move(other.first_read_time_us_);
178 update_time_us_ = std::move(other.update_time_us_);
179 update_title_time_us_ = std::move(other.update_title_time_us_);
180 distillation_time_us_ = std::move(other.distillation_time_us_);
181 distillation_size_ = std::move(other.distillation_size_);
182 return *this;
183 }
184
185 bool ReadingListEntry::operator==(const ReadingListEntry& other) const {
186 return url_ == other.url_;
187 }
188
189 void ReadingListEntry::SetTitle(const std::string& title,
190 const base::Time& now) {
191 title_ = title;
192 update_title_time_us_ = TimeToUS(now);
193 }
194
195 void ReadingListEntry::SetRead(bool read, const base::Time& now) {
196 State previous_state = state_;
197 state_ = read ? READ : UNREAD;
198 if (state_ == previous_state) {
199 return;
200 }
201 if (FirstReadTime() == 0 && read) {
202 first_read_time_us_ = TimeToUS(now);
203 }
204 if (!(previous_state == UNSEEN && state_ == UNREAD)) {
205 // If changing UNSEEN -> UNREAD, entry is not marked updated to preserve
206 // order in Reading List View.
207 MarkEntryUpdated(now);
208 }
209 }
210
211 bool ReadingListEntry::IsRead() const {
212 return state_ == READ;
213 }
214
215 bool ReadingListEntry::HasBeenSeen() const {
216 return state_ != UNSEEN;
217 }
218
219 void ReadingListEntry::SetDistilledInfo(const base::FilePath& path,
220 const GURL& distilled_url,
221 int64_t distilation_size,
222 const base::Time& distilation_time) {
223 DCHECK(!path.empty());
224 DCHECK(distilled_url.is_valid());
225 distilled_path_ = path;
226 distilled_state_ = PROCESSED;
227 distilled_url_ = distilled_url;
228 distillation_time_us_ = TimeToUS(distilation_time);
229 distillation_size_ = distilation_size;
230 backoff_->Reset();
231 failed_download_counter_ = 0;
232 }
233
234 void ReadingListEntry::SetDistilledState(DistillationState distilled_state) {
235 DCHECK(distilled_state != PROCESSED); // use SetDistilledPath instead.
236 DCHECK(distilled_state != WAITING);
237 // Increase time until next retry exponentially if the state change from a
238 // non-error state to an error state.
239 if ((distilled_state == WILL_RETRY || distilled_state == ERROR) &&
240 distilled_state_ != WILL_RETRY && distilled_state_ != ERROR) {
241 backoff_->InformOfRequest(false);
242 failed_download_counter_++;
243 }
244
245 distilled_state_ = distilled_state;
246 distilled_path_ = base::FilePath();
247 distilled_url_ = GURL::EmptyGURL();
248 }
249
250 int64_t ReadingListEntry::UpdateTime() const {
251 return update_time_us_;
252 }
253
254 int64_t ReadingListEntry::UpdateTitleTime() const {
255 return update_title_time_us_;
256 }
257
258 int64_t ReadingListEntry::CreationTime() const {
259 return creation_time_us_;
260 }
261
262 int64_t ReadingListEntry::FirstReadTime() const {
263 return first_read_time_us_;
264 }
265
266 void ReadingListEntry::MarkEntryUpdated(const base::Time& now) {
267 update_time_us_ = TimeToUS(now);
268 }
269
270 // static
271 std::unique_ptr<ReadingListEntry> ReadingListEntry::FromReadingListLocal(
272 const reading_list::ReadingListLocal& pb_entry,
273 const base::Time& now) {
274 if (!pb_entry.has_url()) {
275 return nullptr;
276 }
277 GURL url(pb_entry.url());
278 if (url.is_empty() || !url.is_valid()) {
279 return nullptr;
280 }
281 std::string title;
282 if (pb_entry.has_title()) {
283 title = pb_entry.title();
284 }
285
286 int64_t creation_time_us = 0;
287 if (pb_entry.has_creation_time_us()) {
288 creation_time_us = pb_entry.creation_time_us();
289 } else {
290 creation_time_us = (now - base::Time::UnixEpoch()).InMicroseconds();
291 }
292
293 int64_t first_read_time_us = 0;
294 if (pb_entry.has_first_read_time_us()) {
295 first_read_time_us = pb_entry.first_read_time_us();
296 }
297
298 int64_t update_time_us = creation_time_us;
299 if (pb_entry.has_update_time_us()) {
300 update_time_us = pb_entry.update_time_us();
301 }
302
303 int64_t update_title_time_us = creation_time_us;
304 if (pb_entry.has_update_title_time_us()) {
305 update_title_time_us = pb_entry.update_title_time_us();
306 }
307
308 State state = UNSEEN;
309 if (pb_entry.has_status()) {
310 switch (pb_entry.status()) {
311 case reading_list::ReadingListLocal::READ:
312 state = READ;
313 break;
314 case reading_list::ReadingListLocal::UNREAD:
315 state = UNREAD;
316 break;
317 case reading_list::ReadingListLocal::UNSEEN:
318 state = UNSEEN;
319 break;
320 }
321 }
322
323 ReadingListEntry::DistillationState distillation_state =
324 ReadingListEntry::WAITING;
325 if (pb_entry.has_distillation_state()) {
326 switch (pb_entry.distillation_state()) {
327 case reading_list::ReadingListLocal::WAITING:
328 distillation_state = ReadingListEntry::WAITING;
329 break;
330 case reading_list::ReadingListLocal::PROCESSING:
331 distillation_state = ReadingListEntry::PROCESSING;
332 break;
333 case reading_list::ReadingListLocal::PROCESSED:
334 distillation_state = ReadingListEntry::PROCESSED;
335 break;
336 case reading_list::ReadingListLocal::WILL_RETRY:
337 distillation_state = ReadingListEntry::WILL_RETRY;
338 break;
339 case reading_list::ReadingListLocal::ERROR:
340 distillation_state = ReadingListEntry::ERROR;
341 break;
342 }
343 }
344
345 base::FilePath distilled_path;
346 if (pb_entry.has_distilled_path()) {
347 distilled_path = base::FilePath(pb_entry.distilled_path());
348 }
349
350 GURL distilled_url;
351 if (pb_entry.has_distilled_url()) {
352 distilled_url = GURL(pb_entry.distilled_url());
353 }
354
355 int64_t distillation_time_us = 0;
356 if (pb_entry.has_distillation_time_us()) {
357 distillation_time_us = pb_entry.distillation_time_us();
358 }
359
360 int64_t distillation_size = 0;
361 if (pb_entry.has_distillation_size()) {
362 distillation_size = pb_entry.distillation_size();
363 }
364
365 int64_t failed_download_counter = 0;
366 if (pb_entry.has_failed_download_counter()) {
367 failed_download_counter = pb_entry.failed_download_counter();
368 }
369
370 std::unique_ptr<net::BackoffEntry> backoff;
371 if (pb_entry.has_backoff()) {
372 JSONStringValueDeserializer deserializer(pb_entry.backoff());
373 std::unique_ptr<base::Value> value(
374 deserializer.Deserialize(nullptr, nullptr));
375 if (value) {
376 backoff = net::BackoffEntrySerializer::DeserializeFromValue(
377 *value, &kBackoffPolicy, nullptr, now);
378 }
379 }
380
381 return base::WrapUnique<ReadingListEntry>(new ReadingListEntry(
382 url, title, state, creation_time_us, first_read_time_us, update_time_us,
383 update_title_time_us, distillation_state, distilled_path, distilled_url,
384 distillation_time_us, distillation_size, failed_download_counter,
385 std::move(backoff)));
386 }
387
388 // static
389 std::unique_ptr<ReadingListEntry> ReadingListEntry::FromReadingListSpecifics(
390 const sync_pb::ReadingListSpecifics& pb_entry,
391 const base::Time& now) {
392 if (!pb_entry.has_url()) {
393 return nullptr;
394 }
395 GURL url(pb_entry.url());
396 if (url.is_empty() || !url.is_valid()) {
397 return nullptr;
398 }
399 std::string title;
400 if (pb_entry.has_title()) {
401 title = pb_entry.title();
402 }
403
404 int64_t creation_time_us = TimeToUS(now);
405 if (pb_entry.has_creation_time_us()) {
406 creation_time_us = pb_entry.creation_time_us();
407 }
408
409 int64_t first_read_time_us = 0;
410 if (pb_entry.has_first_read_time_us()) {
411 first_read_time_us = pb_entry.first_read_time_us();
412 }
413
414 int64_t update_time_us = creation_time_us;
415 if (pb_entry.has_update_time_us()) {
416 update_time_us = pb_entry.update_time_us();
417 }
418
419 int64_t update_title_time_us = creation_time_us;
420 if (pb_entry.has_update_title_time_us()) {
421 update_title_time_us = pb_entry.update_title_time_us();
422 }
423
424 State state = UNSEEN;
425 if (pb_entry.has_status()) {
426 switch (pb_entry.status()) {
427 case sync_pb::ReadingListSpecifics::READ:
428 state = READ;
429 break;
430 case sync_pb::ReadingListSpecifics::UNREAD:
431 state = UNREAD;
432 break;
433 case sync_pb::ReadingListSpecifics::UNSEEN:
434 state = UNSEEN;
435 break;
436 }
437 }
438
439 return base::WrapUnique<ReadingListEntry>(new ReadingListEntry(
440 url, title, state, creation_time_us, first_read_time_us, update_time_us,
441 update_title_time_us, WAITING, base::FilePath(), GURL(), 0, 0, 0,
442 nullptr));
443 }
444
445 void ReadingListEntry::MergeWithEntry(const ReadingListEntry& other) {
446 #if !defined(NDEBUG)
447 // Checks that the result entry respects the sync order.
448 std::unique_ptr<sync_pb::ReadingListSpecifics> old_this_pb(
449 AsReadingListSpecifics());
450 std::unique_ptr<sync_pb::ReadingListSpecifics> other_pb(
451 other.AsReadingListSpecifics());
452 #endif
453 DCHECK(url_ == other.url_);
454 if (update_title_time_us_ < other.update_title_time_us_) {
455 // Take the most recent title updated.
456 title_ = std::move(other.title_);
457 update_title_time_us_ = std::move(other.update_title_time_us_);
458 } else if (update_title_time_us_ == other.update_title_time_us_) {
459 if (title_.compare(other.title_) < 0) {
460 // Take the last in alphabetical order or the longer one.
461 // This ensure empty string is replaced.
462 title_ = std::move(other.title_);
463 }
464 }
465 if (creation_time_us_ < other.creation_time_us_) {
466 creation_time_us_ = std::move(other.creation_time_us_);
467 first_read_time_us_ = std::move(other.first_read_time_us_);
468 } else if (creation_time_us_ == other.creation_time_us_) {
469 // The first_time_read_us from |other| is used if
470 // - this.first_time_read_us == 0: the entry was never read in this device.
471 // - this.first_time_read_us > other.first_time_read_us: the entry was
472 // first read on another device.
473 if (first_read_time_us_ == 0 ||
474 (other.first_read_time_us_ != 0 &&
475 other.first_read_time_us_ < first_read_time_us_)) {
476 first_read_time_us_ = std::move(other.first_read_time_us_);
477 }
478 }
479 if (update_time_us_ < other.update_time_us_) {
480 update_time_us_ = std::move(other.update_time_us_);
481 state_ = std::move(other.state_);
482 } else if (update_time_us_ == other.update_time_us_) {
483 if (state_ == UNSEEN) {
484 state_ = std::move(other.state_);
485 } else if (other.state_ == READ) {
486 state_ = std::move(other.state_);
487 }
488 }
489 #if !defined(NDEBUG)
490 std::unique_ptr<sync_pb::ReadingListSpecifics> new_this_pb(
491 AsReadingListSpecifics());
492 DCHECK(ReadingListStore::CompareEntriesForSync(*old_this_pb, *new_this_pb));
493 DCHECK(ReadingListStore::CompareEntriesForSync(*other_pb, *new_this_pb));
494 #endif
495 }
496
497 std::unique_ptr<reading_list::ReadingListLocal>
498 ReadingListEntry::AsReadingListLocal(const base::Time& now) const {
499 std::unique_ptr<reading_list::ReadingListLocal> pb_entry =
500 base::MakeUnique<reading_list::ReadingListLocal>();
501
502 // URL is used as the key for the database and sync as there is only one entry
503 // per URL.
504 pb_entry->set_entry_id(URL().spec());
505 pb_entry->set_title(Title());
506 pb_entry->set_url(URL().spec());
507 pb_entry->set_creation_time_us(CreationTime());
508 pb_entry->set_first_read_time_us(FirstReadTime());
509 pb_entry->set_update_time_us(UpdateTime());
510 pb_entry->set_update_title_time_us(UpdateTitleTime());
511
512 switch (state_) {
513 case READ:
514 pb_entry->set_status(reading_list::ReadingListLocal::READ);
515 break;
516 case UNREAD:
517 pb_entry->set_status(reading_list::ReadingListLocal::UNREAD);
518 break;
519 case UNSEEN:
520 pb_entry->set_status(reading_list::ReadingListLocal::UNSEEN);
521 break;
522 }
523
524 reading_list::ReadingListLocal::DistillationState distilation_state;
525 switch (DistilledState()) {
526 case ReadingListEntry::WAITING:
527 distilation_state = reading_list::ReadingListLocal::WAITING;
528 break;
529 case ReadingListEntry::PROCESSING:
530 distilation_state = reading_list::ReadingListLocal::PROCESSING;
531 break;
532 case ReadingListEntry::PROCESSED:
533 distilation_state = reading_list::ReadingListLocal::PROCESSED;
534 break;
535 case ReadingListEntry::WILL_RETRY:
536 distilation_state = reading_list::ReadingListLocal::WILL_RETRY;
537 break;
538 case ReadingListEntry::ERROR:
539 distilation_state = reading_list::ReadingListLocal::ERROR;
540 break;
541 }
542 pb_entry->set_distillation_state(distilation_state);
543 if (!DistilledPath().empty()) {
544 pb_entry->set_distilled_path(DistilledPath().value());
545 }
546 if (DistilledURL().is_valid()) {
547 pb_entry->set_distilled_url(DistilledURL().spec());
548 }
549 if (DistillationTime()) {
550 pb_entry->set_distillation_time_us(DistillationTime());
551 }
552 if (DistillationSize()) {
553 pb_entry->set_distillation_size(DistillationSize());
554 }
555
556 pb_entry->set_failed_download_counter(failed_download_counter_);
557
558 if (backoff_) {
559 std::unique_ptr<base::Value> backoff =
560 net::BackoffEntrySerializer::SerializeToValue(*backoff_, now);
561
562 std::string output;
563 JSONStringValueSerializer serializer(&output);
564 serializer.Serialize(*backoff);
565 pb_entry->set_backoff(output);
566 }
567 return pb_entry;
568 }
569
570 std::unique_ptr<sync_pb::ReadingListSpecifics>
571 ReadingListEntry::AsReadingListSpecifics() const {
572 std::unique_ptr<sync_pb::ReadingListSpecifics> pb_entry =
573 base::MakeUnique<sync_pb::ReadingListSpecifics>();
574
575 // URL is used as the key for the database and sync as there is only one entry
576 // per URL.
577 pb_entry->set_entry_id(URL().spec());
578 pb_entry->set_title(Title());
579 pb_entry->set_url(URL().spec());
580 pb_entry->set_creation_time_us(CreationTime());
581 pb_entry->set_first_read_time_us(FirstReadTime());
582 pb_entry->set_update_time_us(UpdateTime());
583 pb_entry->set_update_title_time_us(UpdateTitleTime());
584
585 switch (state_) {
586 case READ:
587 pb_entry->set_status(sync_pb::ReadingListSpecifics::READ);
588 break;
589 case UNREAD:
590 pb_entry->set_status(sync_pb::ReadingListSpecifics::UNREAD);
591 break;
592 case UNSEEN:
593 pb_entry->set_status(sync_pb::ReadingListSpecifics::UNSEEN);
594 break;
595 }
596
597 return pb_entry;
598 }
OLDNEW
« no previous file with comments | « components/reading_list/ios/reading_list_entry.h ('k') | components/reading_list/ios/reading_list_entry_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698