Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ios / web / web_state / ui / crw_web_controller_container_view.mm
blob86211b60274dfd89852542fdf932c0ed4f9de99f
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
27 // toolbars.
28 - (void)addToolbar:(UIView*)toolbar;
30 // Removes |toolbar| from the container view.
31 - (void)removeToolbar:(UIView*)toolbar;
33 @end
35 @implementation CRWToolbarContainerView
37 #pragma mark Accessors
39 - (NSMutableArray*)toolbars {
40   if (!_toolbars)
41     _toolbars.reset([[NSMutableArray alloc] init]);
42   return _toolbars.get();
45 #pragma mark Layout
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);
59   }
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);
70 #pragma mark Toolbars
72 - (void)addToolbar:(UIView*)toolbar {
73   DCHECK(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 {
81   DCHECK(toolbar);
82   DCHECK([self.toolbars containsObject:toolbar]);
83   [self.toolbars removeObject:toolbar];
84   [toolbar removeFromSuperview];
87 @end
89 #pragma mark - CRWWebControllerContainerView
91 @interface CRWWebControllerContainerView () {
92   // The proxy for the content added to the container.  It is owned by the web
93   // controller.
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;
113 @end
115 @implementation CRWWebControllerContainerView
117 - (instancetype)initWithContentViewProxy:(CRWWebViewProxyImpl*)proxy {
118   self = [super initWithFrame:CGRectZero];
119   if (self) {
120     DCHECK(proxy);
121     _webViewProxy.reset(proxy);
122     self.backgroundColor = [UIColor whiteColor];
123     self.autoresizingMask =
124         UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
125   }
126   return self;
129 - (instancetype)initWithCoder:(NSCoder*)decoder {
130   NOTREACHED();
131   return nil;
134 - (instancetype)initWithFrame:(CGRect)frame {
135   NOTREACHED();
136   return nil;
139 - (void)dealloc {
140   [_webViewProxy setContentView:nil];
141   [super dealloc];
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];
155   }
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);
175   }
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];
187   }
190 - (void)setToolbarContainerView:(CRWToolbarContainerView*)toolbarContainerView {
191   if (![_toolbarContainerView isEqual:toolbarContainerView]) {
192     [_toolbarContainerView removeFromSuperview];
193     _toolbarContainerView.reset([toolbarContainerView retain]);
194     [self addSubview:_toolbarContainerView];
195   }
198 - (UIView*)toolbarContainerView {
199   return _toolbarContainerView.get();
202 #pragma mark Layout
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);
221   }
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];
276   }
277   // Add the toolbar to the container.
278   [self.toolbarContainerView addToolbar:toolbar];
279   [self setNeedsLayout];
282 - (void)addToolbars:(NSArray*)toolbars {
283   DCHECK(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];
294   else
295     self.toolbarContainerView = nil;
298 - (void)removeAllToolbars {
299   // Resetting the property will remove the toolbars from the hierarchy.
300   self.toolbarContainerView = nil;
303 @end