blob: 39fc2eff155589b9dcdba458f1413d0c9520b8ce [file] [log] [blame]
// 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 "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/public/web_view_creation_util.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"
namespace {
// Returns true if WKWebView should be used instead of UIWebView.
// TODO(stuartmorgan): Decide on a better way to control this.
bool UseWKWebView() {
#if defined(FORCE_ENABLE_WKWEBVIEW)
return web::IsWKWebViewSupported();
#else
return false;
#endif
}
}
@interface ViewController () {
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, 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];
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];
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 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);
web::WebViewType webViewType =
UseWKWebView() ? web::WK_WEB_VIEW_TYPE : web::UI_WEB_VIEW_TYPE;
_webController.reset(web::CreateWebController(webViewType, webState.Pass()));
[_webController setDelegate:self];
[_webController setWebUsageEnabled:YES];
[[_webController view] setFrame:[_containerView bounds]];
[_containerView addSubview:[_webController view]];
web::WebLoadParams params(GURL("https://dev.chromium.org/"));
params.transition_type = ui::PAGE_TRANSITION_TYPED;
[_webController loadWithParams:params];
}
- (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 {
if ([_webController canGoBack]) {
[_webController goBack];
}
}
- (void)forward {
if ([_webController canGoForward]) {
[_webController 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& url = [_webController webStateImpl]->GetVisibleURL();
[_field setText:base::SysUTF8ToNSString(url.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*)webPageOrderedOpenBlankWithReferrer:
(const web::Referrer&)referrer
inBackground:(BOOL)inBackground {
return nil;
}
- (void)webPageOrderedClose {
}
- (void)goDelta:(int)delta {
}
- (void)openURLWithParams:(const web::WebState::OpenURLParams&)params {
}
- (BOOL)openExternalURL:(const GURL&)url {
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 {
}
- (void)webWillGoDelta:(int)delta {
}
- (void)webDidPrepareForGoBack {
}
- (int)downloadImageAtUrl:(const GURL&)url
maxBitmapSize:(uint32_t)maxBitmapSize
callback:
(const web::WebState::ImageDownloadCallback&)callback {
return -1;
}
@end