chromium/ios/web/web_state/ui/crw_web_controller_container_view.mm

// Copyright 2015 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/web/web_state/ui/crw_web_controller_container_view.h"

#import "base/check.h"
#import "base/notreached.h"
#import "ios/web/common/crw_content_view.h"
#import "ios/web/common/crw_viewport_adjustment_container.h"
#import "ios/web/common/crw_web_view_content_view.h"
#import "ios/web/common/features.h"
#import "ios/web/web_state/ui/crw_web_view_proxy_impl.h"

@interface CRWWebControllerContainerView () <CRWViewportAdjustmentContainer>

// Redefine properties as readwrite.
@property(nonatomic, strong, readwrite)
    CRWWebViewContentView* webViewContentView;

// Convenience getter for the proxy object.
@property(nonatomic, weak, readonly) CRWWebViewProxyImpl* contentViewProxy;

@end

@implementation CRWWebControllerContainerView

@synthesize webViewContentView = _webViewContentView;
@synthesize delegate = _delegate;

- (instancetype)initWithDelegate:
    (id<CRWWebControllerContainerViewDelegate>)delegate {
  self = [super initWithFrame:CGRectZero];
  if (self) {
    DCHECK(delegate);
    _delegate = delegate;
    self.backgroundColor = [UIColor whiteColor];
    self.autoresizingMask =
        UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  }
  return self;
}

- (instancetype)initWithCoder:(NSCoder*)decoder {
  NOTREACHED_IN_MIGRATION();
  return nil;
}

- (instancetype)initWithFrame:(CGRect)frame {
  NOTREACHED_IN_MIGRATION();
  return nil;
}

- (void)dealloc {
  [self.contentViewProxy clearContentViewAndAddPlaceholder:NO];
}

#pragma mark Accessors

- (UIView<CRWViewportAdjustment>*)fullscreenViewportAdjuster {
  if (![self.webViewContentView
          conformsToProtocol:@protocol(CRWViewportAdjustment)]) {
    return nil;
  }
  return self.webViewContentView;
}

- (void)setWebViewContentView:(CRWWebViewContentView*)webViewContentView {
  if (![_webViewContentView isEqual:webViewContentView]) {
    [_webViewContentView removeFromSuperview];
    _webViewContentView = webViewContentView;
    [self updateWebViewContentViewFrame];
    [self addSubview:_webViewContentView];
  }
}

- (CRWWebViewProxyImpl*)contentViewProxy {
  return [_delegate contentViewProxyForContainerView:self];
}

#pragma mark Layout

- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
  [super traitCollectionDidChange:previousTraitCollection];
  if ((self.traitCollection.verticalSizeClass !=
       previousTraitCollection.verticalSizeClass) ||
      (self.traitCollection.horizontalSizeClass !=
       previousTraitCollection.horizontalSizeClass) ||
      self.traitCollection.preferredContentSizeCategory !=
          previousTraitCollection.preferredContentSizeCategory) {
    // Reset zoom scale when the window is resized (portrait to landscape,
    // landscape to portrait or multi-window resizing), or if text size is
    // modified as websites can adjust to the preferred content size (using
    // font: -apple-system-body;). It avoids being in a different zoomed
    // position from where the user initially zoomed.
    UIScrollView* scrollView = self.contentViewProxy.contentView.scrollView;
    scrollView.zoomScale = scrollView.minimumZoomScale;
  }
  if (previousTraitCollection.preferredContentSizeCategory !=
      self.traitCollection.preferredContentSizeCategory) {
    // In case the preferred content size changes, the layout is dirty.
    [self setNeedsLayout];
  }
}

- (void)layoutSubviews {
  [super layoutSubviews];

  // webViewContentView layout.  `-setNeedsLayout` is called in case any webview
  // layout updates need to occur despite the bounds size staying constant.
  [self updateWebViewContentViewFrame];
  [self.webViewContentView setNeedsLayout];
}

- (BOOL)isViewAlive {
  return self.webViewContentView;
}

- (void)willMoveToWindow:(UIWindow*)newWindow {
  [super willMoveToWindow:newWindow];
  [self updateWebViewContentViewForContainerWindow:newWindow];
}

- (void)updateWebViewContentViewForContainerWindow:(UIWindow*)containerWindow {
  if (!base::FeatureList::IsEnabled(web::features::kKeepsRenderProcessAlive))
    return;

  if (!self.webViewContentView)
    return;

  // If there's a containerWindow or `webViewContentView` is inactive, put it
  // back where it belongs.
  if (containerWindow ||
      ![_delegate shouldKeepRenderProcessAliveForContainerView:self]) {
    if (self.webViewContentView.superview != self) {
      [self updateWebViewContentViewFrame];
      // Insert the content view on the back of the container view so any view
      // that was presented on top of the content view can still appear.
      [self insertSubview:_webViewContentView atIndex:0];
    }
    return;
  }

  // There's no window and `webViewContentView` is active, stash it.
  [_delegate containerView:self storeWebViewInWindow:self.webViewContentView];
}

#pragma mark Content Setters

- (void)resetContentForShutdown:(BOOL)shutdown {
  self.webViewContentView = nil;
  [self.contentViewProxy clearContentViewAndAddPlaceholder:!shutdown];
}

- (void)displayWebViewContentView:(CRWWebViewContentView*)webViewContentView {
  DCHECK(webViewContentView);
  self.webViewContentView = webViewContentView;
  self.contentViewProxy.contentView = self.webViewContentView;
  [self updateWebViewContentViewForContainerWindow:self.window];
  [self setNeedsLayout];
}

- (void)updateWebViewContentViewFullscreenState:
    (CrFullscreenState)fullscreenState {
  DCHECK(_webViewContentView);
  [self.webViewContentView updateFullscreenState:fullscreenState];
}

#pragma mark UIView (printing)

// Only print the web view by returning the web view printformatter.
- (UIViewPrintFormatter*)viewPrintFormatter {
  return [self.webViewContentView.webView viewPrintFormatter];
}

- (void)drawRect:(CGRect)rect
    forViewPrintFormatter:(UIViewPrintFormatter*)formatter {
  [self.webViewContentView.webView drawRect:rect];
}

#pragma mark - UIView overrides

- (void)safeAreaInsetsDidChange {
  // Update the frame to take into account the safe area inset as they are set
  // fractionally later than the rest of the view loading.
  [self updateWebViewContentViewFrame];
}

#pragma mark - Private helpers

// Update the content view frame.
- (void)updateWebViewContentViewFrame {
  if (base::FeatureList::IsEnabled(web::features::kSmoothScrollingDefault)) {
    [self.webViewContentView setFrame:self.bounds];
  } else {
    if (self.cover) {
      [self.webViewContentView setFrame:self.bounds];
    } else {
      [self.webViewContentView
          setFrame:UIEdgeInsetsInsetRect(self.bounds, self.safeAreaInsets)];
    }
  }
}

@end