summaryrefslogtreecommitdiffstats
path: root/ios/web/shell/view_controller.mm
blob: 9460538d30b0f6dbc32e0e9e2536ba8c21631673 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
// Copyright 2014 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/shell/view_controller.h"

#include <stdint.h>

#include <utility>

#include "base/mac/objc_property_releaser.h"
#import "base/mac/scoped_nsobject.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/sys_string_conversions.h"
#include "ios/net/cookies/cookie_store_ios.h"
#import "ios/net/crn_http_protocol_handler.h"
#import "ios/net/empty_nsurlcache.h"
#import "ios/web/navigation/crw_session_controller.h"
#include "ios/web/navigation/web_load_params.h"
#import "ios/web/net/crw_url_verifying_protocol_handler.h"
#include "ios/web/net/request_tracker_factory_impl.h"
#import "ios/web/net/web_http_protocol_handler_delegate.h"
#include "ios/web/public/referrer.h"
#import "ios/web/public/web_controller_factory.h"
#include "ios/web/public/web_state/web_state.h"
#include "ios/web/shell/shell_browser_state.h"
#include "ios/web/web_state/ui/crw_web_controller.h"
#include "ios/web/web_state/web_state_impl.h"
#include "ui/base/page_transition_types.h"

NSString* const kWebShellBackButtonAccessibilityLabel = @"Back";
NSString* const kWebShellForwardButtonAccessibilityLabel = @"Forward";
NSString* const kWebShellAddressFieldAccessibilityLabel = @"Address field";

@interface ViewController ()<CRWWebUserInterfaceDelegate> {
  web::BrowserState* _browserState;
  base::scoped_nsobject<CRWWebController> _webController;
  scoped_ptr<web::RequestTrackerFactoryImpl> _requestTrackerFactory;
  scoped_ptr<web::WebHTTPProtocolHandlerDelegate> _httpProtocolDelegate;

  base::mac::ObjCPropertyReleaser _propertyReleaser_ViewController;
}
@property(nonatomic, assign, readonly) web::WebState* webState;
@property(nonatomic, readwrite, retain) UITextField* field;
@end

@implementation ViewController

@synthesize field = _field;
@synthesize containerView = _containerView;
@synthesize toolbarView = _toolbarView;

- (instancetype)initWithBrowserState:(web::BrowserState*)browserState {
  self = [super initWithNibName:@"MainView" bundle:nil];
  if (self) {
    _propertyReleaser_ViewController.Init(self, [ViewController class]);
    _browserState = browserState;
  }
  return self;
}

- (void)dealloc {
  net::HTTPProtocolHandlerDelegate::SetInstance(nullptr);
  net::RequestTracker::SetRequestTrackerFactory(nullptr);
  [super dealloc];
}

- (void)viewDidLoad {
  [super viewDidLoad];

  // Set up the toolbar buttons.
  UIButton* back = [UIButton buttonWithType:UIButtonTypeCustom];
  [back setImage:[UIImage imageNamed:@"toolbar_back"]
        forState:UIControlStateNormal];
  [back setFrame:CGRectMake(0, 0, 44, 44)];
  [back setImageEdgeInsets:UIEdgeInsetsMake(5, 5, 4, 4)];
  [back setAutoresizingMask:UIViewAutoresizingFlexibleRightMargin];
  [back addTarget:self
                action:@selector(back)
      forControlEvents:UIControlEventTouchUpInside];
  [back setAccessibilityLabel:kWebShellBackButtonAccessibilityLabel];

  UIButton* forward = [UIButton buttonWithType:UIButtonTypeCustom];
  [forward setImage:[UIImage imageNamed:@"toolbar_forward"]
           forState:UIControlStateNormal];
  [forward setFrame:CGRectMake(44, 0, 44, 44)];
  [forward setImageEdgeInsets:UIEdgeInsetsMake(5, 5, 4, 4)];
  [forward setAutoresizingMask:UIViewAutoresizingFlexibleRightMargin];
  [forward addTarget:self
                action:@selector(forward)
      forControlEvents:UIControlEventTouchUpInside];
  [forward setAccessibilityLabel:kWebShellForwardButtonAccessibilityLabel];

  base::scoped_nsobject<UITextField> field([[UITextField alloc]
      initWithFrame:CGRectMake(88, 6, CGRectGetWidth([_toolbarView frame]) - 98,
                               31)]);
  [field setDelegate:self];
  [field setBackground:[[UIImage imageNamed:@"textfield_background"]
                           resizableImageWithCapInsets:UIEdgeInsetsMake(
                                                           12, 12, 12, 12)]];
  [field setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
  [field setKeyboardType:UIKeyboardTypeWebSearch];
  [field setAutocorrectionType:UITextAutocorrectionTypeNo];
  [field setAccessibilityLabel:kWebShellAddressFieldAccessibilityLabel];
  [field setClearButtonMode:UITextFieldViewModeWhileEditing];
  self.field = field;

  [_toolbarView addSubview:back];
  [_toolbarView addSubview:forward];
  [_toolbarView addSubview:field];

  // Set up the network stack before creating the WebState.
  [self setUpNetworkStack];

  scoped_ptr<web::WebStateImpl> webState(new web::WebStateImpl(_browserState));
  webState->GetNavigationManagerImpl().InitializeSession(nil, nil, NO, 0);
  _webController.reset(web::CreateWebController(std::move(webState)));
  [_webController setDelegate:self];
  [_webController setUIDelegate:self];
  [_webController setWebUsageEnabled:YES];

  UIView* view = self.webState->GetView();
  [view setFrame:[_containerView bounds]];
  [_containerView addSubview:view];

  web::WebLoadParams params(GURL("https://dev.chromium.org/"));
  params.transition_type = ui::PAGE_TRANSITION_TYPED;
  [_webController loadWithParams:params];
}

- (web::WebState*)webState {
  return [_webController webState];
}

- (void)setUpNetworkStack {
  // Disable the default cache.
  [NSURLCache setSharedURLCache:[EmptyNSURLCache emptyNSURLCache]];

  _httpProtocolDelegate.reset(new web::WebHTTPProtocolHandlerDelegate(
      _browserState->GetRequestContext()));
  net::HTTPProtocolHandlerDelegate::SetInstance(_httpProtocolDelegate.get());
  BOOL success = [NSURLProtocol registerClass:[CRNHTTPProtocolHandler class]];
  DCHECK(success);
  // The CRWURLVerifyingProtocolHandler is used to verify URL in the
  // CRWWebController. It must be registered after the HttpProtocolHandler
  // because handlers are called in the reverse order of declaration.
  success =
      [NSURLProtocol registerClass:[CRWURLVerifyingProtocolHandler class]];
  DCHECK(success);
  _requestTrackerFactory.reset(
      new web::RequestTrackerFactoryImpl(std::string()));
  net::RequestTracker::SetRequestTrackerFactory(_requestTrackerFactory.get());
  net::CookieStoreIOS::SetCookiePolicy(net::CookieStoreIOS::ALLOW);
}

- (void)didReceiveMemoryWarning {
  [super didReceiveMemoryWarning];
}

- (UIBarPosition)positionForBar:(id<UIBarPositioning>)bar {
  if (bar == _toolbarView) {
    return UIBarPositionTopAttached;
  }
  return UIBarPositionAny;
}

- (void)back {
  web::NavigationManager* navManager = self.webState->GetNavigationManager();
  if (navManager->CanGoBack()) {
    navManager->GoBack();
  }
}

- (void)forward {
  web::NavigationManager* navManager = self.webState->GetNavigationManager();
  if (navManager->CanGoForward()) {
    navManager->GoForward();
  }
}

- (BOOL)textFieldShouldReturn:(UITextField*)field {
  GURL url = GURL(base::SysNSStringToUTF8([field text]));

  // Do not try to load invalid URLs.
  if (url.is_valid()) {
    web::WebLoadParams params(url);
    params.transition_type = ui::PAGE_TRANSITION_TYPED;
    [_webController loadWithParams:params];
  }

  [field resignFirstResponder];
  [self updateToolbar];
  return YES;
}

- (void)updateToolbar {
  // Do not update the URL if the text field is currently being edited.
  if ([_field isFirstResponder]) {
    return;
  }

  const GURL& visibleURL = self.webState->GetVisibleURL();
  [_field setText:base::SysUTF8ToNSString(visibleURL.spec())];
}

// -----------------------------------------------------------------------
#pragma mark Bikeshedding Implementation

// Overridden to allow this view controller to receive motion events by being
// first responder when no other views are.
- (BOOL)canBecomeFirstResponder {
  return YES;
}

- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent*)event {
  if (event.subtype == UIEventSubtypeMotionShake) {
    [self updateToolbarColor];
  }
}

- (void)updateToolbarColor {
  // Cycle through the following set of colors:
  NSArray* colors = @[
    // Vanilla Blue.
    [UIColor colorWithRed:0.337 green:0.467 blue:0.988 alpha:1.0],
    // Vanilla Red.
    [UIColor colorWithRed:0.898 green:0.110 blue:0.137 alpha:1.0],
    // Blue Grey.
    [UIColor colorWithRed:0.376 green:0.490 blue:0.545 alpha:1.0],
    // Brown.
    [UIColor colorWithRed:0.475 green:0.333 blue:0.282 alpha:1.0],
    // Purple.
    [UIColor colorWithRed:0.612 green:0.153 blue:0.690 alpha:1.0],
    // Teal.
    [UIColor colorWithRed:0.000 green:0.737 blue:0.831 alpha:1.0],
    // Deep Orange.
    [UIColor colorWithRed:1.000 green:0.341 blue:0.133 alpha:1.0],
    // Indigo.
    [UIColor colorWithRed:0.247 green:0.318 blue:0.710 alpha:1.0],
    // Vanilla Green.
    [UIColor colorWithRed:0.145 green:0.608 blue:0.141 alpha:1.0],
    // Pinkerton.
    [UIColor colorWithRed:0.914 green:0.118 blue:0.388 alpha:1.0],
  ];

  NSUInteger currentIndex = [colors indexOfObject:_toolbarView.barTintColor];
  if (currentIndex == NSNotFound) {
    currentIndex = 0;
  }
  NSUInteger newIndex = currentIndex + 1;
  if (newIndex >= [colors count]) {
    // TODO(rohitrao): Out of colors!  Consider prompting the user to pick their
    // own color here.  Also consider allowing the user to choose the entire set
    // of colors or allowing the user to choose color randomization.
    newIndex = 0;
  }
  _toolbarView.barTintColor = [colors objectAtIndex:newIndex];
}

// -----------------------------------------------------------------------
// WebDelegate implementation.

- (void)webWillAddPendingURL:(const GURL&)url
                  transition:(ui::PageTransition)transition {
}
- (void)webDidAddPendingURL {
  [self updateToolbar];
}
- (void)webCancelStartLoadingRequest {
}
- (void)webDidStartLoadingURL:(const GURL&)currentUrl
          shouldUpdateHistory:(BOOL)updateHistory {
  [self updateToolbar];
}
- (void)webDidFinishWithURL:(const GURL&)url loadSuccess:(BOOL)loadSuccess {
  [self updateToolbar];
}

- (CRWWebController*)webPageOrderedOpen:(const GURL&)url
                               referrer:(const web::Referrer&)referrer
                             windowName:(NSString*)windowName
                           inBackground:(BOOL)inBackground {
  return nil;
}

- (CRWWebController*)webPageOrderedOpen {
  return nil;
}

- (void)webPageOrderedClose {
}
- (void)goDelta:(int)delta {
}
- (void)openURLWithParams:(const web::WebState::OpenURLParams&)params {
}
- (BOOL)openExternalURL:(const GURL&)URL linkClicked:(BOOL)linkClicked {
  return NO;
}

- (void)presentSSLError:(const net::SSLInfo&)info
           forSSLStatus:(const web::SSLStatus&)status
            recoverable:(BOOL)recoverable
               callback:(SSLErrorCallback)shouldContinue {
  UIAlertController* alert = [UIAlertController
      alertControllerWithTitle:@"Your connection is not private"
                       message:nil
                preferredStyle:UIAlertControllerStyleActionSheet];
  [alert addAction:[UIAlertAction actionWithTitle:@"Go Back"
                                            style:UIAlertActionStyleCancel
                                          handler:^(UIAlertAction*) {
                                            shouldContinue(NO);
                                          }]];

  if (recoverable) {
    [alert addAction:[UIAlertAction actionWithTitle:@"Continue"
                                              style:UIAlertActionStyleDefault
                                            handler:^(UIAlertAction*) {
                                              shouldContinue(YES);
                                            }]];
  }
  [self presentViewController:alert animated:YES completion:nil];
}

- (void)presentSpoofingError {
}
- (void)webLoadCancelled:(const GURL&)url {
}
- (void)webDidUpdateHistoryStateWithPageURL:(const GURL&)pageUrl {
}
- (void)webController:(CRWWebController*)webController
    retrievePlaceholderOverlayImage:(void (^)(UIImage*))block {
}
- (void)webController:(CRWWebController*)webController
    onFormResubmissionForRequest:(NSURLRequest*)request
                   continueBlock:(ProceduralBlock)continueBlock
                     cancelBlock:(ProceduralBlock)cancelBlock {
}
- (void)webWillReload {
}
- (void)webWillInitiateLoadWithParams:(web::WebLoadParams&)params {
}
- (void)webDidUpdateSessionForLoadWithParams:(const web::WebLoadParams&)params
                        wasInitialNavigation:(BOOL)initialNavigation {
}
- (void)webWillFinishHistoryNavigationFromEntry:(CRWSessionEntry*)fromEntry {
}
- (int)downloadImageAtUrl:(const GURL&)url
            maxBitmapSize:(uint32_t)maxBitmapSize
                 callback:
                     (const web::WebState::ImageDownloadCallback&)callback {
  return -1;
}

// -----------------------------------------------------------------------
// CRWWebUserInterfaceDelegate implementation.

- (void)webController:(CRWWebController*)webController
    runAuthDialogForProtectionSpace:(NSURLProtectionSpace*)protectionSpace
                 proposedCredential:(NSURLCredential*)credential
                  completionHandler:
                      (void (^)(NSString* user, NSString* password))handler {
  // Calling |handler| with nil objects is the same as not implemeting it. This
  // method is implemented to make testing easier.
  handler(nil, nil);
}

@end