text: // 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 "chrome/browser/ui/thumbnails/thumbnail_readiness_tracker.h"
  
  #include <utility>
  
  #include "content/public/browser/navigation_handle.h"
  
  namespace {
  
  bool NavigationShouldInvalidateThumbnail(
      content::NavigationHandle* navigation) {
    // Ignore subframe navigations.
    if (!navigation->IsInPrimaryMainFrame())
      return false;
  
    // Some navigations change the tab URL but don't create a new
    // document. They aren't considered loading; onload is never triggered
    // after they complete. They shouldn't affect the thumbnail.
    //
    // See crbug.com/1120940 for why this is necessary.
    if (navigation->IsSameDocument())
      return false;
  
    return true;
  }
  
  }  // namespace
  
  ThumbnailReadinessTracker::ThumbnailReadinessTracker(
      content::WebContents* web_contents,
      ReadinessChangeCallback callback)
      : content::WebContentsObserver(web_contents),
        callback_(std::move(callback)) {
    DCHECK(callback_);
  }
  
  ThumbnailReadinessTracker::~ThumbnailReadinessTracker() = default;
  
  void ThumbnailReadinessTracker::DidStartNavigation(
      content::NavigationHandle* navigation_handle) {
    if (!NavigationShouldInvalidateThumbnail(navigation_handle))
      return;
  
    pending_navigation_ = navigation_handle;
    UpdateReadiness(Readiness::kNotReady);
  }
  
  void ThumbnailReadinessTracker::DidFinishNavigation(
      content::NavigationHandle* navigation_handle) {
    // Only respond to the last navigation handled by DidStartNavigation.
    // Others may be unimportant or outdated.
    if (navigation_handle != pending_navigation_)
      return;
  
    pending_navigation_ = nullptr;
    UpdateReadiness(Readiness::kReadyForInitialCapture);
  
    if (last_readiness_ > Readiness::kReadyForInitialCapture)
      return;
    UpdateReadiness(Readiness::kReadyForInitialCapture);
  }
  
  void ThumbnailReadinessTracker::DocumentOnLoadCompletedInMainFrame(
      content::RenderFrameHost* render_frame_host) {
    UpdateReadiness(Readiness::kReadyForFinalCapture);
  }
  
  void ThumbnailReadinessTracker::WebContentsDestroyed() {
    pending_navigation_ = nullptr;
    UpdateReadiness(Readiness::kNotReady);
  }
  
  void ThumbnailReadinessTracker::UpdateReadiness(Readiness readiness) {
    if (readiness == last_readiness_)
      return;
  
    // If the WebContents is closing, it shouldn't be captured.
    if (web_contents()->IsBeingDestroyed())
      readiness = Readiness::kNotReady;
  
    last_readiness_ = readiness;
    callback_.Run(readiness);
  }
  