chromium/ios/chrome/browser/ui/omnibox/zero_suggest_prefetch_helper.mm

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

#import "ios/chrome/browser/ui/omnibox/zero_suggest_prefetch_helper.h"

#import "base/feature_list.h"
#import "components/omnibox/browser/omnibox_controller.h"
#import "ios/chrome/browser/shared/model/web_state_list/active_web_state_observation_forwarder.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_list_observer_bridge.h"
#import "ios/web/public/web_state.h"
#import "ios/web/public/web_state_observer_bridge.h"

using web::WebState;
using web::WebStateObserverBridge;

@interface ZeroSuggestPrefetchHelper () <CRWWebStateObserver,
                                         WebStateListObserving>
@end

@implementation ZeroSuggestPrefetchHelper {
  /// Bridge to receive active web state events
  std::unique_ptr<ActiveWebStateObservationForwarder>
      _activeWebStateObservationForwarder;
  /// Bridge to receive WS events for the active web state.
  std::unique_ptr<WebStateObserverBridge> _webStateObserverBridge;

  /// Bridge to receive WSL events.
  std::unique_ptr<WebStateListObserverBridge> _webStateListObserverBridge;
}

- (void)dealloc {
  /// Reset the web state observation forwarder, which will remove
  /// `_webStateObserverBridge` from the relevant observer list.
  _activeWebStateObservationForwarder.reset();
  if (_webStateList) {
    _webStateList->RemoveObserver(_webStateListObserverBridge.get());
    _webStateListObserverBridge.reset();
    _webStateList = nullptr;
  }

  [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (instancetype)initWithWebStateList:(WebStateList*)webStateList
                          controller:(OmniboxController*)controller {
  self = [super init];
  if (self) {
    DCHECK(webStateList);
    DCHECK(controller);

    _webStateList = webStateList;
    _controller = controller;
    _webStateObserverBridge = std::make_unique<WebStateObserverBridge>(self);
    _activeWebStateObservationForwarder =
        std::make_unique<ActiveWebStateObservationForwarder>(
            webStateList, _webStateObserverBridge.get());
    _webStateListObserverBridge =
        std::make_unique<WebStateListObserverBridge>(self);
    _webStateList->AddObserver(_webStateListObserverBridge.get());

    [[NSNotificationCenter defaultCenter]
        addObserver:self
           selector:@selector(updateForAppWillForeground)
               name:UIApplicationWillEnterForegroundNotification
             object:nil];

    [[NSNotificationCenter defaultCenter]
        addObserver:self
           selector:@selector(updateForAppDidBackground)
               name:UIApplicationDidEnterBackgroundNotification
             object:nil];

    [self startPrefetchIfNecessary];
  }
  return self;
}

#pragma mark - CRWWebStateObserver

- (void)webState:(web::WebState*)webState
    didFinishNavigation:(web::NavigationContext*)navigationContext {
  [self startPrefetchIfNecessary];
}

#pragma mark - WebStateListObserving

- (void)didChangeWebStateList:(WebStateList*)webStateList
                       change:(const WebStateListChange&)change
                       status:(const WebStateListStatus&)status {
  if (status.active_web_state_change() && status.new_active_web_state) {
    [self startPrefetchIfNecessary];
  }
}

#pragma mark - private

/// Tell edit model to start prefetching whenever there's an active web state.
/// The prefetching is expected on navigation, tab switch, and page reload.
- (void)startPrefetchIfNecessary {
  WebState* activeWebState = _webStateList->GetActiveWebState();
  if (activeWebState == nullptr) {
    return;
  }

  self.controller->StartZeroSuggestPrefetch();
}

/// Indicates to this tab helper that the app has entered a foreground state.
- (void)updateForAppWillForeground {
  self.controller->autocomplete_controller()
      ->autocomplete_provider_client()
      ->set_in_background_state(false);

  [self startPrefetchIfNecessary];
}

/// Indicates to this tab helper that the app has entered a background state.
- (void)updateForAppDidBackground {
  self.controller->autocomplete_controller()
      ->autocomplete_provider_client()
      ->set_in_background_state(true);
}

@end