Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(230)

Side by Side Diff: ios/web/web_state/ui/crw_wk_web_view_web_controller.mm

Issue 1375023002: Adds support for POST request with bodies on WKWebView. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rest of the commetns Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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/objc_property_releaser.h"
12 #import "base/mac/scoped_nsobject.h" 13 #import "base/mac/scoped_nsobject.h"
13 #include "base/macros.h" 14 #include "base/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"
24 #include "ios/web/public/navigation_item.h" 25 #include "ios/web/public/navigation_item.h"
25 #include "ios/web/public/ssl_status.h" 26 #include "ios/web/public/ssl_status.h"
26 #include "ios/web/public/web_client.h" 27 #include "ios/web/public/web_client.h"
27 #import "ios/web/public/web_state/js/crw_js_injection_manager.h" 28 #import "ios/web/public/web_state/js/crw_js_injection_manager.h"
28 #import "ios/web/public/web_state/ui/crw_native_content_provider.h" 29 #import "ios/web/public/web_state/ui/crw_native_content_provider.h"
29 #import "ios/web/public/web_state/ui/crw_web_view_content_view.h" 30 #import "ios/web/public/web_state/ui/crw_web_view_content_view.h"
30 #import "ios/web/ui_web_view_util.h" 31 #import "ios/web/ui_web_view_util.h"
31 #include "ios/web/web_state/blocked_popup_info.h" 32 #include "ios/web/web_state/blocked_popup_info.h"
32 #import "ios/web/web_state/error_translation_util.h" 33 #import "ios/web/web_state/error_translation_util.h"
33 #include "ios/web/web_state/frame_info.h" 34 #include "ios/web/web_state/frame_info.h"
35 #import "ios/web/web_state/js/crw_js_post_request_loader.h"
34 #import "ios/web/web_state/js/crw_js_window_id_manager.h" 36 #import "ios/web/web_state/js/crw_js_window_id_manager.h"
35 #import "ios/web/web_state/js/page_script_util.h" 37 #import "ios/web/web_state/js/page_script_util.h"
36 #import "ios/web/web_state/ui/crw_web_controller+protected.h" 38 #import "ios/web/web_state/ui/crw_web_controller+protected.h"
37 #import "ios/web/web_state/ui/crw_wk_web_view_crash_detector.h" 39 #import "ios/web/web_state/ui/crw_wk_web_view_crash_detector.h"
38 #import "ios/web/web_state/ui/web_view_js_utils.h" 40 #import "ios/web/web_state/ui/web_view_js_utils.h"
39 #import "ios/web/web_state/ui/wk_back_forward_list_item_holder.h" 41 #import "ios/web/web_state/ui/wk_back_forward_list_item_holder.h"
40 #import "ios/web/web_state/ui/wk_web_view_configuration_provider.h" 42 #import "ios/web/web_state/ui/wk_web_view_configuration_provider.h"
41 #import "ios/web/web_state/web_state_impl.h" 43 #import "ios/web/web_state/web_state_impl.h"
42 #import "ios/web/web_state/web_view_internal_creation_util.h" 44 #import "ios/web/web_state/web_view_internal_creation_util.h"
43 #import "ios/web/web_state/wk_web_view_security_util.h" 45 #import "ios/web/web_state/wk_web_view_security_util.h"
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
78 DCHECK(error); 80 DCHECK(error);
79 return static_cast<WKWebViewErrorSource>( 81 return static_cast<WKWebViewErrorSource>(
80 [error.userInfo[kWKWebViewErrorSourceKey] integerValue]); 82 [error.userInfo[kWKWebViewErrorSourceKey] integerValue]);
81 } 83 }
82 84
83 } // namespace 85 } // namespace
84 86
85 @interface CRWWKWebViewWebController () <WKNavigationDelegate, 87 @interface CRWWKWebViewWebController () <WKNavigationDelegate,
86 WKScriptMessageHandler, 88 WKScriptMessageHandler,
87 WKUIDelegate> { 89 WKUIDelegate> {
90 base::mac::ObjCPropertyReleaser _propertyReleaser_CRWWKWebViewWebController;
Eugene But (OOO till 7-30) 2015/10/08 17:46:33 I would prefer to avoid mixing ObjCPropertyRelease
stkhapugin 2015/12/03 15:43:01 Done.
91
88 // The WKWebView managed by this instance. 92 // The WKWebView managed by this instance.
89 base::scoped_nsobject<WKWebView> _wkWebView; 93 base::scoped_nsobject<WKWebView> _wkWebView;
90 94
91 // The Watch Dog that detects and reports WKWebView crashes. 95 // The Watch Dog that detects and reports WKWebView crashes.
92 base::scoped_nsobject<CRWWKWebViewCrashDetector> _crashDetector; 96 base::scoped_nsobject<CRWWKWebViewCrashDetector> _crashDetector;
93 97
94 // The actual URL of the document object (i.e., the last committed URL). 98 // The actual URL of the document object (i.e., the last committed URL).
95 // TODO(stuartmorgan): Remove this in favor of just updating the session 99 // TODO(stuartmorgan): Remove this in favor of just updating the session
96 // controller and treating that as authoritative. For now, this allows sharing 100 // controller and treating that as authoritative. For now, this allows sharing
97 // the flow that's currently in the superclass. 101 // the flow that's currently in the superclass.
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
143 // Whether the pending navigation has been directly cancelled in 147 // Whether the pending navigation has been directly cancelled in
144 // |decidePolicyForNavigationAction| or |decidePolicyForNavigationResponse|. 148 // |decidePolicyForNavigationAction| or |decidePolicyForNavigationResponse|.
145 // Cancelled navigations should be simply discarded without handling any 149 // Cancelled navigations should be simply discarded without handling any
146 // specific error. 150 // specific error.
147 BOOL _pendingNavigationCancelled; 151 BOOL _pendingNavigationCancelled;
148 } 152 }
149 153
150 // Response's MIME type of the last known navigation. 154 // Response's MIME type of the last known navigation.
151 @property(nonatomic, copy) NSString* documentMIMEType; 155 @property(nonatomic, copy) NSString* documentMIMEType;
152 156
157 // Object for loading POST requests with body.
158 @property(nonatomic, retain) CRWJSPOSTRequestLoader* POSTRequestLoader;
159
153 // Dictionary where keys are the names of WKWebView properties and values are 160 // Dictionary where keys are the names of WKWebView properties and values are
154 // selector names which should be called when a corresponding property has 161 // selector names which should be called when a corresponding property has
155 // changed. e.g. @{ @"URL" : @"webViewURLDidChange" } means that 162 // changed. e.g. @{ @"URL" : @"webViewURLDidChange" } means that
156 // -[self webViewURLDidChange] must be called every time when WKWebView.URL is 163 // -[self webViewURLDidChange] must be called every time when WKWebView.URL is
157 // changed. 164 // changed.
158 @property(nonatomic, readonly) NSDictionary* wkWebViewObservers; 165 @property(nonatomic, readonly) NSDictionary* wkWebViewObservers;
159 166
160 // Returns the string to use as the request group ID in the user agent. Returns 167 // Returns the string to use as the request group ID in the user agent. Returns
161 // nil unless the network stack is enabled. 168 // nil unless the network stack is enabled.
162 @property(nonatomic, readonly) NSString* requestGroupIDForUserAgent; 169 @property(nonatomic, readonly) NSString* requestGroupIDForUserAgent;
163 170
164 // Activity indicator group ID for this web controller. 171 // Activity indicator group ID for this web controller.
165 @property(nonatomic, readonly) NSString* activityIndicatorGroupID; 172 @property(nonatomic, readonly) NSString* activityIndicatorGroupID;
166 173
167 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW) 174 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
168 // Identifier used for storing and retrieving certificates. 175 // Identifier used for storing and retrieving certificates.
169 @property(nonatomic, readonly) int certGroupID; 176 @property(nonatomic, readonly) int certGroupID;
170 #endif // #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW) 177 #endif // #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
171 178
179 // Loads POST request with body in |_wkWebView| by constructing an HTML page
180 // that executes the request through javascript and replaces document with the
Eugene But (OOO till 7-30) 2015/10/08 17:46:33 NIT: s/javascript/JavaScript
stkhapugin 2015/12/03 15:43:01 Done.
181 // result.
182 // Note that this approach includes multiple body encodings and decodings, plus
183 // the data is passed to |_wkWebView| on main thread, hence the performance is
184 // expected to be low.
185 // This is necessary because WKWebView ignores POST request body.
186 // Workaround for https://bugs.webkit.org/show_bug.cgi?id=145410
187 - (void)loadPOSTRequestWithBody:(NSMutableURLRequest*)request;
Eugene But (OOO till 7-30) 2015/10/08 17:46:33 Word before argument should describe argument. Thi
stkhapugin 2015/12/03 15:43:01 Done.
188
172 // Returns the WKWebViewConfigurationProvider associated with the web 189 // Returns the WKWebViewConfigurationProvider associated with the web
173 // controller's BrowserState. 190 // controller's BrowserState.
174 - (web::WKWebViewConfigurationProvider&)webViewConfigurationProvider; 191 - (web::WKWebViewConfigurationProvider&)webViewConfigurationProvider;
175 192
176 // Creates a web view with given |config|. No-op if web view is already created. 193 // Creates a web view with given |config|. No-op if web view is already created.
177 - (void)ensureWebViewCreatedWithConfiguration:(WKWebViewConfiguration*)config; 194 - (void)ensureWebViewCreatedWithConfiguration:(WKWebViewConfiguration*)config;
178 195
179 // Returns a new autoreleased web view created with given configuration. 196 // Returns a new autoreleased web view created with given configuration.
180 - (WKWebView*)createWebViewWithConfiguration:(WKWebViewConfiguration*)config; 197 - (WKWebView*)createWebViewWithConfiguration:(WKWebViewConfiguration*)config;
181 198
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
289 306
290 // Called when WKWebView title has been changed. 307 // Called when WKWebView title has been changed.
291 - (void)webViewTitleDidChange; 308 - (void)webViewTitleDidChange;
292 309
293 // Called when WKWebView URL has been changed. 310 // Called when WKWebView URL has been changed.
294 - (void)webViewURLDidChange; 311 - (void)webViewURLDidChange;
295 312
296 @end 313 @end
297 314
298 @implementation CRWWKWebViewWebController 315 @implementation CRWWKWebViewWebController
316 @synthesize POSTRequestLoader = _POSTRequestLoader;
299 317
300 #pragma mark CRWWebController public methods 318 #pragma mark CRWWebController public methods
301 319
302 - (instancetype)initWithWebState:(scoped_ptr<web::WebStateImpl>)webState { 320 - (instancetype)initWithWebState:(scoped_ptr<web::WebStateImpl>)webState {
303 DCHECK(webState); 321 DCHECK(webState);
304 web::BrowserState* browserState = webState->GetBrowserState(); 322 web::BrowserState* browserState = webState->GetBrowserState();
305 self = [super initWithWebState:webState.Pass()]; 323 self = [super initWithWebState:webState.Pass()];
306 if (self) { 324 if (self) {
325 _propertyReleaser_CRWWKWebViewWebController.Init(
326 self, [CRWWKWebViewWebController class]);
307 _certVerificationController.reset([[CRWCertVerificationController alloc] 327 _certVerificationController.reset([[CRWCertVerificationController alloc]
308 initWithBrowserState:browserState]); 328 initWithBrowserState:browserState]);
309 } 329 }
310 return self; 330 return self;
311 } 331 }
312 332
313 - (BOOL)keyboardDisplayRequiresUserAction { 333 - (BOOL)keyboardDisplayRequiresUserAction {
314 // TODO(stuartmorgan): Find out whether YES or NO is correct; see comment 334 // TODO(stuartmorgan): Find out whether YES or NO is correct; see comment
315 // in protected header. 335 // in protected header.
316 NOTIMPLEMENTED(); 336 NOTIMPLEMENTED();
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after
470 [_injectedScriptManagers addObject:JSInjectionManagerClass]; 490 [_injectedScriptManagers addObject:JSInjectionManagerClass];
471 } 491 }
472 492
473 - (void)willLoadCurrentURLInWebView { 493 - (void)willLoadCurrentURLInWebView {
474 // TODO(stuartmorgan): Get a WKWebView version of the request ID verification 494 // TODO(stuartmorgan): Get a WKWebView version of the request ID verification
475 // code working for debug builds. 495 // code working for debug builds.
476 } 496 }
477 497
478 - (void)loadRequestForCurrentNavigationItem { 498 - (void)loadRequestForCurrentNavigationItem {
479 DCHECK(self.webView && !self.nativeController); 499 DCHECK(self.webView && !self.nativeController);
500 DCHECK([self currentSessionEntry]);
501
502 web::WKBackForwardListItemHolder* holder =
503 [self currentBackForwardListItemHolder];
504 BOOL isFormResubmission =
505 (holder->navigation_type() == WKNavigationTypeFormResubmitted ||
506 holder->navigation_type() == WKNavigationTypeFormSubmitted);
507 web::NavigationItemImpl* currentItem =
508 [self currentSessionEntry].navigationItemImpl;
509 NSData* POSTData = currentItem->GetPostData();
510 NSMutableURLRequest* request = [self requestForCurrentNavigationItem];
480 511
481 ProceduralBlock defaultNavigationBlock = ^{ 512 ProceduralBlock defaultNavigationBlock = ^{
482 [self registerLoadRequest:[self currentNavigationURL] 513 [self registerLoadRequest:[self currentNavigationURL]
483 referrer:[self currentSessionEntryReferrer] 514 referrer:[self currentSessionEntryReferrer]
484 transition:[self currentTransition]]; 515 transition:[self currentTransition]];
485 [self loadRequest:[self requestForCurrentNavigationItem]]; 516 [self loadRequest:request];
486 }; 517 };
487 518
488 // If there is no corresponding WKBackForwardListItem, or the item is not in 519 ProceduralBlock POSTBlock = ^{
489 // the current WKWebView's back-forward list, navigating using WKWebView API 520 [request setHTTPMethod:@"POST"];
490 // is not possible. In this case, fall back to the default navigation 521 [request setHTTPBody:POSTData];
491 // mechanism. 522 [request setAllHTTPHeaderFields:[self currentHTTPHeaders]];
492 web::WKBackForwardListItemHolder* holder = 523 [self registerLoadRequest:[self currentNavigationURL]
493 [self currentBackForwardListItemHolder]; 524 referrer:[self currentSessionEntryReferrer]
494 if (!holder->back_forward_list_item() || 525 transition:[self currentTransition]];
495 ![self isBackForwardListItemValid:holder->back_forward_list_item()]) { 526 [self loadPOSTRequestWithBody:request];
496 defaultNavigationBlock(); 527 };
497 return;
498 }
499 528
500 ProceduralBlock webViewNavigationBlock = ^{ 529 ProceduralBlock webViewNavigationBlock = ^{
501 // If the current navigation URL is the same as the URL of the visible 530 // If the current navigation URL is the same as the URL of the visible
502 // page, that means the user requested a reload. |goToBackForwardListItem| 531 // page, that means the user requested a reload. |goToBackForwardListItem|
503 // will be a no-op when it is passed the current back forward list item, 532 // will be a no-op when it is passed the current back forward list item,
504 // so |reload| must be explicitly called. 533 // so |reload| must be explicitly called.
505 [self registerLoadRequest:[self currentNavigationURL] 534 [self registerLoadRequest:[self currentNavigationURL]
506 referrer:[self currentSessionEntryReferrer] 535 referrer:[self currentSessionEntryReferrer]
507 transition:[self currentTransition]]; 536 transition:[self currentTransition]];
508 if ([self currentNavigationURL] == net::GURLWithNSURL([_wkWebView URL])) { 537 if ([self currentNavigationURL] == net::GURLWithNSURL([_wkWebView URL])) {
509 [_wkWebView reload]; 538 [_wkWebView reload];
510 } else { 539 } else {
511 [_wkWebView goToBackForwardListItem:holder->back_forward_list_item()]; 540 [_wkWebView goToBackForwardListItem:holder->back_forward_list_item()];
512 } 541 }
513 }; 542 };
514 543
544 // If the request has POST data and is not a form resubmission, configure and
545 // run the POST request.
546 if (POSTData.length && !isFormResubmission) {
547 POSTBlock();
548 return;
549 }
550
551 // If there is no corresponding WKBackForwardListItem, or the item is not in
552 // the current WKWebView's back-forward list, navigating using WKWebView API
553 // is not possible. In this case, fall back to the default navigation
554 // mechanism.
555 if (!holder->back_forward_list_item() ||
556 ![self isBackForwardListItemValid:holder->back_forward_list_item()]) {
557 defaultNavigationBlock();
558 return;
559 }
560
515 // If the request is not a form submission or resubmission, or the user 561 // If the request is not a form submission or resubmission, or the user
516 // doesn't need to confirm the load, then continue right away. 562 // doesn't need to confirm the load, then continue right away.
517 web::NavigationItemImpl* currentItem = 563 if (!isFormResubmission ||
518 [self currentSessionEntry].navigationItemImpl;
519 if ((holder->navigation_type() != WKNavigationTypeFormResubmitted &&
520 holder->navigation_type() != WKNavigationTypeFormSubmitted) ||
521 currentItem->ShouldSkipResubmitDataConfirmation()) { 564 currentItem->ShouldSkipResubmitDataConfirmation()) {
522 webViewNavigationBlock(); 565 webViewNavigationBlock();
523 return; 566 return;
524 } 567 }
525 568
526 // If the request is form submission or resubmission, then prompt the 569 // If the request is form submission or resubmission, then prompt the
527 // user before proceeding. 570 // user before proceeding.
571 DCHECK(isFormResubmission);
528 [self.delegate webController:self 572 [self.delegate webController:self
529 onFormResubmissionForRequest:nil 573 onFormResubmissionForRequest:nil
530 continueBlock:webViewNavigationBlock 574 continueBlock:webViewNavigationBlock
531 cancelBlock:defaultNavigationBlock]; 575 cancelBlock:defaultNavigationBlock];
532 } 576 }
533 577
534 // Overrides the hashchange workaround in the super class that manually 578 // Overrides the hashchange workaround in the super class that manually
535 // triggers Javascript hashchange events. If navigating with native API, 579 // triggers Javascript hashchange events. If navigating with native API,
536 // i.e. using a back forward list item, hashchange events will be triggered 580 // i.e. using a back forward list item, hashchange events will be triggered
537 // automatically, so no URL tampering is required. 581 // automatically, so no URL tampering is required.
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after
628 } 672 }
629 673
630 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW) 674 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
631 - (int)certGroupID { 675 - (int)certGroupID {
632 DCHECK(self.webStateImpl); 676 DCHECK(self.webStateImpl);
633 // Request tracker IDs are used as certificate groups. 677 // Request tracker IDs are used as certificate groups.
634 return self.webStateImpl->GetRequestTracker()->identifier(); 678 return self.webStateImpl->GetRequestTracker()->identifier();
635 } 679 }
636 #endif 680 #endif
637 681
682 - (void)loadPOSTRequestWithBody:(NSMutableURLRequest*)request {
683 if (!self.POSTRequestLoader) {
684 _POSTRequestLoader = [[CRWJSPOSTRequestLoader alloc] init];
685 }
686
687 [self.POSTRequestLoader loadPOSTRequest:request inWebView:_wkWebView];
688 }
689
638 - (web::WKWebViewConfigurationProvider&)webViewConfigurationProvider { 690 - (web::WKWebViewConfigurationProvider&)webViewConfigurationProvider {
639 DCHECK(self.webStateImpl); 691 DCHECK(self.webStateImpl);
640 web::BrowserState* browserState = self.webStateImpl->GetBrowserState(); 692 web::BrowserState* browserState = self.webStateImpl->GetBrowserState();
641 return web::WKWebViewConfigurationProvider::FromBrowserState(browserState); 693 return web::WKWebViewConfigurationProvider::FromBrowserState(browserState);
642 } 694 }
643 695
644 - (void)ensureWebViewCreatedWithConfiguration:(WKWebViewConfiguration*)config { 696 - (void)ensureWebViewCreatedWithConfiguration:(WKWebViewConfiguration*)config {
645 if (!_wkWebView) { 697 if (!_wkWebView) {
646 // Use a separate userContentController for each web view. 698 // Use a separate userContentController for each web view.
647 // WKUserContentController does not allow adding multiple script message 699 // WKUserContentController does not allow adding multiple script message
(...skipping 838 matching lines...) Expand 10 before | Expand all | Expand 10 after
1486 placeholderText:defaultText 1538 placeholderText:defaultText
1487 requestURL: 1539 requestURL:
1488 net::GURLWithNSURL(frame.request.URL) 1540 net::GURLWithNSURL(frame.request.URL)
1489 completionHandler:completionHandler]; 1541 completionHandler:completionHandler];
1490 } else if (completionHandler) { 1542 } else if (completionHandler) {
1491 completionHandler(nil); 1543 completionHandler(nil);
1492 } 1544 }
1493 } 1545 }
1494 1546
1495 @end 1547 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698