blob: 12d4d057e65e64fd8aeddfcd8cf203f33c00a85b [file] [log] [blame]
// Copyright 2015 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.
#import "ios/web/web_state/ui/crw_web_controller_container_view.h"
#include "base/logging.h"
#import "ios/web/common/crw_content_view.h"
#import "ios/web/common/crw_web_view_content_view.h"
#include "ios/web/common/features.h"
#import "ios/web/public/web_state/ui/crw_native_content.h"
#import "ios/web/web_state/ui/crw_web_view_proxy_impl.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@interface CRWWebControllerContainerView ()
// Redefine properties as readwrite.
@property(nonatomic, strong, readwrite)
CRWWebViewContentView* webViewContentView;
@property(nonatomic, strong, readwrite) id<CRWNativeContent> nativeController;
@property(nonatomic, strong, readwrite) CRWContentView* transientContentView;
// Convenience getter for the proxy object.
@property(nonatomic, weak, readonly) CRWWebViewProxyImpl* contentViewProxy;
@end
@implementation CRWWebControllerContainerView
@synthesize webViewContentView = _webViewContentView;
@synthesize nativeController = _nativeController;
@synthesize transientContentView = _transientContentView;
@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();
return nil;
}
- (instancetype)initWithFrame:(CGRect)frame {
NOTREACHED();
return nil;
}
- (void)dealloc {
self.contentViewProxy.contentView = nil;
}
#pragma mark Accessors
- (void)setWebViewContentView:(CRWWebViewContentView*)webViewContentView {
if (![_webViewContentView isEqual:webViewContentView]) {
[_webViewContentView removeFromSuperview];
_webViewContentView = webViewContentView;
[_webViewContentView setFrame:self.bounds];
[self addSubview:_webViewContentView];
}
}
- (void)setNativeController:(id<CRWNativeContent>)nativeController {
if (![_nativeController isEqual:nativeController]) {
__weak id oldController = _nativeController;
if ([oldController respondsToSelector:@selector(willBeDismissed)]) {
[oldController willBeDismissed];
}
[[oldController view] removeFromSuperview];
_nativeController = nativeController;
// TODO(crbug.com/503297): Re-enable this DCHECK once native controller
// leaks are fixed.
// DCHECK(!oldController);
}
}
- (void)setTransientContentView:(CRWContentView*)transientContentView {
if (![_transientContentView isEqual:transientContentView]) {
[_transientContentView removeFromSuperview];
_transientContentView = transientContentView;
}
}
- (CRWWebViewProxyImpl*)contentViewProxy {
return [_delegate contentViewProxyForContainerView:self];
}
#pragma mark Layout
- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
[super traitCollectionDidChange:previousTraitCollection];
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.webViewContentView.frame = self.bounds;
[self.webViewContentView setNeedsLayout];
// TODO(crbug.com/570114): Move adding of the following subviews to another
// place.
// nativeController layout.
if (self.nativeController) {
UIView* nativeView = [self.nativeController view];
if (!nativeView.superview) {
[self addSubview:nativeView];
[nativeView setNeedsUpdateConstraints];
}
nativeView.frame = UIEdgeInsetsInsetRect(
self.bounds, [self.delegate nativeContentInsetsForContainerView:self]);
}
// transientContentView layout.
if (self.transientContentView) {
if (!self.transientContentView.superview)
[self addSubview:self.transientContentView];
[self bringSubviewToFront:self.transientContentView];
self.transientContentView.frame = self.bounds;
}
}
- (BOOL)isViewAlive {
return self.webViewContentView || self.transientContentView ||
[self.nativeController isViewAlive];
}
- (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) {
[_webViewContentView setFrame:self.bounds];
[self addSubview:_webViewContentView];
}
return;
}
// There's no window and |webViewContentView| is active, stash it.
[_delegate containerView:self storeWebViewInWindow:self.webViewContentView];
}
#pragma mark Content Setters
- (void)resetContent {
self.webViewContentView = nil;
self.nativeController = nil;
self.transientContentView = nil;
self.contentViewProxy.contentView = nil;
}
- (void)displayWebViewContentView:(CRWWebViewContentView*)webViewContentView {
DCHECK(webViewContentView);
self.webViewContentView = webViewContentView;
self.nativeController = nil;
self.transientContentView = nil;
self.contentViewProxy.contentView = self.webViewContentView;
[self updateWebViewContentViewForContainerWindow:self.window];
[self setNeedsLayout];
}
- (void)displayNativeContent:(id<CRWNativeContent>)nativeController {
DCHECK(nativeController);
self.webViewContentView = nil;
self.nativeController = nativeController;
self.transientContentView = nil;
self.contentViewProxy.contentView = nil;
[self setNeedsLayout];
}
- (void)displayTransientContent:(CRWContentView*)transientContentView {
DCHECK(transientContentView);
self.transientContentView = transientContentView;
self.contentViewProxy.contentView = self.transientContentView;
[self setNeedsLayout];
}
- (void)clearTransientContentView {
self.transientContentView = nil;
self.contentViewProxy.contentView = self.webViewContentView;
}
@end