237 lines
8.4 KiB
Objective-C
237 lines
8.4 KiB
Objective-C
/*
|
|
* Copyright (C) 2016 Apple Inc. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#import "WebViewController.h"
|
|
|
|
#import "TabViewController.h"
|
|
#import <WebKit/WKNavigation.h>
|
|
#import <WebKit/WKNavigationDelegate.h>
|
|
#import <WebKit/WKPreferencesPrivate.h>
|
|
#import <WebKit/WKWebView.h>
|
|
#import <WebKit/WKWebViewConfiguration.h>
|
|
|
|
@implementation NSURL (BundleURLMethods)
|
|
+ (NSURL *)__bundleURLForFileURL:(NSURL *)url bundle:(NSBundle *)bundle
|
|
{
|
|
if (![url.scheme isEqualToString:@"file"])
|
|
return nil;
|
|
NSString *resourcePath = [bundle.resourcePath stringByAppendingString:@"/"];
|
|
if (![url.path hasPrefix:resourcePath])
|
|
return nil;
|
|
NSURLComponents *bundleComponents = [[NSURLComponents alloc] init];
|
|
bundleComponents.scheme = @"bundle";
|
|
bundleComponents.path = [url.path substringFromIndex:resourcePath.length];
|
|
return [bundleComponents.URL copy];
|
|
}
|
|
|
|
+ (NSURL *)__fileURLForBundleURL:(NSURL *)url bundle:(NSBundle *)bundle
|
|
{
|
|
if (![url.scheme isEqualToString:@"bundle"])
|
|
return nil;
|
|
return [bundle.resourceURL URLByAppendingPathComponent:url.path];
|
|
}
|
|
@end
|
|
|
|
@interface WebViewController () <WKNavigationDelegate> {
|
|
WKWebView *_currentWebView;
|
|
}
|
|
- (WKWebView *)createWebView;
|
|
- (void)removeWebView:(WKWebView *)webView;
|
|
- (void)setCurrentWebView:(WKWebView *)webView;
|
|
@end
|
|
|
|
void* EstimatedProgressContext = &EstimatedProgressContext;
|
|
void* TitleContext = &TitleContext;
|
|
void* URLContext = &URLContext;
|
|
|
|
@implementation WebViewController
|
|
|
|
- (void)viewDidLoad
|
|
{
|
|
[super viewDidLoad];
|
|
self.webViews = [[NSMutableArray alloc] initWithCapacity:1];
|
|
self.tabViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"idTabViewController"];
|
|
self.tabViewController.parent = self;
|
|
self.tabViewController.modalPresentationStyle = UIModalPresentationPopover;
|
|
|
|
WKWebView *webView = [self createWebView];
|
|
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://webkit.org"]]];
|
|
[self setCurrentWebView:webView];
|
|
}
|
|
|
|
|
|
- (void)didReceiveMemoryWarning
|
|
{
|
|
[super didReceiveMemoryWarning];
|
|
}
|
|
|
|
#pragma mark Actions
|
|
|
|
- (IBAction)reload:(id)sender
|
|
{
|
|
[self.currentWebView reload];
|
|
}
|
|
|
|
- (IBAction)goBack:(id)sender
|
|
{
|
|
[self.currentWebView goBack];
|
|
}
|
|
|
|
- (IBAction)goForward:(id)sender
|
|
{
|
|
[self.currentWebView goForward];
|
|
}
|
|
|
|
- (IBAction)urlFieldEditingBegan:(id)sender
|
|
{
|
|
self.urlField.selectedTextRange = [self.urlField textRangeFromPosition:self.urlField.beginningOfDocument toPosition:self.urlField.endOfDocument];
|
|
}
|
|
|
|
- (IBAction)navigateTo:(id)sender
|
|
{
|
|
[self.urlField resignFirstResponder];
|
|
NSString* requestedDestination = self.urlField.text;
|
|
if ([requestedDestination rangeOfString:@"^[\\p{Alphabetic}]+:" options:(NSRegularExpressionSearch | NSCaseInsensitiveSearch | NSAnchoredSearch)].location == NSNotFound)
|
|
requestedDestination = [@"http://" stringByAppendingString:requestedDestination];
|
|
NSURL* requestedURL = [NSURL URLWithString:requestedDestination];
|
|
if ([requestedURL.scheme isEqualToString:@"bundle"]) {
|
|
NSBundle *frameworkBundle = [NSBundle bundleForClass:[WebViewController class]];
|
|
requestedURL = [NSURL __fileURLForBundleURL:requestedURL bundle:frameworkBundle];
|
|
[self.currentWebView loadFileURL:requestedURL allowingReadAccessToURL:frameworkBundle.resourceURL];
|
|
}
|
|
[self.currentWebView loadRequest:[NSURLRequest requestWithURL:requestedURL]];
|
|
}
|
|
|
|
- (IBAction)showTabs:(id)sender
|
|
{
|
|
[self presentViewController:self.tabViewController animated:YES completion:nil];
|
|
self.tabViewController.popoverPresentationController.barButtonItem = self.tabButton;
|
|
}
|
|
|
|
#pragma mark Public methods
|
|
|
|
@dynamic currentWebView;
|
|
|
|
- (WKWebView *)currentWebView
|
|
{
|
|
return _currentWebView;
|
|
}
|
|
|
|
- (void)setCurrentWebView:(WKWebView *)webView
|
|
{
|
|
[_currentWebView removeObserver:self forKeyPath:@"estimatedProgress" context:EstimatedProgressContext];
|
|
[_currentWebView removeObserver:self forKeyPath:@"URL" context:URLContext];
|
|
[_currentWebView removeFromSuperview];
|
|
|
|
_currentWebView = webView;
|
|
|
|
[_currentWebView addObserver:self forKeyPath:@"estimatedProgress" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:EstimatedProgressContext];
|
|
[_currentWebView addObserver:self forKeyPath:@"URL" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:URLContext];
|
|
[self.webViewContainer addSubview:_currentWebView];
|
|
}
|
|
|
|
- (void)selectWebViewAtIndex:(NSUInteger)index
|
|
{
|
|
if (index >= self.webViews.count)
|
|
return;
|
|
self.currentWebView = self.webViews[index];
|
|
}
|
|
|
|
- (void)removeWebViewAtIndex:(NSUInteger)index
|
|
{
|
|
if (index >= self.webViews.count)
|
|
return;
|
|
[self removeWebView:self.webViews[index]];
|
|
}
|
|
|
|
- (void)addWebView
|
|
{
|
|
self.currentWebView = [self createWebView];
|
|
}
|
|
|
|
#pragma mark Internal methods
|
|
|
|
- (WKWebView *)createWebView
|
|
{
|
|
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
|
|
|
|
configuration.preferences._mockCaptureDevicesEnabled = YES;
|
|
|
|
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.webViewContainer.bounds configuration:configuration];
|
|
webView.navigationDelegate = self;
|
|
webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
[webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:TitleContext];
|
|
[self.webViews addObject:webView];
|
|
|
|
[self.tabViewController.tableView reloadData];
|
|
|
|
return webView;
|
|
}
|
|
|
|
- (void)removeWebView:(WKWebView *)webView
|
|
{
|
|
NSUInteger index = [self.webViews indexOfObject:webView];
|
|
if (index != NSNotFound) {
|
|
[self.webViews[index] removeObserver:self forKeyPath:@"title" context:TitleContext];
|
|
[self.webViews removeObjectAtIndex:index];
|
|
} if (!self.webViews.count)
|
|
[self createWebView];
|
|
if (index >= self.webViews.count)
|
|
index = self.webViews.count - 1;
|
|
[self setCurrentWebView:self.webViews[index]];
|
|
|
|
[self.tabViewController.tableView reloadData];
|
|
}
|
|
|
|
#pragma mark Navigation Delegate
|
|
|
|
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error
|
|
{
|
|
[webView loadHTMLString:[error description] baseURL:nil];
|
|
}
|
|
|
|
#pragma mark KVO
|
|
|
|
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
|
|
{
|
|
if (context == EstimatedProgressContext) {
|
|
float value = [[change valueForKey:NSKeyValueChangeNewKey] floatValue];
|
|
[self.progressView setProgress:value animated:YES];
|
|
} else if (context == URLContext) {
|
|
NSURL *newURLValue = [change valueForKey:NSKeyValueChangeNewKey];
|
|
if ([newURLValue isKindOfClass:[NSURL class]]) {
|
|
if ([newURLValue.scheme isEqualToString:@"file"])
|
|
newURLValue = [NSURL __bundleURLForFileURL:newURLValue bundle:[NSBundle bundleForClass:[WebViewController class]]];
|
|
self.urlField.text = [newURLValue absoluteString];
|
|
} else if ([newURLValue isKindOfClass:[NSNull class]])
|
|
self.urlField.text = @"";
|
|
} else if (context == TitleContext)
|
|
[self.tabViewController.tableView reloadData];
|
|
else
|
|
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
|
}
|
|
|
|
@end
|