OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import "ios/web/web_state/ui/crw_wk_web_view_web_controller.h" | 5 #import "ios/web/web_state/ui/crw_wk_web_view_web_controller.h" |
6 | 6 |
7 #import <WebKit/WebKit.h> | 7 #import <WebKit/WebKit.h> |
8 | 8 |
9 #include "base/ios/ios_util.h" | 9 #include "base/ios/ios_util.h" |
10 #include "base/ios/weak_nsobject.h" | 10 #include "base/ios/weak_nsobject.h" |
11 #include "base/json/json_reader.h" | 11 #include "base/json/json_reader.h" |
12 #import "base/mac/scoped_nsobject.h" | 12 #import "base/mac/scoped_nsobject.h" |
13 #include "base/macros.h" | 13 #include "base/macros.h" |
| 14 #include "base/metrics/histogram_macros.h" |
14 #include "base/strings/sys_string_conversions.h" | 15 #include "base/strings/sys_string_conversions.h" |
15 #include "base/values.h" | 16 #include "base/values.h" |
16 #import "ios/net/http_response_headers_util.h" | 17 #import "ios/net/http_response_headers_util.h" |
17 #import "ios/web/crw_network_activity_indicator_manager.h" | 18 #import "ios/web/crw_network_activity_indicator_manager.h" |
18 #import "ios/web/navigation/crw_session_controller.h" | 19 #import "ios/web/navigation/crw_session_controller.h" |
19 #import "ios/web/navigation/crw_session_entry.h" | 20 #import "ios/web/navigation/crw_session_entry.h" |
20 #include "ios/web/navigation/navigation_item_impl.h" | 21 #include "ios/web/navigation/navigation_item_impl.h" |
21 #include "ios/web/navigation/web_load_params.h" | 22 #include "ios/web/navigation/web_load_params.h" |
22 #import "ios/web/net/crw_cert_verification_controller.h" | 23 #import "ios/web/net/crw_cert_verification_controller.h" |
23 #include "ios/web/public/cert_store.h" | 24 #include "ios/web/public/cert_store.h" |
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
241 // Called when a load ends in an SSL error and certificate chain. | 242 // Called when a load ends in an SSL error and certificate chain. |
242 - (void)handleSSLCertError:(NSError*)error; | 243 - (void)handleSSLCertError:(NSError*)error; |
243 #endif | 244 #endif |
244 | 245 |
245 // Adds an activity indicator tasks for this web controller. | 246 // Adds an activity indicator tasks for this web controller. |
246 - (void)addActivityIndicatorTask; | 247 - (void)addActivityIndicatorTask; |
247 | 248 |
248 // Clears all activity indicator tasks for this web controller. | 249 // Clears all activity indicator tasks for this web controller. |
249 - (void)clearActivityIndicatorTasks; | 250 - (void)clearActivityIndicatorTasks; |
250 | 251 |
| 252 // Updates |security_style| and |cert_status| for the NavigationItem with ID |
| 253 // |navigationItemID|, if URL and certificate chain still match |host| and |
| 254 // |certChain|. |
| 255 - (void)updateSSLStatusForNavigationItemWithID:(int)navigationItemID |
| 256 certChain:(NSArray*)chain |
| 257 host:(NSString*)host |
| 258 withSecurityStyle:(web::SecurityStyle)style |
| 259 certStatus:(net::CertStatus)certStatus; |
| 260 |
| 261 // Asynchronously obtains SSL status from given |certChain| and |host| and |
| 262 // updates current navigation item. Before scheduling update changes SSLStatus' |
| 263 // cert_status and security_style to default. |
| 264 - (void)scheduleSSLStatusUpdateUsingCertChain:(NSArray*)chain |
| 265 host:(NSString*)host; |
| 266 |
251 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW) | 267 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW) |
252 // Updates SSL status for the current navigation item based on the information | 268 // Updates SSL status for the current navigation item based on the information |
253 // provided by web view. | 269 // provided by web view. |
254 - (void)updateSSLStatusForCurrentNavigationItem; | 270 - (void)updateSSLStatusForCurrentNavigationItem; |
255 #endif | 271 #endif |
256 | 272 |
257 // Registers load request with empty referrer and link or client redirect | 273 // Registers load request with empty referrer and link or client redirect |
258 // transition based on user interaction state. | 274 // transition based on user interaction state. |
259 - (void)registerLoadRequest:(const GURL&)url; | 275 - (void)registerLoadRequest:(const GURL&)url; |
260 | 276 |
(...skipping 599 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
860 - (void)addActivityIndicatorTask { | 876 - (void)addActivityIndicatorTask { |
861 [[CRWNetworkActivityIndicatorManager sharedInstance] | 877 [[CRWNetworkActivityIndicatorManager sharedInstance] |
862 startNetworkTaskForGroup:[self activityIndicatorGroupID]]; | 878 startNetworkTaskForGroup:[self activityIndicatorGroupID]]; |
863 } | 879 } |
864 | 880 |
865 - (void)clearActivityIndicatorTasks { | 881 - (void)clearActivityIndicatorTasks { |
866 [[CRWNetworkActivityIndicatorManager sharedInstance] | 882 [[CRWNetworkActivityIndicatorManager sharedInstance] |
867 clearNetworkTasksForGroup:[self activityIndicatorGroupID]]; | 883 clearNetworkTasksForGroup:[self activityIndicatorGroupID]]; |
868 } | 884 } |
869 | 885 |
| 886 - (void)updateSSLStatusForNavigationItemWithID:(int)navigationItemID |
| 887 certChain:(NSArray*)chain |
| 888 host:(NSString*)host |
| 889 withSecurityStyle:(web::SecurityStyle)style |
| 890 certStatus:(net::CertStatus)certStatus { |
| 891 web::NavigationManager* navigationManager = |
| 892 self.webStateImpl->GetNavigationManager(); |
| 893 |
| 894 // The searched item almost always be the last one, so walk backward rather |
| 895 // than forward. |
| 896 for (int i = navigationManager->GetEntryCount() - 1; 0 <= i; i--) { |
| 897 web::NavigationItem* item = navigationManager->GetItemAtIndex(i); |
| 898 if (item->GetUniqueID() != navigationItemID) |
| 899 continue; |
| 900 |
| 901 // NavigationItem's UniqueID is preserved even after redirects, so |
| 902 // checking that cert and URL match is necessary. |
| 903 scoped_refptr<net::X509Certificate> cert(web::CreateCertFromChain(chain)); |
| 904 int certID = |
| 905 web::CertStore::GetInstance()->StoreCert(cert.get(), self.certGroupID); |
| 906 std::string GURLHost = base::SysNSStringToUTF8(host); |
| 907 web::SSLStatus& SSLStatus = item->GetSSL(); |
| 908 if (SSLStatus.cert_id == certID && item->GetURL().host() == GURLHost) { |
| 909 web::SSLStatus previousSSLStatus = item->GetSSL(); |
| 910 SSLStatus.cert_status = certStatus; |
| 911 SSLStatus.security_style = style; |
| 912 if (navigationManager->GetCurrentEntryIndex() == i && |
| 913 !previousSSLStatus.Equals(SSLStatus)) { |
| 914 [self didUpdateSSLStatusForCurrentNavigationItem]; |
| 915 } |
| 916 } |
| 917 return; |
| 918 } |
| 919 } |
| 920 |
| 921 - (void)scheduleSSLStatusUpdateUsingCertChain:(NSArray*)chain |
| 922 host:(NSString*)host { |
| 923 // Use Navigation Item's unique ID to locate requested item after |
| 924 // obtaining cert status asynchronously. |
| 925 int itemID = self.webStateImpl->GetNavigationManager() |
| 926 ->GetLastCommittedItem() |
| 927 ->GetUniqueID(); |
| 928 |
| 929 base::WeakNSObject<CRWWKWebViewWebController> weakSelf(self); |
| 930 void (^SSLStatusResponse)(web::SecurityStyle, net::CertStatus) = |
| 931 ^(web::SecurityStyle style, net::CertStatus certStatus) { |
| 932 base::scoped_nsobject<CRWWKWebViewWebController> strongSelf( |
| 933 [weakSelf retain]); |
| 934 if (!strongSelf || [strongSelf isBeingDestroyed]) { |
| 935 return; |
| 936 } |
| 937 [strongSelf updateSSLStatusForNavigationItemWithID:itemID |
| 938 certChain:chain |
| 939 host:host |
| 940 withSecurityStyle:style |
| 941 certStatus:certStatus]; |
| 942 }; |
| 943 |
| 944 [_certVerificationController |
| 945 querySSLStatusForTrust:web::CreateServerTrustFromChain(chain, host) |
| 946 host:host |
| 947 completionHandler:SSLStatusResponse]; |
| 948 } |
| 949 |
870 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW) | 950 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW) |
| 951 |
871 - (void)updateSSLStatusForCurrentNavigationItem { | 952 - (void)updateSSLStatusForCurrentNavigationItem { |
872 if ([self isBeingDestroyed]) | 953 if ([self isBeingDestroyed]) |
873 return; | 954 return; |
874 | 955 |
875 DCHECK(self.webStateImpl); | |
876 web::NavigationItem* item = | 956 web::NavigationItem* item = |
877 self.webStateImpl->GetNavigationManagerImpl().GetLastCommittedItem(); | 957 self.webStateImpl->GetNavigationManagerImpl().GetLastCommittedItem(); |
878 if (!item) | 958 if (!item) |
879 return; | 959 return; |
880 | 960 |
881 web::SSLStatus previousSSLStatus = item->GetSSL(); | 961 web::SSLStatus previousSSLStatus = item->GetSSL(); |
882 web::SSLStatus& SSLStatus = item->GetSSL(); | 962 |
| 963 // Starting from iOS9 WKWebView blocks active mixed content, so if |
| 964 // |hasOnlySecureContent| returns NO it means passive content. |
| 965 item->GetSSL().content_status = |
| 966 [_wkWebView hasOnlySecureContent] |
| 967 ? web::SSLStatus::NORMAL_CONTENT |
| 968 : web::SSLStatus::DISPLAYED_INSECURE_CONTENT; |
| 969 |
| 970 // Try updating SSLStatus for current NavigationItem asynchronously. |
| 971 scoped_refptr<net::X509Certificate> cert; |
883 if (item->GetURL().SchemeIsCryptographic()) { | 972 if (item->GetURL().SchemeIsCryptographic()) { |
884 // TODO(eugenebut): Do not set security style to authenticated once | 973 NSArray* chain = [_wkWebView certificateChain]; |
885 // proceeding with bad ssl cert is implemented. | 974 cert = web::CreateCertFromChain(chain); |
886 SSLStatus.security_style = web::SECURITY_STYLE_AUTHENTICATED; | 975 if (cert) { |
887 SSLStatus.content_status = [_wkWebView hasOnlySecureContent] | 976 int oldCertID = item->GetSSL().cert_id; |
888 ? web::SSLStatus::NORMAL_CONTENT | 977 std::string oldHost = item->GetSSL().cert_status_host; |
889 : web::SSLStatus::DISPLAYED_INSECURE_CONTENT; | 978 item->GetSSL().cert_id = web::CertStore::GetInstance()->StoreCert( |
| 979 cert.get(), self.certGroupID); |
| 980 item->GetSSL().cert_status_host = _documentURL.host(); |
| 981 // Only recompute the SSLStatus information if the certificate or host has |
| 982 // since changed. Host can be changed in case of redirect. |
| 983 if (oldCertID != item->GetSSL().cert_id || |
| 984 oldHost != item->GetSSL().cert_status_host) { |
| 985 // Real SSL status is unknown, reset cert status and security style. |
| 986 // They will be asynchronously updated in |
| 987 // |scheduleSSLStatusUpdateUsingCertChain|. |
| 988 item->GetSSL().cert_status = net::CertStatus(); |
| 989 item->GetSSL().security_style = web::SECURITY_STYLE_UNKNOWN; |
890 | 990 |
891 if (base::ios::IsRunningOnIOS9OrLater()) { | 991 NSString* host = base::SysUTF8ToNSString(_documentURL.host()); |
892 scoped_refptr<net::X509Certificate> cert(web::CreateCertFromChain( | 992 [self scheduleSSLStatusUpdateUsingCertChain:chain host:host]; |
893 [_wkWebView performSelector:@selector(certificateChain)])); | |
894 if (cert) { | |
895 SSLStatus.cert_id = web::CertStore::GetInstance()->StoreCert( | |
896 cert.get(), self.certGroupID); | |
897 } else { | |
898 SSLStatus.security_style = web::SECURITY_STYLE_UNAUTHENTICATED; | |
899 SSLStatus.cert_id = 0; | |
900 } | 993 } |
901 } | 994 } |
902 } else { | |
903 SSLStatus.security_style = web::SECURITY_STYLE_UNAUTHENTICATED; | |
904 SSLStatus.cert_id = 0; | |
905 } | 995 } |
906 | 996 |
907 if (!previousSSLStatus.Equals(SSLStatus)) { | 997 if (!cert) { |
| 998 item->GetSSL().cert_id = 0; |
| 999 if (!item->GetURL().SchemeIsCryptographic()) { |
| 1000 // HTTP or other non-secure connection. |
| 1001 item->GetSSL().security_style = web::SECURITY_STYLE_UNAUTHENTICATED; |
| 1002 } else { |
| 1003 // HTTPS, no certificate (this use-case has not been observed). |
| 1004 item->GetSSL().security_style = web::SECURITY_STYLE_UNKNOWN; |
| 1005 } |
| 1006 } |
| 1007 |
| 1008 if (!previousSSLStatus.Equals(item->GetSSL())) { |
908 [self didUpdateSSLStatusForCurrentNavigationItem]; | 1009 [self didUpdateSSLStatusForCurrentNavigationItem]; |
909 } | 1010 } |
910 } | 1011 } |
| 1012 |
911 #endif // !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW) | 1013 #endif // !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW) |
912 | 1014 |
913 - (void)registerLoadRequest:(const GURL&)url { | 1015 - (void)registerLoadRequest:(const GURL&)url { |
914 // If load request is registered via WKWebViewWebController, assume transition | 1016 // If load request is registered via WKWebViewWebController, assume transition |
915 // is link or client redirect as other transitions will already be registered | 1017 // is link or client redirect as other transitions will already be registered |
916 // by web controller or delegates. | 1018 // by web controller or delegates. |
917 // TODO(stuartmorgan): Remove guesswork and replace with information from | 1019 // TODO(stuartmorgan): Remove guesswork and replace with information from |
918 // decidePolicyForNavigationAction:. | 1020 // decidePolicyForNavigationAction:. |
919 ui::PageTransition transition = self.userInteractionRegistered | 1021 ui::PageTransition transition = self.userInteractionRegistered |
920 ? ui::PAGE_TRANSITION_LINK | 1022 ? ui::PAGE_TRANSITION_LINK |
921 : ui::PAGE_TRANSITION_CLIENT_REDIRECT; | 1023 : ui::PAGE_TRANSITION_CLIENT_REDIRECT; |
922 // The referrer is not known yet, and will be updated later. | 1024 // The referrer is not known yet, and will be updated later. |
923 const web::Referrer emptyReferrer; | 1025 const web::Referrer emptyReferrer; |
924 [self registerLoadRequest:url referrer:emptyReferrer transition:transition]; | 1026 [self registerLoadRequest:url referrer:emptyReferrer transition:transition]; |
925 } | 1027 } |
926 | 1028 |
927 - (void)URLDidChangeWithoutDocumentChange:(const GURL&)newURL { | 1029 - (void)URLDidChangeWithoutDocumentChange:(const GURL&)newURL { |
928 DCHECK(newURL == net::GURLWithNSURL([_wkWebView URL])); | 1030 DCHECK(newURL == net::GURLWithNSURL([_wkWebView URL])); |
| 1031 DCHECK_EQ(_documentURL.host(), newURL.host()); |
929 _documentURL = newURL; | 1032 _documentURL = newURL; |
930 // If called during window.history.pushState or window.history.replaceState | 1033 // If called during window.history.pushState or window.history.replaceState |
931 // JavaScript evaluation, only update the document URL. This callback does not | 1034 // JavaScript evaluation, only update the document URL. This callback does not |
932 // have any information about the state object and cannot create (or edit) the | 1035 // have any information about the state object and cannot create (or edit) the |
933 // navigation entry for this page change. Web controller will sync with | 1036 // navigation entry for this page change. Web controller will sync with |
934 // history changes when a window.history.didPushState or | 1037 // history changes when a window.history.didPushState or |
935 // window.history.didReplaceState message is received, which should happen in | 1038 // window.history.didReplaceState message is received, which should happen in |
936 // the next runloop. | 1039 // the next runloop. |
937 if (!_changingHistoryState) { | 1040 if (!_changingHistoryState) { |
938 [self registerLoadRequest:_documentURL]; | 1041 [self registerLoadRequest:_documentURL]; |
(...skipping 401 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1340 // This is the point where the document's URL has actually changed. | 1443 // This is the point where the document's URL has actually changed. |
1341 _documentURL = net::GURLWithNSURL([_wkWebView URL]); | 1444 _documentURL = net::GURLWithNSURL([_wkWebView URL]); |
1342 DCHECK(_documentURL == self.lastRegisteredRequestURL); | 1445 DCHECK(_documentURL == self.lastRegisteredRequestURL); |
1343 [self webPageChanged]; | 1446 [self webPageChanged]; |
1344 | 1447 |
1345 [self updateCurrentBackForwardListItemHolder]; | 1448 [self updateCurrentBackForwardListItemHolder]; |
1346 | 1449 |
1347 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW) | 1450 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW) |
1348 [self updateSSLStatusForCurrentNavigationItem]; | 1451 [self updateSSLStatusForCurrentNavigationItem]; |
1349 #endif | 1452 #endif |
| 1453 |
| 1454 // Report cases where SSL cert is missing for a secure connection. |
| 1455 if (_documentURL.SchemeIsCryptographic()) { |
| 1456 scoped_refptr<net::X509Certificate> cert = |
| 1457 web::CreateCertFromChain([_wkWebView certificateChain]); |
| 1458 UMA_HISTOGRAM_BOOLEAN("WebController.WKWebViewHasCertForSecureConnection", |
| 1459 cert); |
| 1460 } |
1350 } | 1461 } |
1351 | 1462 |
1352 - (void)webView:(WKWebView *)webView | 1463 - (void)webView:(WKWebView *)webView |
1353 didFinishNavigation:(WKNavigation *)navigation { | 1464 didFinishNavigation:(WKNavigation *)navigation { |
1354 DCHECK(!self.isHalted); | 1465 DCHECK(!self.isHalted); |
1355 // Trigger JavaScript driven post-document-load-completion tasks. | 1466 // Trigger JavaScript driven post-document-load-completion tasks. |
1356 // TODO(jyquinn): Investigate using WKUserScriptInjectionTimeAtDocumentEnd to | 1467 // TODO(jyquinn): Investigate using WKUserScriptInjectionTimeAtDocumentEnd to |
1357 // inject this material at the appropriate time rather than invoking here. | 1468 // inject this material at the appropriate time rather than invoking here. |
1358 web::EvaluateJavaScript(webView, | 1469 web::EvaluateJavaScript(webView, |
1359 @"__gCrWeb.didFinishNavigation()", nil); | 1470 @"__gCrWeb.didFinishNavigation()", nil); |
(...skipping 13 matching lines...) Expand all Loading... |
1373 (void (^)(NSURLSessionAuthChallengeDisposition disposition, | 1484 (void (^)(NSURLSessionAuthChallengeDisposition disposition, |
1374 NSURLCredential *credential))completionHandler { | 1485 NSURLCredential *credential))completionHandler { |
1375 if (![challenge.protectionSpace.authenticationMethod | 1486 if (![challenge.protectionSpace.authenticationMethod |
1376 isEqual:NSURLAuthenticationMethodServerTrust]) { | 1487 isEqual:NSURLAuthenticationMethodServerTrust]) { |
1377 completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); | 1488 completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); |
1378 return; | 1489 return; |
1379 } | 1490 } |
1380 | 1491 |
1381 SecTrustRef trust = challenge.protectionSpace.serverTrust; | 1492 SecTrustRef trust = challenge.protectionSpace.serverTrust; |
1382 scoped_refptr<net::X509Certificate> cert = web::CreateCertFromTrust(trust); | 1493 scoped_refptr<net::X509Certificate> cert = web::CreateCertFromTrust(trust); |
| 1494 // TODO(eugenebut): pass SecTrustRef instead of cert. |
1383 [_certVerificationController | 1495 [_certVerificationController |
1384 decidePolicyForCert:cert | 1496 decidePolicyForCert:cert |
1385 host:challenge.protectionSpace.host | 1497 host:challenge.protectionSpace.host |
1386 completionHandler:^(web::CertAcceptPolicy policy, | 1498 completionHandler:^(web::CertAcceptPolicy policy, |
1387 net::CertStatus status) { | 1499 net::CertStatus status) { |
1388 completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, | 1500 completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, |
1389 nil); | 1501 nil); |
1390 }]; | 1502 }]; |
1391 } | 1503 } |
1392 | 1504 |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1479 placeholderText:defaultText | 1591 placeholderText:defaultText |
1480 requestURL: | 1592 requestURL: |
1481 net::GURLWithNSURL(frame.request.URL) | 1593 net::GURLWithNSURL(frame.request.URL) |
1482 completionHandler:completionHandler]; | 1594 completionHandler:completionHandler]; |
1483 } else if (completionHandler) { | 1595 } else if (completionHandler) { |
1484 completionHandler(nil); | 1596 completionHandler(nil); |
1485 } | 1597 } |
1486 } | 1598 } |
1487 | 1599 |
1488 @end | 1600 @end |
OLD | NEW |