diff --git a/Endless.xcodeproj/project.pbxproj b/Endless.xcodeproj/project.pbxproj index 8dea763..36d1ea4 100644 --- a/Endless.xcodeproj/project.pbxproj +++ b/Endless.xcodeproj/project.pbxproj @@ -80,6 +80,7 @@ 01FE82961FC0DC94006E5777 /* BlackIcon-83.5@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 01FE82951FC0DC94006E5777 /* BlackIcon-83.5@2x.png */; }; A024D5AE1ECEF10F00B28CC5 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = A024D5AA1ECEF10F00B28CC5 /* Localizable.strings */; }; A024D5AF1ECEF10F00B28CC5 /* OnePasswordExtension.strings in Resources */ = {isa = PBXBuildFile; fileRef = A024D5AC1ECEF10F00B28CC5 /* OnePasswordExtension.strings */; }; + A09A2AA52254BB2E00A3EEF0 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = A09A2AA72254BB2E00A3EEF0 /* InfoPlist.strings */; }; AA9A5FECA2E668F834FDECD9 /* libPods-Endless.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 095BC7BD685DE17FADDBF166 /* libPods-Endless.a */; }; D919EA71FF999D55CCC96334 /* libPods-Endless Tests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FC10255E5E506A0C25D6276B /* libPods-Endless Tests.a */; }; /* End PBXBuildFile section */ @@ -241,6 +242,9 @@ A024D5AD1ECEF10F00B28CC5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Endless/Base.lproj/OnePasswordExtension.strings; sourceTree = ""; }; A024D5B01ECEF11A00B28CC5 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = Endless/de.lproj/Localizable.strings; sourceTree = ""; }; A024D5B11ECEF11A00B28CC5 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = Endless/de.lproj/OnePasswordExtension.strings; sourceTree = ""; }; + A07E147222381A8700B76F77 /* SilenceWarnings.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SilenceWarnings.h; sourceTree = ""; }; + A09A2AA62254BB2E00A3EEF0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + A09A2AA82254BB2F00A3EEF0 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = ""; }; FC10255E5E506A0C25D6276B /* libPods-Endless Tests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Endless Tests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; FDEC322B35C8D861612E24E3 /* Pods-Endless Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Endless Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Endless Tests/Pods-Endless Tests.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -360,6 +364,7 @@ 01D42C3B1E0A37DB00566022 /* Endless.entitlements */, 018333CF1A351B3B00670CD1 /* Endless-Prefix.pch */, 01801E961A32CA2A002B4718 /* Info.plist */, + A09A2AA72254BB2E00A3EEF0 /* InfoPlist.strings */, 01801E971A32CA2A002B4718 /* main.m */, 01F7CB4C1A52FC4E00F42B73 /* NSString+IPAddress.h */, 01F7CB4D1A52FC4E00F42B73 /* NSString+IPAddress.m */, @@ -372,6 +377,7 @@ 013E71CF1E5150C000BB0572 /* urlblocker.json */, 015748031E208C5000DB2044 /* UIResponder+FirstResponder.h */, 015748041E208C5000DB2044 /* UIResponder+FirstResponder.m */, + A07E147222381A8700B76F77 /* SilenceWarnings.h */, ); name = "Supporting Files"; path = Endless; @@ -602,7 +608,7 @@ }; buildConfigurationList = 01801E8D1A32CA2A002B4718 /* Build configuration list for PBXProject "Endless" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -633,6 +639,7 @@ 018333E91A35746500670CD1 /* https-everywhere_rules.plist in Resources */, 01801EC31A3360F8002B4718 /* InAppSettings.bundle in Resources */, A024D5AF1ECEF10F00B28CC5 /* OnePasswordExtension.strings in Resources */, + A09A2AA52254BB2E00A3EEF0 /* InfoPlist.strings in Resources */, 01D7412C1A45F8EB007B7033 /* injected.js in Resources */, 01801EA61A32CA2A002B4718 /* Images.xcassets in Resources */, 01BFEE4A1E3D3CD60069AC83 /* urlblocker.json in Resources */, @@ -838,6 +845,15 @@ name = OnePasswordExtension.strings; sourceTree = ""; }; + A09A2AA72254BB2E00A3EEF0 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + A09A2AA62254BB2E00A3EEF0 /* en */, + A09A2AA82254BB2F00A3EEF0 /* de */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ diff --git a/Endless/AppDelegate.m b/Endless/AppDelegate.m index fc06926..226e628 100644 --- a/Endless/AppDelegate.m +++ b/Endless/AppDelegate.m @@ -111,22 +111,6 @@ - (void)applicationWillTerminate:(UIApplication *)application [application ignoreSnapshotOnNextApplicationLaunch]; } -- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation -{ -#ifdef TRACE - NSLog(@"[AppDelegate] request to open url at launch: %@", url); -#endif - if ([[[url scheme] lowercaseString] isEqualToString:@"endlesshttp"]) - url = [NSURL URLWithString:[[url absoluteString] stringByReplacingCharactersInRange:NSMakeRange(0, [@"endlesshttp" length]) withString:@"http"]]; - else if ([[[url scheme] lowercaseString] isEqualToString:@"endlesshttps"]) - url = [NSURL URLWithString:[[url absoluteString] stringByReplacingCharactersInRange:NSMakeRange(0, [@"endlesshttps" length]) withString:@"https"]]; - - /* delay until we're done drawing the UI */ - self.urlToOpenAtLaunch = url; - - return YES; -} - - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options { #ifdef TRACE @@ -138,9 +122,22 @@ - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDiction else if ([[[url scheme] lowercaseString] isEqualToString:@"endlesshttps"]) url = [NSURL URLWithString:[[url absoluteString] stringByReplacingCharactersInRange:NSMakeRange(0, [@"endlesshttps" length]) withString:@"https"]]; - [[self webViewController] dismissViewControllerAnimated:YES completion:nil]; - [[self webViewController] addNewTabForURL:url]; - + // In case, a modal view controller is overlaying the WebViewController, + // we need to close it *before* adding a new tab. Otherwise, the UI will + // be broken on iPhone-X-type devices: The address field will be in the + // notch area. + if ([self webViewController].presentedViewController != nil) + { + [[self webViewController] dismissViewControllerAnimated:YES completion:^{ + [[self webViewController] addNewTabForURL:url]; + }]; + } + // If there's no modal view controller, however, the completion block would + // never be called. + else { + [[self webViewController] addNewTabForURL:url]; + } + return YES; } diff --git a/Endless/Base.lproj/Localizable.strings b/Endless/Base.lproj/Localizable.strings index 9878d87..6fd47e8 100644 Binary files a/Endless/Base.lproj/Localizable.strings and b/Endless/Base.lproj/Localizable.strings differ diff --git a/Endless/HostSettingsController.m b/Endless/HostSettingsController.m index 67aecae..280c191 100644 --- a/Endless/HostSettingsController.m +++ b/Endless/HostSettingsController.m @@ -9,7 +9,11 @@ #import "HostSettings.h" #import "HostSettingsController.h" +#import "SilenceWarnings.h" + +SILENCE_DEPRECATION_ON #import "XLForm.h" +SILENCE_WARNINGS_OFF @interface HostSettingsXLFormViewController : XLFormViewController @property (copy, nonatomic) void (^disappearCallback)(HostSettingsXLFormViewController *); @@ -163,6 +167,8 @@ - (void)showDetailsForHost:(NSString *)thost XLFormDescriptor *form = [XLFormDescriptor formDescriptorWithTitle:[host hostname]]; + HostSettingsXLFormViewController *formController = [[HostSettingsXLFormViewController alloc] initWithForm:form]; + /* hostname */ { XLFormSectionDescriptor *section = [XLFormSectionDescriptor formSection]; @@ -207,7 +213,24 @@ - (void)showDetailsForHost:(NSString *)thost { XLFormRowDescriptor *row = [XLFormRowDescriptor formRowDescriptorWithTag:HOST_SETTINGS_KEY_ALLOW_WEBRTC rowType:XLFormRowDescriptorTypeSelectorActionSheet title:NSLocalizedString(@"Allow WebRTC", nil)]; [self setYesNoSelectorOptionsForSetting:HOST_SETTINGS_KEY_ALLOW_WEBRTC host:host row:row withDefault:(![host isDefault])]; - + + row.onChangeBlock = ^(XLFormOptionsObject * _Nullable oldValue, XLFormOptionsObject * _Nullable newValue, XLFormRowDescriptor * _Nonnull rowDescriptor) { + if ([newValue.formValue isEqual:HOST_SETTINGS_VALUE_YES]) { + XLFormRowDescriptor *row = [form formRowWithTag:HOST_SETTINGS_KEY_CSP]; + + if ([((XLFormOptionsObject *)row.value).formValue isEqual:HOST_SETTINGS_CSP_STRICT]) { + for (XLFormOptionsObject *opt in row.selectorOptions) { + if ([opt.valueData isEqual:HOST_SETTINGS_CSP_BLOCK_CONNECT]) { + row.value = opt; + break; + } + } + + [formController reloadFormRow: row]; + } + } + }; + section = [XLFormSectionDescriptor formSection]; [section setTitle:@""]; [section setFooterTitle:([host isDefault] @@ -304,7 +327,22 @@ - (void)showDetailsForHost:(NSString *)thost for (XLFormOptionsObject *opt in opts) if ([[opt valueData] isEqualToString:val]) [row setValue:opt]; - + + row.onChangeBlock = ^(XLFormOptionsObject * _Nullable oldValue, XLFormOptionsObject * _Nullable newValue, XLFormRowDescriptor * _Nonnull rowDescriptor) { + if ([newValue.formValue isEqual:HOST_SETTINGS_CSP_STRICT]) { + XLFormRowDescriptor *row = [form formRowWithTag:HOST_SETTINGS_KEY_ALLOW_WEBRTC]; + + for (XLFormOptionsObject *opt in row.selectorOptions) { + if ([opt.valueData isEqual:HOST_SETTINGS_VALUE_NO]) { + row.value = opt; + break; + } + } + + [formController reloadFormRow: row]; + } + }; + section = [XLFormSectionDescriptor formSection]; [section setTitle:@""]; [section setFooterTitle:([host isDefault] @@ -358,7 +396,6 @@ - (void)showDetailsForHost:(NSString *)thost } } - HostSettingsXLFormViewController *formController = [[HostSettingsXLFormViewController alloc] initWithForm:form]; [formController setDisappearCallback:^(HostSettingsXLFormViewController *form) { if (![host isDefault]) [host setHostname:[[form formValues] objectForKey:HOST_SETTINGS_KEY_HOST]]; diff --git a/Endless/Info.plist b/Endless/Info.plist index a08b5bf..e66c856 100644 --- a/Endless/Info.plist +++ b/Endless/Info.plist @@ -121,5 +121,7 @@ UIViewControllerBasedStatusBarAppearance + NSPhotoLibraryAddUsageDescription + Needed to store the selected image to your photos. diff --git a/Endless/RuleEditorController.h b/Endless/RuleEditorController.h index ba9bacc..df12f6d 100644 --- a/Endless/RuleEditorController.h +++ b/Endless/RuleEditorController.h @@ -9,13 +9,12 @@ #import "AppDelegate.h" #import "RuleEditorRow.h" -@interface RuleEditorController : UITableViewController +@interface RuleEditorController : UITableViewController @property AppDelegate *appDelegate; @property NSMutableArray *sortedRuleRows; @property NSMutableArray *inUseRuleRows; -@property UISearchBar *searchBar; @property NSMutableArray *searchResult; - (NSString *)ruleDisabledReason:(RuleEditorRow *)row; diff --git a/Endless/RuleEditorController.m b/Endless/RuleEditorController.m index abaec6d..5b7e19c 100644 --- a/Endless/RuleEditorController.m +++ b/Endless/RuleEditorController.m @@ -9,7 +9,7 @@ @implementation RuleEditorController -UISearchDisplayController *searchDisplayController; +UISearchController *searchController; - (id)initWithStyle:(UITableViewStyle)style { @@ -29,19 +29,17 @@ - (void)viewDidLoad { [super viewDidLoad]; - self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)]; - - searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self]; - searchDisplayController.delegate = self; - searchDisplayController.searchResultsDataSource = self; - searchDisplayController.searchResultsTableView.delegate = self; + searchController = [[UISearchController alloc] initWithSearchResultsController:nil]; + searchController.searchResultsUpdater = self; + searchController.obscuresBackgroundDuringPresentation = NO; + self.definesPresentationContext = YES; + self.navigationItem.searchController = searchController; self.tableView.delegate = self; - - [[self tableView] setTableHeaderView:self.searchBar]; } -#pragma mark - Table view data source + +#pragma mark - UITableViewDataSource - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { @@ -60,7 +58,7 @@ - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger { if (section == 0) return [self.inUseRuleRows count]; - else if (tableView == self.searchDisplayController.searchResultsTableView) + else if ([self isFiltering]) return [self.searchResult count]; else return [self.sortedRuleRows count]; @@ -121,18 +119,36 @@ - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEd [tableView reloadData]; } + +#pragma mark - UITableViewDelegate + - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [tableView deselectRowAtIndexPath:indexPath animated:YES]; } -- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString +- (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(nonnull NSIndexPath *)indexPath +{ + RuleEditorRow *row = [self ruleForTableView:tableView atIndexPath:indexPath]; + + if ([self ruleDisabledReason:row] == nil) + return NSLocalizedString(@"Disable", nil); + else + return NSLocalizedString(@"Enable", nil); +} + + +# pragma mark - UISearchResultsUpdating + +- (void)updateSearchResultsForSearchController:(UISearchController *)searchController { + NSString *search = searchController.searchBar.text; + [self.searchResult removeAllObjects]; - + for (RuleEditorRow *row in self.sortedRuleRows) { if ([row textLabel] != nil) { - NSRange range = [[row textLabel] rangeOfString:searchString options:NSCaseInsensitiveSearch]; + NSRange range = [[row textLabel] rangeOfString:search options:NSCaseInsensitiveSearch]; if (range.length > 0) { [self.searchResult addObject:row]; @@ -140,58 +156,59 @@ - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldRe } } if ([row detailTextLabel] != nil) { - NSRange range = [[row detailTextLabel] rangeOfString:searchString options:NSCaseInsensitiveSearch]; - + NSRange range = [[row detailTextLabel] rangeOfString:search options:NSCaseInsensitiveSearch]; + if (range.length > 0) { [self.searchResult addObject:row]; continue; } } } - - return YES; + + [self.tableView reloadData]; } -- (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(nonnull NSIndexPath *)indexPath + +# pragma mark - Public Methods + +- (NSString *)ruleDisabledReason:(RuleEditorRow *)row { - RuleEditorRow *row = [self ruleForTableView:tableView atIndexPath:indexPath]; + return nil; +} - if ([self ruleDisabledReason:row] == nil) - return NSLocalizedString(@"Disable", nil); - else - return NSLocalizedString(@"Enable", nil); +- (void)disableRuleForRow:(RuleEditorRow *)row withReason:(NSString *)reason +{ + abort(); } +- (void)enableRuleForRow:(RuleEditorRow *)row +{ + abort(); +} + + +# pragma mark - Private Methods + - (RuleEditorRow *)ruleForTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath { NSMutableArray *group; - + if ([indexPath section] == 0) group = [self inUseRuleRows]; - else if (tableView == self.searchDisplayController.searchResultsTableView) + else if ([self isFiltering]) group = [self searchResult]; else group = [self sortedRuleRows]; - + if (group && [group count] > [indexPath row]) return [group objectAtIndex:indexPath.row]; else return nil; } -- (NSString *)ruleDisabledReason:(RuleEditorRow *)row +- (BOOL)isFiltering { - return nil; -} - -- (void)disableRuleForRow:(RuleEditorRow *)row withReason:(NSString *)reason -{ - abort(); -} - -- (void)enableRuleForRow:(RuleEditorRow *)row -{ - abort(); + return searchController.isActive && searchController.searchBar.text.length != 0; } @end diff --git a/Endless/SilenceWarnings.h b/Endless/SilenceWarnings.h new file mode 100644 index 0000000..bfd10b0 --- /dev/null +++ b/Endless/SilenceWarnings.h @@ -0,0 +1,41 @@ +// +// SilenceWarnings.h +// Endless +// +// Created by Benjamin Erhart on 12.03.19. +// Copyright © 2019 jcs. All rights reserved. +// + +#ifndef SilenceWarnings_h +#define SilenceWarnings_h + +#define SILENCE_DEPRECATION_ON \ +_Pragma("clang diagnostic push") \ +_Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") \ +_Pragma("clang diagnostic ignored \"-Wdeprecated-implementations\"") + +#define SILENCE_DEPRECATION(expr) \ +do { \ +_Pragma("clang diagnostic push") \ +_Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") \ +_Pragma("clang diagnostic ignored \"-Wdeprecated-implementations\"") \ +expr; \ +_Pragma("clang diagnostic pop") \ +} while(0) + +#define SILENCE_PERFORM_SELECTOR_LEAKS_ON \ +_Pragma("clang diagnostic push") \ +_Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") + +#define SILENCE_PERFORM_SELECTOR_LEAKS(expr) \ +do { \ +_Pragma("clang diagnostic push") \ +_Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \ +expr; \ +_Pragma("clang diagnostic pop") \ +} while(0) + +#define SILENCE_WARNINGS_OFF \ +_Pragma("clang diagnostic pop") + +#endif /* SilenceWarnings_h */ diff --git a/Endless/URLInterceptor.m b/Endless/URLInterceptor.m index b53dd35..86dcb25 100644 --- a/Endless/URLInterceptor.m +++ b/Endless/URLInterceptor.m @@ -461,7 +461,7 @@ - (void)HTTPConnection:(CKHTTPConnection *)connection didReceiveResponse:(NSHTTP NSString *CSPmode = [self.originHostSettings settingOrDefault:HOST_SETTINGS_KEY_CSP]; if ([CSPmode isEqualToString:HOST_SETTINGS_CSP_STRICT]) - CSPheader = @"connect-src 'none'; default-src 'none'; font-src 'none'; media-src 'none'; object-src 'none'; sandbox allow-forms allow-top-navigation; script-src 'none'; style-src 'unsafe-inline' *; report-uri;"; + CSPheader = @"connect-src 'none'; default-src 'none'; font-src 'none'; media-src 'none'; object-src 'none'; sandbox allow-forms allow-top-navigation; script-src 'none'; style-src 'unsafe-inline' *; image-src 'unsafe-inline' *; report-uri;"; else if ([CSPmode isEqualToString:HOST_SETTINGS_CSP_BLOCK_CONNECT]) CSPheader = @"connect-src 'none'; media-src 'none'; object-src 'none'; report-uri;"; diff --git a/Endless/WebViewController.m b/Endless/WebViewController.m index d4ec256..55763e7 100644 --- a/Endless/WebViewController.m +++ b/Endless/WebViewController.m @@ -372,8 +372,7 @@ - (void)viewSafeAreaInsetsDidChange - (void)viewDidLayoutSubviews { - UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; - float statusBarHeight = (UIInterfaceOrientationIsLandscape(orientation) ? 0 : [[UIApplication sharedApplication] statusBarFrame].size.height); + float statusBarHeight = [[UIApplication sharedApplication] statusBarFrame].size.height; /* views are transforming and we may calculate things incorrectly here, so just ignore this request */ if (showingTabs) @@ -615,8 +614,10 @@ - (WebViewTab *)addNewTabForURL:(NSURL *)url forRestoration:(BOOL)restoration wi - (void)addNewTabFromToolbar:(id)_id { + UITextField *urlField = self->urlField; + [self addNewTabForURL:nil forRestoration:NO withAnimation:WebViewTabAnimationDefault withCompletionBlock:^(BOOL finished) { - [self->urlField becomeFirstResponder]; + [urlField becomeFirstResponder]; }]; } @@ -714,8 +715,11 @@ - (void)removeTab:(NSNumber *)tabNumber andFocusTab:(NSNumber *)toFocus else { /* no tabs left, add one and zoom out */ [self reindexTabs]; + + UITextField *urlField = self->urlField; + [self addNewTabForURL:nil forRestoration:false withAnimation:WebViewTabAnimationDefault withCompletionBlock:^(BOOL finished) { - [self->urlField becomeFirstResponder]; + [urlField becomeFirstResponder]; }]; return; } @@ -1218,7 +1222,9 @@ - (NSString *)buildDefaultUserAgent * to "Mozilla/5.0 (iPhone; CPU iPhone OS 8_4_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12H321 Safari/600.1.4" */ + SILENCE_DEPRECATION_ON UIWebView *twv = [[UIWebView alloc] initWithFrame:CGRectZero]; + SILENCE_WARNINGS_OFF NSString *ua = [twv stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"]; NSMutableArray *uapieces = [[NSMutableArray alloc] initWithArray:[ua componentsSeparatedByString:@" "]]; diff --git a/Endless/WebViewMenuController.m b/Endless/WebViewMenuController.m index 88ab247..f6b12fb 100644 --- a/Endless/WebViewMenuController.m +++ b/Endless/WebViewMenuController.m @@ -97,6 +97,12 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N cell.detailTextLabel.text = nil; cell.detailTextLabel.font = [UIFont systemFontOfSize:11]; + // Allow auto-adjustment for translations. + cell.textLabel.adjustsFontSizeToFitWidth = YES; + cell.textLabel.minimumScaleFactor = .5; + cell.detailTextLabel.adjustsFontSizeToFitWidth = YES; + cell.detailTextLabel.minimumScaleFactor = .5; + if ([[appDelegate webViewController] darkInterface]) { cell.textLabel.textColor = [UIColor colorWithRed:0.8 green:0.8 blue:0.8 alpha:1.0]; cell.detailTextLabel.textColor = [UIColor grayColor]; @@ -169,7 +175,8 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath SEL action = NSSelectorFromString([button objectForKey:FUNC]); if ([self respondsToSelector:action]) - [self performSelector:action]; + // The calls are hardcoded, there will be no leakage. + SILENCE_PERFORM_SELECTOR_LEAKS([self performSelector:action]); else NSLog(@"can't call %@", NSStringFromSelector(action)); } diff --git a/Endless/WebViewTab.h b/Endless/WebViewTab.h index c61a89d..8b262f9 100644 --- a/Endless/WebViewTab.h +++ b/Endless/WebViewTab.h @@ -9,6 +9,7 @@ #import #import "SSLCertificate.h" +#import "SilenceWarnings.h" #define ZOOM_OUT_SCALE 0.8 #define ZOOM_OUT_SCALE_ROTATED 0.7 @@ -99,7 +100,9 @@ static const struct keyboard_map_entry { @interface WebViewTab : NSObject @property (strong, atomic) UIView *viewHolder; +SILENCE_DEPRECATION_ON @property (strong, atomic) UIWebView *webView; +SILENCE_WARNINGS_OFF @property (strong, atomic) UIRefreshControl *refresher; @property (strong, atomic) NSURL *url; @property BOOL needsRefresh; diff --git a/Endless/WebViewTab.m b/Endless/WebViewTab.m index bfa8285..7026b7c 100644 --- a/Endless/WebViewTab.m +++ b/Endless/WebViewTab.m @@ -50,7 +50,7 @@ - (id)initWithFrame:(CGRect)frame withRestorationIdentifier:(NSString *)rid /* re-register user agent with our hash, which should only affect this UIWebView */ [[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"UserAgent": [NSString stringWithFormat:@"%@/%lu", [appDelegate defaultUserAgent], (unsigned long)self.hash] }]; - _webView = [[UIWebView alloc] initWithFrame:CGRectZero]; + SILENCE_DEPRECATION(_webView = [[UIWebView alloc] initWithFrame:CGRectZero]); _needsRefresh = FALSE; if (rid != nil) { [_webView setRestorationIdentifier:rid]; @@ -324,8 +324,11 @@ - (void)searchFor:(NSString *)query } /* this will only fire for top-level requests (and iframes), not page elements */ +SILENCE_DEPRECATION_ON - (BOOL)webView:(UIWebView *)__webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { +SILENCE_WARNINGS_OFF + NSURL *url = [request URL]; /* treat endlesshttps?:// links clicked inside of web pages as normal links */ @@ -475,8 +478,11 @@ - (BOOL)webView:(UIWebView *)__webView shouldStartLoadWithRequest:(NSURLRequest return NO; } +SILENCE_DEPRECATION_ON - (void)webViewDidStartLoad:(UIWebView *)__webView { +SILENCE_WARNINGS_OFF + /* reset and then let WebViewController animate to our actual progress */ [self setProgress:@0.0]; [self setProgress:@0.1]; @@ -485,8 +491,10 @@ - (void)webViewDidStartLoad:(UIWebView *)__webView self.url = [[__webView request] URL]; } +SILENCE_DEPRECATION_ON - (void)webViewDidFinishLoad:(UIWebView *)__webView { +SILENCE_WARNINGS_OFF #ifdef TRACE NSLog(@"[Tab %@] finished loading page/iframe %@, security level is %lu", self.tabIndex, [[[__webView request] URL] absoluteString], self.secureMode); #endif @@ -522,8 +530,11 @@ - (void)webViewDidFinishLoad:(UIWebView *)__webView skipHistory = NO; } +SILENCE_DEPRECATION_ON - (void)webView:(UIWebView *)__webView didFailLoadWithError:(NSError *)error { +SILENCE_WARNINGS_OFF + BOOL isTLSError = false; self.url = __webView.request.URL; @@ -570,7 +581,9 @@ - (void)webView:(UIWebView *)__webView didFailLoadWithError:(NSError *)error #ifdef TRACE NSLog(@"[Tab %@] not showing dialog for non-origin error: %@ (%@)", self.tabIndex, msg, error); #endif +SILENCE_DEPRECATION_ON [self webViewDidFinishLoad:__webView]; +SILENCE_WARNINGS_OFF return; } } @@ -613,12 +626,17 @@ - (void)webView:(UIWebView *)__webView didFailLoadWithError:(NSError *)error } [[appDelegate webViewController] presentViewController:uiac animated:YES completion:nil]; - + +SILENCE_DEPRECATION_ON [self webViewDidFinishLoad:__webView]; +SILENCE_WARNINGS_OFF } +SILENCE_DEPRECATION_ON - (void)webView:(UIWebView *)__webView callbackWith:(NSString *)callback { +SILENCE_WARNINGS_OFF + NSString *finalcb = [NSString stringWithFormat:@"(function() { %@; __endless.ipcDone = (new Date()).getTime(); })();", callback]; #ifdef TRACE_IPC @@ -760,6 +778,11 @@ - (void)pressedMenu:(UIGestureRecognizer *)event newtab.openedByTabHash = [NSNumber numberWithLong:self.hash]; }]; + UIAlertAction *openBackgroundTabAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Open in Background Tab", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { + WebViewTab *newtab = [[self->appDelegate webViewController] addNewTabForURL:[NSURL URLWithString:href] forRestoration:NO withAnimation:WebViewTabAnimationHidden withCompletionBlock:nil]; + newtab.openedByTabHash = [NSNumber numberWithLong:self.hash]; + }]; + UIAlertAction *openSafariAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Open in Safari", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:href] options:@{} completionHandler:nil]; }]; @@ -786,6 +809,7 @@ - (void)pressedMenu:(UIGestureRecognizer *)event if (href) { [alertController addAction:openAction]; [alertController addAction:openNewTabAction]; + [alertController addAction:openBackgroundTabAction]; [alertController addAction:openSafariAction]; } diff --git a/Endless/de.lproj/InfoPlist.strings b/Endless/de.lproj/InfoPlist.strings new file mode 100644 index 0000000..a0b8a52 --- /dev/null +++ b/Endless/de.lproj/InfoPlist.strings @@ -0,0 +1,8 @@ +/* + InfoPlist.strings + Endless + + Created by Benjamin Erhart on 03.04.19. + Copyright © 2019 jcs. All rights reserved. +*/ +NSPhotoLibraryAddUsageDescription = "Nötig, um das ausgewählte Bild zu ihren Fotos zu speichern."; diff --git a/Endless/de.lproj/Localizable.strings b/Endless/de.lproj/Localizable.strings index 80ba279..35b2750 100644 Binary files a/Endless/de.lproj/Localizable.strings and b/Endless/de.lproj/Localizable.strings differ diff --git a/Endless/en.lproj/InfoPlist.strings b/Endless/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..f7bf65b --- /dev/null +++ b/Endless/en.lproj/InfoPlist.strings @@ -0,0 +1,8 @@ +/* + InfoPlist.strings + Endless + + Created by Benjamin Erhart on 03.04.19. + Copyright © 2019 jcs. All rights reserved. +*/ +NSPhotoLibraryAddUsageDescription = "Needed to store the selected image to your photos."; diff --git a/External/CKHTTPConnection.m b/External/CKHTTPConnection.m index 099b7cc..a19674d 100644 --- a/External/CKHTTPConnection.m +++ b/External/CKHTTPConnection.m @@ -16,6 +16,7 @@ #import "CKHTTPConnection.h" #import "HostSettings.h" #import "SSLCertificate.h" +#import "../Endless/SilenceWarnings.h" @interface CKHTTPConnection () - (CFHTTPMessageRef)HTTPRequest; @@ -91,7 +92,8 @@ - (void)start { NSAssert(!_HTTPStream, @"Connection already started"); HostSettings *hs; - + +SILENCE_DEPRECATION_ON if (_HTTPBodyStream) _HTTPStream = (__bridge_transfer NSInputStream *)(CFReadStreamCreateForStreamedHTTPRequest(NULL, [self HTTPRequest], (__bridge CFReadStreamRef)_HTTPBodyStream)); else @@ -105,7 +107,9 @@ - (void)start CFReadStreamSetProperty((__bridge CFReadStreamRef)(_HTTPStream), kCFStreamPropertyHTTPAttemptPersistentConnection, kCFBooleanTrue); else CFReadStreamSetProperty((__bridge CFReadStreamRef)(_HTTPStream), kCFStreamPropertyHTTPAttemptPersistentConnection, kCFBooleanFalse); - + +SILENCE_WARNINGS_OFF + /* set SSL protocol version enforcement before opening, when using kCFStreamSSLLevel */ NSURL *url = (__bridge_transfer NSURL *)(CFHTTPMessageCopyRequestURL([self HTTPRequest])); if ([[[url scheme] lowercaseString] isEqualToString:@"https"]) { @@ -253,7 +257,8 @@ - (void)cancel - (void)stream:(NSInputStream *)theStream handleEvent:(NSStreamEvent)streamEvent { NSParameterAssert(theStream == [self stream]); - + +SILENCE_DEPRECATION_ON NSURL *URL = [theStream propertyForKey:(NSString *)kCFStreamPropertyHTTPFinalURL]; if (!_haveReceivedResponse) { @@ -262,7 +267,8 @@ - (void)stream:(NSInputStream *)theStream handleEvent:(NSStreamEvent)streamEvent NSHTTPURLResponse *URLResponse = [[NSHTTPURLResponse alloc] initWithURL:URL statusCode:CFHTTPMessageGetResponseStatusCode(response) HTTPVersion:(__bridge NSString * _Nullable)(CFHTTPMessageCopyVersion(response)) headerFields:(__bridge NSDictionary * _Nullable)(CFHTTPMessageCopyAllHeaderFields(response))]; NSData *d = (__bridge NSData *)CFHTTPMessageCopySerializedMessage((__bridge CFHTTPMessageRef _Nonnull)([_HTTPStream propertyForKey:(NSString *)kCFStreamPropertyHTTPResponseHeader])); - +SILENCE_WARNINGS_OFF + /* work around bug where CFHTTPMessageIsHeaderComplete reports true but there is no actual header data to be found */ if ([d length] < 5) { #ifdef TRACE @@ -452,8 +458,6 @@ - (id)initWithResponse:(CFHTTPMessageRef)response { NSParameterAssert(response); -#warning "Instance variable used while 'self' is not set to the result of [self init]" - // Try to create an authentication object from the response _HTTPAuthentication = CFHTTPAuthenticationCreateFromResponse(NULL, response); if (![self CFHTTPAuthentication]) diff --git a/External/OnePasswordExtension.m b/External/OnePasswordExtension.m index 7b7b663..cb55b66 100644 --- a/External/OnePasswordExtension.m +++ b/External/OnePasswordExtension.m @@ -6,6 +6,7 @@ // #import "OnePasswordExtension.h" +#import "../Endless/SilenceWarnings.h" // Version #define VERSION_NUMBER @(184) @@ -208,10 +209,13 @@ - (void)changePasswordForLoginForURLString:(nonnull NSString *)URLString loginDe - (void)fillItemIntoWebView:(nonnull id)webView forViewController:(nonnull UIViewController *)viewController sender:(nullable id)sender showOnlyLogins:(BOOL)yesOrNo completion:(nonnull OnePasswordSuccessCompletionBlock)completion { NSAssert(webView != nil, @"webView must not be nil"); NSAssert(viewController != nil, @"viewController must not be nil"); - NSAssert([webView isKindOfClass:[UIWebView class]] || [webView isKindOfClass:[WKWebView class]], @"webView must be an instance of WKWebView or UIWebView."); + SILENCE_DEPRECATION(NSAssert([webView isKindOfClass:[UIWebView class]] || [webView isKindOfClass:[WKWebView class]], @"webView must be an instance of WKWebView or UIWebView.")); #ifdef __IPHONE_8_0 +SILENCE_DEPRECATION_ON if ([webView isKindOfClass:[UIWebView class]]) { +SILENCE_WARNINGS_OFF + [self fillItemIntoUIWebView:webView webViewController:viewController sender:(id)sender showOnlyLogins:yesOrNo completion:^(BOOL success, NSError *error) { if (completion) { completion(success, error); @@ -238,11 +242,15 @@ - (BOOL)isOnePasswordExtensionActivityType:(nullable NSString *)activityType { - (void)createExtensionItemForWebView:(nonnull id)webView completion:(nonnull OnePasswordExtensionItemCompletionBlock)completion { NSAssert(webView != nil, @"webView must not be nil"); - NSAssert([webView isKindOfClass:[UIWebView class]] || [webView isKindOfClass:[WKWebView class]], @"webView must be an instance of WKWebView or UIWebView."); + SILENCE_DEPRECATION(NSAssert([webView isKindOfClass:[UIWebView class]] || [webView isKindOfClass:[WKWebView class]], @"webView must be an instance of WKWebView or UIWebView.")); #ifdef __IPHONE_8_0 + +SILENCE_DEPRECATION_ON if ([webView isKindOfClass:[UIWebView class]]) { UIWebView *uiWebView = (UIWebView *)webView; +SILENCE_WARNINGS_OFF + NSString *collectedPageDetails = [uiWebView stringByEvaluatingJavaScriptFromString:OPWebViewCollectFieldsScript]; [self createExtensionItemForURLString:uiWebView.request.URL.absoluteString webPageDetails:collectedPageDetails completion:completion]; @@ -401,7 +409,10 @@ - (void)fillItemIntoWKWebView:(nonnull WKWebView *)webView forViewController:(no } #endif +SILENCE_DEPRECATION_ON - (void)fillItemIntoUIWebView:(nonnull UIWebView *)webView webViewController:(nonnull UIViewController *)viewController sender:(nullable id)sender showOnlyLogins:(BOOL)yesOrNo completion:(nonnull OnePasswordSuccessCompletionBlock)completion { +SILENCE_WARNINGS_OFF + NSString *collectedPageDetails = [webView stringByEvaluatingJavaScriptFromString:OPWebViewCollectFieldsScript]; [self findLoginIn1PasswordWithURLString:webView.request.URL.absoluteString collectedPageDetails:collectedPageDetails forWebViewController:viewController sender:sender withWebView:webView showOnlyLogins:yesOrNo completion:^(BOOL success, NSError *error) { if (completion) { @@ -425,8 +436,11 @@ - (void)executeFillScript:(NSString * __nullable)fillScript inWebView:(nonnull i [scriptSource appendFormat:@"(document, %@, undefined);", fillScript]; #ifdef __IPHONE_8_0 +SILENCE_DEPRECATION_ON if ([webView isKindOfClass:[UIWebView class]]) { NSString *result = [((UIWebView *)webView) stringByEvaluatingJavaScriptFromString:scriptSource]; +SILENCE_WARNINGS_OFF + BOOL success = (result != nil); NSError *error = nil; diff --git a/Podfile.lock b/Podfile.lock index 2d3ef77..62e0b2c 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -104,4 +104,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 26382b338af288c4202e8abd44a2f9ff01e82209 -COCOAPODS: 1.6.0 +COCOAPODS: 1.6.1 diff --git a/README.md b/README.md index 98cbe21..0a17692 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,6 @@ An open-source MobileSafari-like web browser for iOS ([wrapping around UIWebView](#infrequently-asked-questions)) with a design goal of increased security and privacy. -Current builds are available for free in the -[App Store](https://itunes.apple.com/us/app/endless-browser/id974745755?mt=8) -and include (completely optional) in-app purchases to contribute to the funding -of continued development. - While this software is open source and you are free to modify it and use it on your own devices, redistribution of this software in binary form, with or without modification, is not permitted.