// Copyright 2020 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/browser_content/ui_bundled/browser_content_mediator.h"

#import "base/check_op.h"
#import "base/scoped_observation.h"
#import "ios/chrome/browser/browser_content/ui_bundled/browser_content_consumer.h"
#import "ios/chrome/browser/browser_content/ui_bundled/browser_content_view_controller.h"
#import "ios/chrome/browser/overlays/model/public/overlay_presentation_context.h"
#import "ios/chrome/browser/overlays/model/public/overlay_presenter.h"
#import "ios/chrome/browser/overlays/model/public/overlay_presenter_observer_bridge.h"
#import "ios/chrome/browser/overlays/model/public/overlay_request.h"
#import "ios/chrome/browser/overlays/model/public/overlay_request_queue.h"
#import "ios/chrome/browser/overlays/model/public/web_content_area/http_auth_overlay.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
#import "ios/web/public/web_state.h"
#import "url/gurl.h"

namespace {
// Checks whether an HTTP authentication dialog is being shown by
// `overlay_presenter` for a page whose host does not match `web_state_list`'s
// active WebState's last committed URL.
bool IsActiveOverlayRequestForNonCommittedHttpAuthentication(
    WebStateList* web_state_list) {
  DCHECK(web_state_list);

  web::WebState* web_state = web_state_list->GetActiveWebState();
  if (!web_state) {
    return false;
  }

  OverlayRequest* request = OverlayRequestQueue::FromWebState(
                                web_state, OverlayModality::kWebContentArea)
                                ->front_request();
  if (!request) {
    return false;
  }

  HTTPAuthOverlayRequestConfig* config =
      request->GetConfig<HTTPAuthOverlayRequestConfig>();
  if (!config) {
    return false;
  }

  return config->url().GetHost() != web_state->GetLastCommittedURL().GetHost();
}
}  // namespace

@interface BrowserContentMediator () <OverlayPresenterObserving> {
  // Observer that listens for HTTP authentication dialog presentation.
  std::unique_ptr<OverlayPresenterObserver> _overlayPresenterObserver;
  std::unique_ptr<
      base::ScopedObservation<OverlayPresenter, OverlayPresenterObserver>>
      _scopedOverlayPresenterObservation;
}
// The Browser's WebStateList.
@property(nonatomic, readonly) WebStateList* webStateList;
// Whether an HTTP authentication dialog is displayed for a page whose host does
// not match the active WebState's last committed URL.
@property(nonatomic, readonly, getter=isShowingAuthDialogForNonCommittedURL)
    BOOL showingAuthDialogForNonCommittedURL;
@end

@implementation BrowserContentMediator

- (instancetype)initWithWebStateList:(WebStateList*)webStateList
      webContentAreaOverlayPresenter:(OverlayPresenter*)overlayPresenter {
  if ((self = [super init])) {
    DCHECK(overlayPresenter);
    DCHECK_EQ(overlayPresenter->GetModality(),
              OverlayModality::kWebContentArea);
    _webStateList = webStateList;
    DCHECK(_webStateList);
    // Begin observing the OverlayPresenter.
    _overlayPresenterObserver =
        std::make_unique<OverlayPresenterObserverBridge>(self);
    _scopedOverlayPresenterObservation = std::make_unique<
        base::ScopedObservation<OverlayPresenter, OverlayPresenterObserver>>(
        _overlayPresenterObserver.get());
    _scopedOverlayPresenterObservation->Observe(overlayPresenter);
    // Check whether an HTTP authentication dialog is shown for a page that does
    // not match the rendered contents.
    _showingAuthDialogForNonCommittedURL =
        IsActiveOverlayRequestForNonCommittedHttpAuthentication(_webStateList);
  }
  return self;
}

#pragma mark - Accessors

- (void)setConsumer:(id<BrowserContentConsumer>)consumer {
  if (_consumer == consumer) {
    return;
  }
  _consumer = consumer;
  [self updateConsumer];
}

- (void)setShowingAuthDialogForNonCommittedURL:
    (BOOL)showingAuthDialogForNonCommittedURL {
  if (_showingAuthDialogForNonCommittedURL ==
      showingAuthDialogForNonCommittedURL) {
    return;
  }
  _showingAuthDialogForNonCommittedURL = showingAuthDialogForNonCommittedURL;
  [self updateConsumer];
}

#pragma mark - OverlayPresenterObserving

- (void)overlayPresenter:(OverlayPresenter*)presenter
    willShowOverlayForRequest:(OverlayRequest*)request
          initialPresentation:(BOOL)initialPresentation {
  self.showingAuthDialogForNonCommittedURL =
      IsActiveOverlayRequestForNonCommittedHttpAuthentication(
          self.webStateList);
}

- (void)overlayPresenter:(OverlayPresenter*)presenter
    didHideOverlayForRequest:(OverlayRequest*)request {
  self.showingAuthDialogForNonCommittedURL = NO;
}

- (void)overlayPresenterDestroyed:(OverlayPresenter*)presenter {
  _scopedOverlayPresenterObservation = nullptr;
  _overlayPresenterObserver = nullptr;
}

#pragma mark - Private

// Updates the consumer based on the current state of the mediator.
- (void)updateConsumer {
  if (!self.consumer) {
    return;
  }
  [self.consumer setContentBlocked:self.showingAuthDialogForNonCommittedURL];
}

@end
