1// Copyright 2015 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#import "ios/web/web_state/ui/crw_web_controller_container_view.h"
6
7#include "base/check.h"
8#include "base/notreached.h"
9#import "ios/web/common/crw_content_view.h"
10#import "ios/web/common/crw_viewport_adjustment_container.h"
11#import "ios/web/common/crw_web_view_content_view.h"
12#include "ios/web/common/features.h"
13#import "ios/web/web_state/ui/crw_web_view_proxy_impl.h"
14
15#if !defined(__has_feature) || !__has_feature(objc_arc)
16#error "This file requires ARC support."
17#endif
18
19@interface CRWWebControllerContainerView () <CRWViewportAdjustmentContainer>
20
21// Redefine properties as readwrite.
22@property(nonatomic, strong, readwrite)
23    CRWWebViewContentView* webViewContentView;
24@property(nonatomic, strong, readwrite) CRWContentView* transientContentView;
25
26// Convenience getter for the proxy object.
27@property(nonatomic, weak, readonly) CRWWebViewProxyImpl* contentViewProxy;
28
29@end
30
31@implementation CRWWebControllerContainerView
32@synthesize webViewContentView = _webViewContentView;
33@synthesize transientContentView = _transientContentView;
34@synthesize delegate = _delegate;
35
36- (instancetype)initWithDelegate:
37        (id<CRWWebControllerContainerViewDelegate>)delegate {
38  self = [super initWithFrame:CGRectZero];
39  if (self) {
40    DCHECK(delegate);
41    _delegate = delegate;
42    self.backgroundColor = [UIColor whiteColor];
43    self.autoresizingMask =
44        UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
45  }
46  return self;
47}
48
49- (instancetype)initWithCoder:(NSCoder*)decoder {
50  NOTREACHED();
51  return nil;
52}
53
54- (instancetype)initWithFrame:(CGRect)frame {
55  NOTREACHED();
56  return nil;
57}
58
59- (void)dealloc {
60  self.contentViewProxy.contentView = nil;
61}
62
63#pragma mark Accessors
64
65- (UIView<CRWViewportAdjustment>*)fullscreenViewportAdjuster {
66  if (![self.webViewContentView
67          conformsToProtocol:@protocol(CRWViewportAdjustment)]) {
68    return nil;
69  }
70  return self.webViewContentView;
71}
72
73- (void)setWebViewContentView:(CRWWebViewContentView*)webViewContentView {
74  if (![_webViewContentView isEqual:webViewContentView]) {
75    [_webViewContentView removeFromSuperview];
76    _webViewContentView = webViewContentView;
77    [_webViewContentView setFrame:self.bounds];
78    [self addSubview:_webViewContentView];
79  }
80}
81
82- (void)setTransientContentView:(CRWContentView*)transientContentView {
83  if (![_transientContentView isEqual:transientContentView]) {
84    [_transientContentView removeFromSuperview];
85    _transientContentView = transientContentView;
86  }
87}
88
89- (CRWWebViewProxyImpl*)contentViewProxy {
90  return [_delegate contentViewProxyForContainerView:self];
91}
92
93#pragma mark Layout
94
95- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
96  [super traitCollectionDidChange:previousTraitCollection];
97  if (previousTraitCollection.preferredContentSizeCategory !=
98      self.traitCollection.preferredContentSizeCategory) {
99    // In case the preferred content size changes, the layout is dirty.
100    [self setNeedsLayout];
101  }
102}
103
104- (void)layoutSubviews {
105  [super layoutSubviews];
106
107  // webViewContentView layout.  |-setNeedsLayout| is called in case any webview
108  // layout updates need to occur despite the bounds size staying constant.
109  self.webViewContentView.frame = self.bounds;
110  [self.webViewContentView setNeedsLayout];
111
112  // TODO(crbug.com/570114): Move adding of the following subviews to another
113  // place.
114  // transientContentView layout.
115  if (self.transientContentView) {
116    if (!self.transientContentView.superview)
117      [self addSubview:self.transientContentView];
118    [self bringSubviewToFront:self.transientContentView];
119    self.transientContentView.frame = self.bounds;
120  }
121}
122
123- (BOOL)isViewAlive {
124  return self.webViewContentView || self.transientContentView;
125}
126
127- (void)willMoveToWindow:(UIWindow*)newWindow {
128  [super willMoveToWindow:newWindow];
129  [self updateWebViewContentViewForContainerWindow:newWindow];
130}
131
132- (void)updateWebViewContentViewForContainerWindow:(UIWindow*)containerWindow {
133  if (!base::FeatureList::IsEnabled(web::features::kKeepsRenderProcessAlive))
134    return;
135
136  if (!self.webViewContentView)
137    return;
138
139  // If there's a containerWindow or |webViewContentView| is inactive, put it
140  // back where it belongs.
141  if (containerWindow ||
142      ![_delegate shouldKeepRenderProcessAliveForContainerView:self]) {
143    if (self.webViewContentView.superview != self) {
144      [_webViewContentView setFrame:self.bounds];
145      // Insert the content view on the back of the container view so any view
146      // that was presented on top of the content view can still appear.
147      [self insertSubview:_webViewContentView atIndex:0];
148    }
149    return;
150  }
151
152  // There's no window and |webViewContentView| is active, stash it.
153  [_delegate containerView:self storeWebViewInWindow:self.webViewContentView];
154}
155
156#pragma mark Content Setters
157
158- (void)resetContent {
159  self.webViewContentView = nil;
160  self.transientContentView = nil;
161  self.contentViewProxy.contentView = nil;
162}
163
164- (void)displayWebViewContentView:(CRWWebViewContentView*)webViewContentView {
165  DCHECK(webViewContentView);
166  self.webViewContentView = webViewContentView;
167  self.transientContentView = nil;
168  self.contentViewProxy.contentView = self.webViewContentView;
169  [self updateWebViewContentViewForContainerWindow:self.window];
170  [self setNeedsLayout];
171}
172
173- (void)displayTransientContent:(CRWContentView*)transientContentView {
174  DCHECK(transientContentView);
175  self.transientContentView = transientContentView;
176  self.contentViewProxy.contentView = self.transientContentView;
177  [self setNeedsLayout];
178}
179
180- (void)clearTransientContentView {
181  self.transientContentView = nil;
182  self.contentViewProxy.contentView = self.webViewContentView;
183}
184
185#pragma mark UIView (printing)
186
187// Only print the web view by returning the web view printformatter.
188- (UIViewPrintFormatter*)viewPrintFormatter {
189  return [self.webViewContentView.webView viewPrintFormatter];
190}
191
192- (void)drawRect:(CGRect)rect
193    forViewPrintFormatter:(UIViewPrintFormatter*)formatter {
194  [self.webViewContentView.webView drawRect:rect];
195}
196
197@end
198