Add a test harness for running UI tests on the iOS Simulator
[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 "config.h"
27 #import "WebViewController.h"
28
29 #import "TabViewController.h"
30 #import <WebKit/WKNavigation.h>
31 #import <WebKit/WKNavigationDelegate.h>
32 #import <WebKit/WKWebView.h>
33 #import <WebKit/WKWebViewConfiguration.h>
34
35 @interface WebViewController () <WKNavigationDelegate> {
36     WKWebView *_currentWebView;
37 }
38 - (WKWebView *)createWebView;
39 - (void)removeWebView:(WKWebView *)webView;
40 - (void)setCurrentWebView:(WKWebView *)webView;
41 @end
42
43 void* EstimatedProgressContext = &EstimatedProgressContext;
44 void* TitleContext = &TitleContext;
45 void* URLContext = &URLContext;
46
47 @implementation WebViewController
48
49 - (void)viewDidLoad
50 {
51     [super viewDidLoad];
52     self.webViews = [[NSMutableArray alloc] initWithCapacity:1];
53     self.tabViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"idTabViewController"];
54     self.tabViewController.parent = self;
55     self.tabViewController.modalPresentationStyle = UIModalPresentationPopover;
56
57     [self setCurrentWebView:[self createWebView]];
58 }
59
60
61 - (void)didReceiveMemoryWarning
62 {
63     [super didReceiveMemoryWarning];
64 }
65
66 #pragma mark Actions
67
68 - (IBAction)reload:(id)sender
69 {
70     [self.currentWebView reload];
71 }
72
73 - (IBAction)goBack:(id)sender
74 {
75     [self.currentWebView goBack];
76 }
77
78 - (IBAction)goForward:(id)sender
79 {
80     [self.currentWebView goForward];
81 }
82
83 - (IBAction)urlFieldEditingBegan:(id)sender
84 {
85     self.urlField.selectedTextRange = [self.urlField textRangeFromPosition:self.urlField.beginningOfDocument toPosition:self.urlField.endOfDocument];
86 }
87
88 - (IBAction)navigateTo:(id)sender
89 {
90     [self.urlField resignFirstResponder];
91     NSString* requestedDestination = self.urlField.text;
92     if ([requestedDestination rangeOfString:@"^[\\p{Alphabetic}]+:" options:(NSRegularExpressionSearch | NSCaseInsensitiveSearch | NSAnchoredSearch)].location == NSNotFound)
93         requestedDestination = [@"http://" stringByAppendingString:requestedDestination];
94     [self.currentWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:requestedDestination]]];
95 }
96
97 - (IBAction)showTabs:(id)sender
98 {
99     [self presentViewController:self.tabViewController animated:YES completion:^{ }];
100     self.tabViewController.popoverPresentationController.barButtonItem = self.tabButton;
101 }
102
103 #pragma mark Public methods
104
105 @dynamic currentWebView;
106
107 - (WKWebView *)currentWebView
108 {
109     return _currentWebView;
110 }
111
112 - (void)setCurrentWebView:(WKWebView *)webView
113 {
114     [_currentWebView removeObserver:self forKeyPath:@"estimatedProgress" context:EstimatedProgressContext];
115     [_currentWebView removeObserver:self forKeyPath:@"URL" context:URLContext];
116     [_currentWebView removeFromSuperview];
117
118     _currentWebView = webView;
119
120     [_currentWebView addObserver:self forKeyPath:@"estimatedProgress" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:EstimatedProgressContext];
121     [_currentWebView addObserver:self forKeyPath:@"URL" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:URLContext];
122     [self.webViewContainer addSubview:_currentWebView];
123 }
124
125 - (void)selectWebViewAtIndex:(NSUInteger)index
126 {
127     if (index >= self.webViews.count)
128         return;
129     self.currentWebView = self.webViews[index];
130 }
131
132 - (void)removeWebViewAtIndex:(NSUInteger)index
133 {
134     if (index >= self.webViews.count)
135         return;
136     [self removeWebView:self.webViews[index]];
137 }
138
139 - (void)addWebView
140 {
141     self.currentWebView = [self createWebView];
142 }
143
144 #pragma mark Internal methods
145
146 - (WKWebView *)createWebView
147 {
148     WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
149     WKWebView *webView = [[WKWebView alloc] initWithFrame:self.webViewContainer.bounds configuration:configuration];
150     webView.navigationDelegate = self;
151     webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
152     [webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:TitleContext];
153     [self.webViews addObject:webView];
154
155     [self.tabViewController.tableView reloadData];
156
157     return webView;
158 }
159
160 - (void)removeWebView:(WKWebView *)webView
161 {
162     NSUInteger index = [self.webViews indexOfObject:webView];
163     if (index != NSNotFound) {
164         [self.webViews[index] removeObserver:self forKeyPath:@"title" context:TitleContext];
165         [self.webViews removeObjectAtIndex:index];
166     } if (!self.webViews.count)
167         [self createWebView];
168     if (index >= self.webViews.count)
169         index = self.webViews.count - 1;
170     [self setCurrentWebView:self.webViews[index]];
171
172     [self.tabViewController.tableView reloadData];
173 }
174
175 #pragma mark Navigation Delegate
176
177 - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;
178 {
179     [webView loadHTMLString:[error description] baseURL:nil];
180 }
181
182 #pragma mark KVO
183
184 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
185 {
186     if (context == EstimatedProgressContext) {
187         float value = [[change valueForKey:NSKeyValueChangeNewKey] floatValue];
188         [self.progressView setProgress:value animated:YES];
189     } else if (context == URLContext) {
190         id newURLValue = [change valueForKey:NSKeyValueChangeNewKey];
191         if ([newURLValue isKindOfClass:[NSURL class]])
192             self.urlField.text = [newURLValue absoluteString];
193         else if ([newURLValue isKindOfClass:[NSNull class]])
194             self.urlField.text = @"";
195     } else if (context == TitleContext)
196         [self.tabViewController.tableView reloadData];
197     else
198         [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
199 }
200
201 @end