chromium/ios/chrome/browser/reading_list/model/reading_list_web_state_observer.h

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef IOS_CHROME_BROWSER_READING_LIST_MODEL_READING_LIST_WEB_STATE_OBSERVER_H_
#define IOS_CHROME_BROWSER_READING_LIST_MODEL_READING_LIST_WEB_STATE_OBSERVER_H_

#import "base/memory/raw_ptr.h"
#import "base/timer/timer.h"
#import "components/reading_list/core/reading_list_model_observer.h"
#import "ios/web/public/web_state_observer.h"
#import "ios/web/public/web_state_user_data.h"
#import "url/gurl.h"

class ReadingListModel;

namespace web {
class NavigationItem;
}

// Observes the loading of pages coming from the reading list, determines
// whether loading an offline version of the page is needed, and actually
// trigger the loading of the offline page (if possible).
class ReadingListWebStateObserver
    : public ReadingListModelObserver,
      public web::WebStateObserver,
      public web::WebStateUserData<ReadingListWebStateObserver> {
 public:
  ReadingListWebStateObserver(const ReadingListWebStateObserver&) = delete;
  ReadingListWebStateObserver& operator=(const ReadingListWebStateObserver&) =
      delete;

  ~ReadingListWebStateObserver() override;

  // ReadingListModelObserver implementation.
  void ReadingListModelLoaded(const ReadingListModel* model) override;
  void ReadingListModelBeingDeleted(const ReadingListModel* model) override;

 private:
  friend class web::WebStateUserData<ReadingListWebStateObserver>;

  ReadingListWebStateObserver(web::WebState* web_state,
                              ReadingListModel* reading_list_model);

  // Looks at the loading percentage. If less than 25% * time, attemps to load
  // the offline version of that page.
  // `time` is the number of seconds since `StartCheckingProgress` was called.
  void VerifyIfReadingListEntryStartedLoading();

  friend class ReadingListWebStateObserverUserDataWrapper;

  // Stops checking the loading of the `pending_url_`.
  // The WebState will still be observed, but no action will be done on events.
  void StopCheckingProgress();

  // Loads the offline version of the URL in place of the current page.
  void LoadOfflineReadingListEntry();

  // Returns if the current page with `url` has an offline version that can be
  // displayed if the normal loading fails.
  bool IsUrlAvailableOffline(const GURL& url) const;

  // Checks if `item` should be observed or not.
  // A non-null item should be observed if it is not already loading an offline
  // URL.
  bool ShouldObserveItem(web::NavigationItem* item) const;

  // Starts checking that the current navigation is loading quickly enough [1].
  // If not, starts to load a distilled version of the page (if there is any).
  // [1] A page loading quickly enough is a page that has loaded 25% within
  // 1 second, 50% within 2 seconds and 75% within 3 seconds.
  void StartCheckingLoading();

  // WebContentsObserver implementation.
  void PageLoaded(
      web::WebState* web_state,
      web::PageLoadCompletionStatus load_completion_status) override;
  void WebStateDestroyed(web::WebState* web_state) override;
  void DidStartLoading(web::WebState* web_state) override;

  // The WebState this instance is observing. Will be null after
  // WebStateDestroyed has been called.
  raw_ptr<web::WebState> web_state_ = nullptr;

  raw_ptr<ReadingListModel> reading_list_model_;
  std::unique_ptr<base::RepeatingTimer> timer_;
  GURL pending_url_;
  int try_number_;
  bool last_load_was_offline_;
  web::PageLoadCompletionStatus last_load_result_;

  base::WeakPtrFactory<ReadingListWebStateObserver> weak_factory_{this};

  WEB_STATE_USER_DATA_KEY_DECL();
};

#endif  // IOS_CHROME_BROWSER_READING_LIST_MODEL_READING_LIST_WEB_STATE_OBSERVER_H_