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.
5 #import "ios/web/web_state/ui/crw_web_controller_container_view.h"
7 #include "base/ios/weak_nsobject.h"
8 #include "base/logging.h"
9 #include "base/mac/scoped_nsobject.h"
10 #import "ios/web/public/web_state/ui/crw_content_view.h"
11 #import "ios/web/public/web_state/ui/crw_native_content.h"
12 #import "ios/web/public/web_state/ui/crw_web_view_content_view.h"
13 #import "ios/web/web_state/crw_web_view_proxy_impl.h"
15 #pragma mark - CRWToolbarContainerView
17 // Class that manages the display of toolbars.
18 @interface CRWToolbarContainerView : UIView {
19 // Backing object for |self.toolbars|.
20 base::scoped_nsobject<NSMutableArray> _toolbars;
23 // The toolbars currently managed by this view.
24 @property(nonatomic, retain, readonly) NSMutableArray* toolbars;
26 // Adds |toolbar| as a subview and bottom aligns to any previously added
28 - (void)addToolbar:(UIView*)toolbar;
30 // Removes |toolbar| from the container view.
31 - (void)removeToolbar:(UIView*)toolbar;
35 @implementation CRWToolbarContainerView
37 #pragma mark Accessors
39 - (NSMutableArray*)toolbars {
41 _toolbars.reset([[NSMutableArray alloc] init]);
42 return _toolbars.get();
47 - (void)layoutSubviews {
48 [super layoutSubviews];
50 // Bottom-align the toolbars.
51 CGPoint toolbarOrigin =
52 CGPointMake(self.bounds.origin.x, CGRectGetMaxY(self.bounds));
53 for (UIView* toolbar in self.toolbars) {
54 CGSize toolbarSize = [toolbar sizeThatFits:self.bounds.size];
55 toolbarSize.width = self.bounds.size.width;
56 toolbarOrigin.y -= toolbarSize.height;
57 toolbar.frame = CGRectMake(toolbarOrigin.x, toolbarOrigin.y,
58 toolbarSize.width, toolbarSize.height);
62 - (CGSize)sizeThatFits:(CGSize)size {
63 CGSize boundingRectSize = CGSizeMake(size.width, CGFLOAT_MAX);
64 CGFloat necessaryHeight = 0.0f;
65 for (UIView* toolbar in self.toolbars)
66 necessaryHeight += [toolbar sizeThatFits:boundingRectSize].height;
67 return CGSizeMake(size.width, necessaryHeight);
72 - (void)addToolbar:(UIView*)toolbar {
74 DCHECK(![self.toolbars containsObject:toolbar]);
75 toolbar.translatesAutoresizingMaskIntoConstraints = NO;
76 [self.toolbars addObject:toolbar];
77 [self addSubview:toolbar];
80 - (void)removeToolbar:(UIView*)toolbar {
82 DCHECK([self.toolbars containsObject:toolbar]);
83 [self.toolbars removeObject:toolbar];
84 [toolbar removeFromSuperview];
89 #pragma mark - CRWWebControllerContainerView
91 @interface CRWWebControllerContainerView () {
92 // The proxy for the content added to the container. It is owned by the web
94 base::WeakNSObject<CRWWebViewProxyImpl> _webViewProxy;
95 // Backing objects for corresponding properties.
96 base::scoped_nsobject<CRWWebViewContentView> _webViewContentView;
97 base::scoped_nsprotocol<id<CRWNativeContent>> _nativeController;
98 base::scoped_nsobject<CRWContentView> _transientContentView;
99 base::scoped_nsobject<CRWToolbarContainerView> _toolbarContainerView;
102 // Redefine properties as readwrite.
103 @property(nonatomic, retain, readwrite)
104 CRWWebViewContentView* webViewContentView;
105 @property(nonatomic, retain, readwrite) id<CRWNativeContent> nativeController;
106 @property(nonatomic, retain, readwrite) CRWContentView* transientContentView;
108 // Container view that displays any added toolbars. It is always the top-most
109 // subview, and is bottom aligned with the CRWWebControllerContainerView.
110 @property(nonatomic, retain, readonly)
111 CRWToolbarContainerView* toolbarContainerView;
115 @implementation CRWWebControllerContainerView
117 - (instancetype)initWithContentViewProxy:(CRWWebViewProxyImpl*)proxy {
118 self = [super initWithFrame:CGRectZero];
121 _webViewProxy.reset(proxy);
122 self.backgroundColor = [UIColor whiteColor];
123 self.autoresizingMask =
124 UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
129 - (instancetype)initWithCoder:(NSCoder*)decoder {
134 - (instancetype)initWithFrame:(CGRect)frame {
140 [_webViewProxy setContentView:nil];
144 #pragma mark Accessors
146 - (CRWWebViewContentView*)webViewContentView {
147 return _webViewContentView.get();
150 - (void)setWebViewContentView:(CRWWebViewContentView*)webViewContentView {
151 if (![_webViewContentView isEqual:webViewContentView]) {
152 [_webViewContentView removeFromSuperview];
153 _webViewContentView.reset([webViewContentView retain]);
154 [self addSubview:_webViewContentView];
158 - (id<CRWNativeContent>)nativeController {
159 return _nativeController.get();
162 - (void)setNativeController:(id<CRWNativeContent>)nativeController {
163 if (![_nativeController isEqual:nativeController]) {
164 // TODO(kkhorimoto): This line isn't strictly necessary since all native
165 // controllers currently inherit from NativeContentController, which removes
166 // its view upon deallocation. Consider moving NativeContentController into
167 // web/ so this behavior can be depended upon from within web/ without
168 // making assumptions about chrome/ code.
169 base::WeakNSProtocol<id> oldController(_nativeController);
170 [[_nativeController view] removeFromSuperview];
171 _nativeController.reset([nativeController retain]);
172 [self addSubview:[_nativeController view]];
173 [[_nativeController view] setNeedsUpdateConstraints];
174 DCHECK(!oldController);
178 - (CRWContentView*)transientContentView {
179 return _transientContentView.get();
182 - (void)setTransientContentView:(CRWContentView*)transientContentView {
183 if (![_transientContentView isEqual:transientContentView]) {
184 [_transientContentView removeFromSuperview];
185 _transientContentView.reset([transientContentView retain]);
186 [self addSubview:_transientContentView];
190 - (void)setToolbarContainerView:(CRWToolbarContainerView*)toolbarContainerView {
191 if (![_toolbarContainerView isEqual:toolbarContainerView]) {
192 [_toolbarContainerView removeFromSuperview];
193 _toolbarContainerView.reset([toolbarContainerView retain]);
194 [self addSubview:_toolbarContainerView];
198 - (UIView*)toolbarContainerView {
199 return _toolbarContainerView.get();
204 - (void)layoutSubviews {
205 [super layoutSubviews];
207 // Resize displayed content to the container's bounds.
208 self.webViewContentView.frame = self.bounds;
209 [self.nativeController view].frame = self.bounds;
210 self.transientContentView.frame = self.bounds;
212 // Bottom align the toolbars with the bottom of the container.
213 if (self.toolbarContainerView) {
214 [self bringSubviewToFront:self.toolbarContainerView];
215 CGSize toolbarContainerSize =
216 [self.toolbarContainerView sizeThatFits:self.bounds.size];
217 self.toolbarContainerView.frame =
218 CGRectMake(CGRectGetMinX(self.bounds),
219 CGRectGetMaxY(self.bounds) - toolbarContainerSize.height,
220 toolbarContainerSize.width, toolbarContainerSize.height);
224 - (BOOL)isViewAlive {
225 return self.webViewContentView || self.transientContentView ||
226 [self.nativeController isViewAlive];
229 #pragma mark Content Setters
231 - (void)resetContent {
232 self.webViewContentView = nil;
233 self.nativeController = nil;
234 self.transientContentView = nil;
235 [self removeAllToolbars];
236 [_webViewProxy setContentView:nil];
239 - (void)displayWebViewContentView:(CRWWebViewContentView*)webViewContentView {
240 DCHECK(webViewContentView);
241 self.webViewContentView = webViewContentView;
242 self.nativeController = nil;
243 self.transientContentView = nil;
244 [_webViewProxy setContentView:self.webViewContentView];
245 [self setNeedsLayout];
248 - (void)displayNativeContent:(id<CRWNativeContent>)nativeController {
249 DCHECK(nativeController);
250 self.webViewContentView = nil;
251 self.nativeController = nativeController;
252 self.transientContentView = nil;
253 [_webViewProxy setContentView:nil];
254 [self setNeedsLayout];
257 - (void)displayTransientContent:(CRWContentView*)transientContentView {
258 DCHECK(transientContentView);
259 self.transientContentView = transientContentView;
260 [_webViewProxy setContentView:self.transientContentView];
261 [self setNeedsLayout];
264 - (void)clearTransientContentView {
265 self.transientContentView = nil;
266 [_webViewProxy setContentView:self.webViewContentView];
269 #pragma mark Toolbars
271 - (void)addToolbar:(UIView*)toolbar {
272 // Create toolbar container if necessary.
273 if (!self.toolbarContainerView) {
274 self.toolbarContainerView = [
275 [[CRWToolbarContainerView alloc] initWithFrame:CGRectZero] autorelease];
277 // Add the toolbar to the container.
278 [self.toolbarContainerView addToolbar:toolbar];
279 [self setNeedsLayout];
282 - (void)addToolbars:(NSArray*)toolbars {
284 for (UIView* toolbar in toolbars)
285 [self addToolbar:toolbar];
288 - (void)removeToolbar:(UIView*)toolbar {
289 // Remove the toolbar from the container view.
290 [self.toolbarContainerView removeToolbar:toolbar];
291 // Reset the container if there are no more toolbars.
292 if ([self.toolbarContainerView.toolbars count])
293 [self setNeedsLayout];
295 self.toolbarContainerView = nil;
298 - (void)removeAllToolbars {
299 // Resetting the property will remove the toolbars from the hierarchy.
300 self.toolbarContainerView = nil;