4692f5ca45ab6f9d4d613bfbb23873ff61665672
[WebKit-https.git] / Tools / MobileMiniBrowser / MobileMiniBrowser / WebViewController.m
1 /*
2  * Copyright (C) 2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "WebViewController.h"
27
28 #import "TabViewController.h"
29 #import <WebKit/WKNavigation.h>
30 #import <WebKit/WKNavigationDelegate.h>
31 #import <WebKit/WKWebView.h>
32 #import <WebKit/WKWebViewConfiguration.h>
33
34 @interface WebViewController () <WKNavigationDelegate> {
35     WKWebView *_currentWebView;
36 }
37 - (WKWebView *)createWebView;
38 - (void)removeWebView:(WKWebView *)webView;
39 - (void)setCurrentWebView:(WKWebView *)webView;
40 @end
41
42 void* EstimatedProgressContext = &EstimatedProgressContext;
43 void* TitleContext = &TitleContext;
44 void* URLContext = &URLContext;
45
46 @implementation WebViewController
47
48 - (void)viewDidLoad
49 {
50     [super viewDidLoad];
51     self.webViews = [[NSMutableArray alloc] initWithCapacity:1];
52     self.tabViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"idTabViewController"];
53     self.tabViewController.parent = self;
54     self.tabViewController.modalPresentationStyle = UIModalPresentationPopover;
55
56     [self setCurrentWebView:[self createWebView]];
57 }
58
59
60 - (void)didReceiveMemoryWarning
61 {
62     [super didReceiveMemoryWarning];
63 }
64
65 #pragma mark Actions
66
67 - (IBAction)reload:(id)sender
68 {
69     [self.currentWebView reload];
70 }
71
72 - (IBAction)goBack:(id)sender
73 {
74     [self.currentWebView goBack];
75 }
76
77 - (IBAction)goForward:(id)sender
78 {
79     [self.currentWebView goForward];
80 }
81
82 - (IBAction)urlFieldEditingBegan:(id)sender
83 {
84     self.urlField.selectedTextRange = [self.urlField textRangeFromPosition:self.urlField.beginningOfDocument toPosition:self.urlField.endOfDocument];
85 }
86
87 - (IBAction)navigateTo:(id)sender
88 {
89     [self.urlField resignFirstResponder];
90     NSString* requestedDestination = self.urlField.text;
91     if ([requestedDestination rangeOfString:@"^[\\p{Alphabetic}]+:" options:(NSRegularExpressionSearch | NSCaseInsensitiveSearch | NSAnchoredSearch)].location == NSNotFound)
92         requestedDestination = [@"http://" stringByAppendingString:requestedDestination];
93     [self.currentWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:requestedDestination]]];
94 }
95
96 - (IBAction)showTabs:(id)sender
97 {
98     [self presentViewController:self.tabViewController animated:YES completion:^{ }];
99     self.tabViewController.popoverPresentationController.barButtonItem = self.tabButton;
100 }
101
102 #pragma mark Public methods
103
104 @dynamic currentWebView;
105
106 - (WKWebView *)currentWebView
107 {
108     return _currentWebView;
109 }
110
111 - (void)setCurrentWebView:(WKWebView *)webView
112 {
113     [_currentWebView removeObserver:self forKeyPath:@"estimatedProgress" context:EstimatedProgressContext];
114     [_currentWebView removeObserver:self forKeyPath:@"URL" context:URLContext];
115     [_currentWebView removeFromSuperview];
116
117     _currentWebView = webView;
118
119     [_currentWebView addObserver:self forKeyPath:@"estimatedProgress" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:EstimatedProgressContext];
120     [_currentWebView addObserver:self forKeyPath:@"URL" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:URLContext];
121     [self.webViewContainer addSubview:_currentWebView];
122 }
123
124 - (void)selectWebViewAtIndex:(NSUInteger)index
125 {
126     if (index >= self.webViews.count)
127         return;
128     self.currentWebView = self.webViews[index];
129 }
130
131 - (void)removeWebViewAtIndex:(NSUInteger)index
132 {
133     if (index >= self.webViews.count)
134         return;
135     [self removeWebView:self.webViews[index]];
136 }
137
138 - (void)addWebView
139 {
140     self.currentWebView = [self createWebView];
141 }
142
143 #pragma mark Internal methods
144
145 - (WKWebView *)createWebView
146 {
147     WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
148     WKWebView *webView = [[WKWebView alloc] initWithFrame:self.webViewContainer.bounds configuration:configuration];
149     webView.navigationDelegate = self;
150     webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
151     [webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:TitleContext];
152     [self.webViews addObject:webView];
153
154     [self.tabViewController.tableView reloadData];
155
156     return webView;
157 }
158
159 - (void)removeWebView:(WKWebView *)webView
160 {
161     NSUInteger index = [self.webViews indexOfObject:webView];
162     if (index != NSNotFound) {
163         [self.webViews[index] removeObserver:self forKeyPath:@"title" context:TitleContext];
164         [self.webViews removeObjectAtIndex:index];
165     } if (!self.webViews.count)
166         [self createWebView];
167     if (index >= self.webViews.count)
168         index = self.webViews.count - 1;
169     [self setCurrentWebView:self.webViews[index]];
170
171     [self.tabViewController.tableView reloadData];
172 }
173
174 #pragma mark Navigation Delegate
175
176 - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;
177 {
178     [webView loadHTMLString:[error description] baseURL:nil];
179 }
180
181 #pragma mark KVO
182
183 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
184 {
185     if (context == EstimatedProgressContext) {
186         float value = [[change valueForKey:NSKeyValueChangeNewKey] floatValue];
187         [self.progressView setProgress:value animated:YES];
188     } else if (context == URLContext) {
189         id newURLValue = [change valueForKey:NSKeyValueChangeNewKey];
190         if ([newURLValue isKindOfClass:[NSURL class]])
191             self.urlField.text = [newURLValue absoluteString];
192         else if ([newURLValue isKindOfClass:[NSNull class]])
193             self.urlField.text = @"";
194     } else if (context == TitleContext)
195         [self.tabViewController.tableView reloadData];
196     else
197         [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
198 }
199
200 @end