Files
bundesmessenger-ios/Riot/Modules/MatrixKit/Controllers/MXKWebViewViewController.m
JanNiklas Grabowski b298dedc22 chore: update from foss 1.11.19 (MESSENGER-6656)
Merge commit 'f823ab9aae70e8d15ed7cc079210dd9bbbb6c8e1' into feature/foss_update_1_11_19

* commit 'f823ab9aae70e8d15ed7cc079210dd9bbbb6c8e1':
  finish version++
  version++
  comments
  update submodule
  remove obsolete tests
  removed unused code
  update submodule
  fix
  Libolm removal
  update license macro
  update license
  Prepare for new sprint

# Conflicts:
#	Config/AppVersion.xcconfig
#	IDETemplateMacros.plist
#	LICENSE
#	README.md
#	Riot/Categories/MXSession+Riot.m
#	Riot/Managers/EncryptionKeyManager/EncryptionKeyManager.swift
#	Riot/Managers/KeyValueStorage/Extensions/Keychain.swift
#	Riot/Managers/KeyValueStorage/KeyValueStore.swift
#	Riot/Managers/KeyValueStorage/KeychainStore.swift
#	Riot/Managers/KeyValueStorage/MemoryStore.swift
#	Riot/Managers/PushNotification/PushNotificationService.m
#	Riot/Managers/Settings/RiotSettings.swift
#	Riot/Managers/Settings/Shared/RiotSharedSettings.swift
#	Riot/Modules/Analytics/AnalyticsUIElement.swift
#	Riot/Modules/Application/AppCoordinator.swift
#	Riot/Modules/Application/LegacyAppDelegate.h
#	Riot/Modules/Application/LegacyAppDelegate.m
#	Riot/Modules/Authentication/Legacy/AuthenticationViewController.h
#	Riot/Modules/Authentication/Legacy/AuthenticationViewController.m
#	Riot/Modules/Authentication/Legacy/Views/AuthInputsView.h
#	Riot/Modules/Authentication/Legacy/Views/AuthInputsView.m
#	Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m
#	Riot/Modules/Common/Recents/RecentsViewController.m
#	Riot/Modules/Common/WebViewController/WebViewViewController.m
#	Riot/Modules/Contacts/Details/ContactDetailsViewController.m
#	Riot/Modules/Contacts/Views/ContactTableViewCell.m
#	Riot/Modules/Favorites/FavouritesViewController.h
#	Riot/Modules/Favorites/FavouritesViewController.m
#	Riot/Modules/GlobalSearch/UnifiedSearchViewController.m
#	Riot/Modules/People/PeopleViewController.h
#	Riot/Modules/People/PeopleViewController.m
#	Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift
#	Riot/Modules/Room/DataSources/RoomDataSource.m
#	Riot/Modules/Room/Files/RoomFilesViewController.m
#	Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m
#	Riot/Modules/Room/Members/RoomParticipantsViewController.m
#	Riot/Modules/Room/RoomViewController.m
#	Riot/Modules/Room/Settings/RoomSettingsViewController.m
#	Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroCell.swift
#	Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroCellContentView.swift
#	Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroViewData.swift
#	Riot/Modules/Room/TimelineCells/RoomTimelineCellIdentifier.h
#	Riot/Modules/Rooms/RoomsViewController.h
#	Riot/Modules/Rooms/ShowDirectory/Cells/Network/DirectoryNetworkTableHeaderFooterView.swift
#	Riot/Modules/Rooms/ShowDirectory/Cells/Room/DirectoryRoomTableViewCell.swift
#	Riot/Modules/Rooms/ShowDirectory/PublicRoomsDirectoryViewModel.swift
#	Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyCoordinator.swift
#	Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewController.swift
#	Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinator.swift
#	Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.swift
#	Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinator.swift
#	Riot/Modules/SecureBackup/Setup/Intro/SecureBackupSetupIntroViewController.swift
#	Riot/Modules/SecureBackup/Setup/Intro/SecureBackupSetupIntroViewModel.swift
#	Riot/Modules/SecureBackup/Setup/Intro/SecureBackupSetupIntroViewModelType.swift
#	Riot/Modules/SetPinCode/PinCodePreferences.swift
#	Riot/Modules/SetPinCode/SetupBiometrics/BiometricsAuthenticationPresenter.swift
#	Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m
#	Riot/Modules/Settings/Security/SecurityViewController.m
#	Riot/Modules/Settings/SettingsViewController.m
#	Riot/Modules/SplitView/SplitViewCoordinator.swift
#	Riot/Modules/SplitView/SplitViewCoordinatorType.swift
#	Riot/Modules/StartChat/StartChatViewController.m
#	Riot/Modules/TabBar/MasterTabBarController.h
#	Riot/Modules/TabBar/MasterTabBarController.m
#	Riot/Utils/EventFormatter.m
#	Riot/Utils/HTMLFormatter.swift
#	Riot/Utils/Tools.m
#	RiotNSE/NotificationService.swift
2024-10-18 15:45:54 +02:00

362 lines
13 KiB
Objective-C

/*
Copyright 2024 New Vector Ltd.
Copyright 2019 The Matrix.org Foundation C.I.C
Copyright 2018 New Vector Ltd
Copyright 2016 OpenMarket Ltd
SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
#import "MXKWebViewViewController.h"
#import "NSBundle+MatrixKit.h"
#import <JavaScriptCore/JavaScriptCore.h>
#import "MXKSwiftHeader.h"
NSString *const kMXKWebViewViewControllerPostMessageJSLog = @"jsLog";
// Override console.* logs methods to send WebKit postMessage events to native code.
// Note: this code has a minimal support of multiple parameters in console.log()
NSString *const kMXKWebViewViewControllerJavaScriptEnableLog =
@"console.debug = console.log; console.info = console.log; console.warn = console.log; console.error = console.log;" \
@"console.log = function() {" \
@" var msg = arguments[0];" \
@" for (var i = 1; i < arguments.length; i++) {" \
@" msg += ' ' + arguments[i];" \
@" }" \
@" window.webkit.messageHandlers.%@.postMessage(msg);" \
@"};";
@interface MXKWebViewViewController ()
{
BOOL enableDebug;
// Right buttons bar state before loading the webview
NSArray<UIBarButtonItem *> *originalRightBarButtonItems;
}
@end
@implementation MXKWebViewViewController
- (instancetype)init
{
self = [super init];
if (self)
{
enableDebug = NO;
}
return self;
}
- (id)initWithURL:(NSString*)URL
{
self = [self init];
if (self)
{
_URL = URL;
}
return self;
}
- (id)initWithLocalHTMLFile:(NSString*)localHTMLFile
{
self = [self init];
if (self)
{
_localHTMLFile = localHTMLFile;
}
return self;
}
- (void)enableDebug
{
// We can only call addScriptMessageHandler on a given message only once
if (enableDebug)
{
return;
}
enableDebug = YES;
// Redirect all console.* logging methods into a WebKit postMessage event with name "jsLog"
[webView.configuration.userContentController addScriptMessageHandler:self name:kMXKWebViewViewControllerPostMessageJSLog];
NSString *javaScriptString = [NSString stringWithFormat:kMXKWebViewViewControllerJavaScriptEnableLog, kMXKWebViewViewControllerPostMessageJSLog];
[webView evaluateJavaScript:javaScriptString completionHandler:nil];
}
- (void)finalizeInit
{
[super finalizeInit];
}
- (void)viewDidLoad
{
[super viewDidLoad];
originalRightBarButtonItems = self.navigationItem.rightBarButtonItems;
// Init the webview
webView = [[WKWebView alloc] initWithFrame:self.view.frame];
webView.backgroundColor= [UIColor whiteColor];
webView.navigationDelegate = self;
webView.UIDelegate = self;
[webView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:webView];
// Force webview in full width (to handle auto-layout in case of screen rotation)
NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:webView
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:0];
NSLayoutConstraint *rightConstraint = [NSLayoutConstraint constraintWithItem:webView
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:0];
// Force webview in full height
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:webView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.topLayoutGuide
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0];
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:webView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.bottomLayoutGuide
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0];
#pragma clang diagnostic pop
[NSLayoutConstraint activateConstraints:@[leftConstraint, rightConstraint, topConstraint, bottomConstraint]];
backButton = [[UIBarButtonItem alloc] initWithTitle:[VectorL10n back] style:UIBarButtonItemStylePlain target:self action:@selector(goBack)];
if (_URL.length)
{
self.URL = _URL;
}
else if (_localHTMLFile.length)
{
self.localHTMLFile = _localHTMLFile;
}
}
- (void)destroy
{
if (webView)
{
webView.navigationDelegate = nil;
[webView stopLoading];
[webView removeFromSuperview];
webView = nil;
}
backButton = nil;
_URL = nil;
_localHTMLFile = nil;
[super destroy];
}
- (void)dealloc
{
[self destroy];
}
- (void)setURL:(NSString *)URL
{
[webView stopLoading];
_URL = URL;
_localHTMLFile = nil;
if (URL.length)
{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:URL]];
[webView loadRequest:request];
}
}
- (void)setLocalHTMLFile:(NSString *)localHTMLFile
{
[webView stopLoading];
_localHTMLFile = localHTMLFile;
_URL = nil;
if (localHTMLFile.length)
{
NSString* htmlString = [NSString stringWithContentsOfFile:localHTMLFile encoding:NSUTF8StringEncoding error:nil];
[webView loadHTMLString:htmlString baseURL:nil];
}
}
- (void)goBack
{
if (webView.canGoBack)
{
[webView goBack];
}
else if (_localHTMLFile.length)
{
// Reload local html file
self.localHTMLFile = _localHTMLFile;
}
}
#pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
// Handle back button visibility here
BOOL canGoBack = webView.canGoBack;
if (_localHTMLFile.length && !canGoBack)
{
// Check whether the current content is not the local html file
canGoBack = (![webView.URL.absoluteString isEqualToString:@"about:blank"]);
}
if (canGoBack)
{
self.navigationItem.rightBarButtonItem = backButton;
}
else
{
// Reset the original state
self.navigationItem.rightBarButtonItems = originalRightBarButtonItems;
}
}
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler
{
NSURLProtectionSpace *protectionSpace = [challenge protectionSpace];
// We handle here only the server trust authentication.
// We fallback to the default logic for other cases.
if (protectionSpace.authenticationMethod != NSURLAuthenticationMethodServerTrust || !protectionSpace.serverTrust)
{
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
return;
}
SecTrustRef serverTrust = [protectionSpace serverTrust];
// Check first whether there are some pinned certificates (certificate included in the bundle).
NSArray *paths = [[NSBundle mainBundle] pathsForResourcesOfType:@"cer" inDirectory:@"."];
if (paths.count)
{
NSMutableArray *pinnedCertificates = [NSMutableArray array];
for (NSString *path in paths)
{
NSData *certificateData = [NSData dataWithContentsOfFile:path];
[pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
}
// Only use these certificates to pin against, and do not trust the built-in anchor certificates.
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
}
else
{
// Check whether some certificates have been trusted by the user (self-signed certificates support).
NSSet<NSData *> *certificates = [MXAllowedCertificates sharedInstance].certificates;
if (certificates.count)
{
NSMutableArray *allowedCertificates = [NSMutableArray array];
for (NSData *certificateData in certificates)
{
[allowedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
}
// Add all the allowed certificates to the chain of trust
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)allowedCertificates);
// Reenable trusting the built-in anchor certificates in addition to those passed in via the SecTrustSetAnchorCertificates API.
SecTrustSetAnchorCertificatesOnly(serverTrust, false);
}
}
// Re-evaluate the trust policy
SecTrustResultType secresult = kSecTrustResultInvalid;
if (SecTrustEvaluate(serverTrust, &secresult) != errSecSuccess)
{
// Reject the server auth if an error occurs
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
}
else
{
switch (secresult)
{
case kSecTrustResultUnspecified: // The OS trusts this certificate implicitly.
case kSecTrustResultProceed: // The user explicitly told the OS to trust it.
{
NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
break;
}
default:
{
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
break;
}
}
}
}
#pragma mark - WKUIDelegate
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(nonnull WKWebViewConfiguration *)configuration forNavigationAction:(nonnull WKNavigationAction *)navigationAction windowFeatures:(nonnull WKWindowFeatures *)windowFeatures
{
// Make sure we open links with `target="_blank"` within this webview
if (!navigationAction.targetFrame.isMainFrame)
{
[webView loadRequest:navigationAction.request];
}
return nil;
}
#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
if ([message.name isEqualToString:kMXKWebViewViewControllerPostMessageJSLog])
{
MXLogDebug(@"-- JavaScript: %@", message.body);
}
}
#pragma mark - BWI: WebViewLinkPolicy
-(void)webView:(WKWebView *)webview decidePolicyForNavigationAction:(nonnull WKNavigationAction *)navigationAction decisionHandler:(nonnull void (^)(WKNavigationActionPolicy))decisionHandler
{
if (navigationAction.navigationType == WKNavigationTypeLinkActivated) {
// bwi: clicked links should be opened in system browser
if (navigationAction.request.URL) {
[[UIApplication sharedApplication] vc_open:navigationAction.request.URL completionHandler:nil];
}
decisionHandler(WKNavigationActionPolicyCancel);
} else {
// bwi: Open url in webview
decisionHandler(WKNavigationActionPolicyAllow);
}
}
@end