| OLD | NEW |
| 1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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/test/web_test.h" | 5 #import "ios/web/public/test/web_test_with_web_state.h" |
| 6 | |
| 7 #include <utility> | |
| 8 | 6 |
| 9 #include "base/base64.h" | 7 #include "base/base64.h" |
| 10 #include "base/memory/ptr_util.h" | 8 #include "base/test/ios/wait_util.h" |
| 11 #include "base/strings/stringprintf.h" | |
| 12 #import "base/test/ios/wait_util.h" | |
| 13 #import "ios/testing/ocmock_complex_type_helper.h" | 9 #import "ios/testing/ocmock_complex_type_helper.h" |
| 14 #import "ios/web/navigation/crw_session_controller.h" | 10 #import "ios/web/navigation/crw_session_controller.h" |
| 15 #include "ios/web/public/active_state_manager.h" | |
| 16 #include "ios/web/public/referrer.h" | |
| 17 #include "ios/web/public/url_schemes.h" | |
| 18 #import "ios/web/public/web_state/ui/crw_web_delegate.h" | |
| 19 #import "ios/web/web_state/ui/crw_web_controller.h" | 11 #import "ios/web/web_state/ui/crw_web_controller.h" |
| 20 #import "ios/web/web_state/web_state_impl.h" | 12 #import "ios/web/web_state/web_state_impl.h" |
| 21 #include "third_party/ocmock/OCMock/OCMock.h" | |
| 22 | 13 |
| 23 // Helper Mock to stub out API with C++ objects in arguments. | 14 // Helper Mock to stub out API with C++ objects in arguments. |
| 24 @interface WebDelegateMock : OCMockComplexTypeHelper | 15 @interface WebDelegateMock : OCMockComplexTypeHelper |
| 25 @end | 16 @end |
| 26 | 17 |
| 27 @implementation WebDelegateMock | 18 @implementation WebDelegateMock |
| 28 // Stub implementation always returns YES. | 19 // Stub implementation always returns YES. |
| 29 - (BOOL)webController:(CRWWebController*)webController | 20 - (BOOL)webController:(CRWWebController*)webController |
| 30 shouldOpenURL:(const GURL&)url | 21 shouldOpenURL:(const GURL&)url |
| 31 mainDocumentURL:(const GURL&)mainDocumentURL | 22 mainDocumentURL:(const GURL&)mainDocumentURL |
| 32 linkClicked:(BOOL)linkClicked { | 23 linkClicked:(BOOL)linkClicked { |
| 33 return YES; | 24 return YES; |
| 34 } | 25 } |
| 35 @end | 26 @end |
| 36 | 27 |
| 37 namespace web { | 28 namespace web { |
| 38 | 29 |
| 39 #pragma mark - | 30 WebTestWithWebState::WebTestWithWebState() {} |
| 40 | 31 |
| 41 WebTest::WebTest() : web_client_(base::WrapUnique(new TestWebClient)) {} | 32 WebTestWithWebState::~WebTestWithWebState() {} |
| 42 | |
| 43 WebTest::~WebTest() {} | |
| 44 | |
| 45 void WebTest::SetUp() { | |
| 46 PlatformTest::SetUp(); | |
| 47 BrowserState::GetActiveStateManager(&browser_state_)->SetActive(true); | |
| 48 } | |
| 49 | |
| 50 void WebTest::TearDown() { | |
| 51 BrowserState::GetActiveStateManager(&browser_state_)->SetActive(false); | |
| 52 PlatformTest::TearDown(); | |
| 53 } | |
| 54 | |
| 55 TestWebClient* WebTest::GetWebClient() { | |
| 56 return static_cast<TestWebClient*>(web_client_.Get()); | |
| 57 } | |
| 58 | |
| 59 BrowserState* WebTest::GetBrowserState() { | |
| 60 return &browser_state_; | |
| 61 } | |
| 62 | |
| 63 #pragma mark - | |
| 64 | |
| 65 WebTestWithWebController::WebTestWithWebController() {} | |
| 66 | |
| 67 WebTestWithWebController::~WebTestWithWebController() {} | |
| 68 | 33 |
| 69 static int s_html_load_count; | 34 static int s_html_load_count; |
| 70 | 35 |
| 71 void WebTestWithWebController::SetUp() { | 36 void WebTestWithWebState::SetUp() { |
| 72 WebTest::SetUp(); | 37 WebTest::SetUp(); |
| 73 web_state_impl_.reset(new WebStateImpl(GetBrowserState())); | 38 std::unique_ptr<WebStateImpl> web_state(new WebStateImpl(GetBrowserState())); |
| 74 web_state_impl_->GetNavigationManagerImpl().InitializeSession(nil, nil, NO, | 39 web_state->GetNavigationManagerImpl().InitializeSession(nil, nil, NO, 0); |
| 75 0); | 40 web_state->SetWebUsageEnabled(true); |
| 76 web_state_impl_->SetWebUsageEnabled(true); | 41 webController_.reset(web_state->GetWebController()); |
| 77 webController_.reset(web_state_impl_->GetWebController()); | 42 web_state_.reset(web_state.release()); |
| 78 | 43 |
| 79 // Force generation of child views; necessary for some tests. | 44 // Force generation of child views; necessary for some tests. |
| 80 [webController_ triggerPendingLoad]; | 45 [webController_ triggerPendingLoad]; |
| 81 s_html_load_count = 0; | 46 s_html_load_count = 0; |
| 82 } | 47 } |
| 83 | 48 |
| 84 void WebTestWithWebController::TearDown() { | 49 void WebTestWithWebState::TearDown() { |
| 85 web_state_impl_.reset(); | 50 web_state_.reset(); |
| 86 WebTest::TearDown(); | 51 WebTest::TearDown(); |
| 87 } | 52 } |
| 88 | 53 |
| 89 void WebTestWithWebController::LoadHtml(NSString* html) { | 54 void WebTestWithWebState::WillProcessTask( |
| 55 const base::PendingTask& pending_task) { |
| 56 // Nothing to do. |
| 57 } |
| 58 |
| 59 void WebTestWithWebState::DidProcessTask( |
| 60 const base::PendingTask& pending_task) { |
| 61 processed_a_task_ = true; |
| 62 } |
| 63 |
| 64 void WebTestWithWebState::LoadHtml(NSString* html) { |
| 90 LoadHtml([html UTF8String]); | 65 LoadHtml([html UTF8String]); |
| 91 } | 66 } |
| 92 | 67 |
| 93 void WebTestWithWebController::LoadHtml(const std::string& html) { | 68 void WebTestWithWebState::LoadHtml(const std::string& html) { |
| 94 NSString* load_check = CreateLoadCheck(); | 69 NSString* load_check = CreateLoadCheck(); |
| 95 std::string marked_html = html + [load_check UTF8String]; | 70 std::string marked_html = html + [load_check UTF8String]; |
| 96 std::string encoded_html; | 71 std::string encoded_html; |
| 97 base::Base64Encode(marked_html, &encoded_html); | 72 base::Base64Encode(marked_html, &encoded_html); |
| 98 GURL url("data:text/html;charset=utf8;base64," + encoded_html); | 73 GURL url("data:text/html;charset=utf8;base64," + encoded_html); |
| 99 LoadURL(url); | 74 LoadURL(url); |
| 100 | 75 |
| 101 if (ResetPageIfNavigationStalled(load_check)) { | 76 if (ResetPageIfNavigationStalled(load_check)) { |
| 102 LoadHtml(html); | 77 LoadHtml(html); |
| 103 } | 78 } |
| 104 } | 79 } |
| 105 | 80 |
| 106 void WebTestWithWebController::LoadURL(const GURL& url) { | 81 void WebTestWithWebState::LoadURL(const GURL& url) { |
| 107 // First step is to ensure that the web controller has finished any previous | 82 // First step is to ensure that the web controller has finished any previous |
| 108 // page loads so the new load is not confused. | 83 // page loads so the new load is not confused. |
| 109 while ([webController_ loadPhase] != PAGE_LOADED) | 84 while ([webController_ loadPhase] != PAGE_LOADED) |
| 110 WaitForBackgroundTasks(); | 85 WaitForBackgroundTasks(); |
| 111 id originalMockDelegate = [OCMockObject | 86 id originalMockDelegate = |
| 112 niceMockForProtocol:@protocol(CRWWebDelegate)]; | 87 [OCMockObject niceMockForProtocol:@protocol(CRWWebDelegate)]; |
| 113 id mockDelegate = [[WebDelegateMock alloc] | 88 id mockDelegate = |
| 114 initWithRepresentedObject:originalMockDelegate]; | 89 [[WebDelegateMock alloc] initWithRepresentedObject:originalMockDelegate]; |
| 115 id existingDelegate = webController_.get().delegate; | 90 id existingDelegate = webController_.get().delegate; |
| 116 webController_.get().delegate = mockDelegate; | 91 webController_.get().delegate = mockDelegate; |
| 117 | 92 |
| 118 web::NavigationManagerImpl& navManager = | 93 web::NavigationManagerImpl& navManager = |
| 119 [webController_ webStateImpl]->GetNavigationManagerImpl(); | 94 [webController_ webStateImpl]->GetNavigationManagerImpl(); |
| 120 navManager.InitializeSession(@"name", nil, NO, 0); | 95 navManager.InitializeSession(@"name", nil, NO, 0); |
| 121 [navManager.GetSessionController() | 96 [navManager.GetSessionController() addPendingEntry:url |
| 122 addPendingEntry:url | 97 referrer:web::Referrer() |
| 123 referrer:web::Referrer() | 98 transition:ui::PAGE_TRANSITION_TYPED |
| 124 transition:ui::PAGE_TRANSITION_TYPED | 99 rendererInitiated:NO]; |
| 125 rendererInitiated:NO]; | |
| 126 | 100 |
| 127 [webController_ loadCurrentURL]; | 101 [webController_ loadCurrentURL]; |
| 128 while ([webController_ loadPhase] != PAGE_LOADED) | 102 while ([webController_ loadPhase] != PAGE_LOADED) |
| 129 WaitForBackgroundTasks(); | 103 WaitForBackgroundTasks(); |
| 130 webController_.get().delegate = existingDelegate; | 104 webController_.get().delegate = existingDelegate; |
| 131 [[webController_ view] layoutIfNeeded]; | 105 [[webController_ view] layoutIfNeeded]; |
| 132 } | 106 } |
| 133 | 107 |
| 134 void WebTestWithWebController::WaitForBackgroundTasks() { | 108 void WebTestWithWebState::WaitForBackgroundTasks() { |
| 135 // Because tasks can add new tasks to either queue, the loop continues until | 109 // Because tasks can add new tasks to either queue, the loop continues until |
| 136 // the first pass where no activity is seen from either queue. | 110 // the first pass where no activity is seen from either queue. |
| 137 bool activitySeen = false; | 111 bool activitySeen = false; |
| 138 base::MessageLoop* messageLoop = base::MessageLoop::current(); | 112 base::MessageLoop* messageLoop = base::MessageLoop::current(); |
| 139 messageLoop->AddTaskObserver(this); | 113 messageLoop->AddTaskObserver(this); |
| 140 do { | 114 do { |
| 141 activitySeen = false; | 115 activitySeen = false; |
| 142 | 116 |
| 143 // Yield to the iOS message queue, e.g. [NSObject performSelector:] events. | 117 // Yield to the iOS message queue, e.g. [NSObject performSelector:] events. |
| 144 if (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true) == | 118 if (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true) == |
| 145 kCFRunLoopRunHandledSource) | 119 kCFRunLoopRunHandledSource) |
| 146 activitySeen = true; | 120 activitySeen = true; |
| 147 | 121 |
| 148 // Yield to the Chromium message queue, e.g. WebThread::PostTask() | 122 // Yield to the Chromium message queue, e.g. WebThread::PostTask() |
| 149 // events. | 123 // events. |
| 150 processed_a_task_ = false; | 124 processed_a_task_ = false; |
| 151 messageLoop->RunUntilIdle(); | 125 messageLoop->RunUntilIdle(); |
| 152 if (processed_a_task_) // Set in TaskObserver method. | 126 if (processed_a_task_) // Set in TaskObserver method. |
| 153 activitySeen = true; | 127 activitySeen = true; |
| 154 | 128 |
| 155 } while (activitySeen); | 129 } while (activitySeen); |
| 156 messageLoop->RemoveTaskObserver(this); | 130 messageLoop->RemoveTaskObserver(this); |
| 157 } | 131 } |
| 158 | 132 |
| 159 void WebTestWithWebController::WaitForCondition(ConditionBlock condition) { | 133 void WebTestWithWebState::WaitForCondition(ConditionBlock condition) { |
| 160 base::MessageLoop* messageLoop = base::MessageLoop::current(); | 134 base::MessageLoop* messageLoop = base::MessageLoop::current(); |
| 161 DCHECK(messageLoop); | 135 DCHECK(messageLoop); |
| 162 base::test::ios::WaitUntilCondition(condition, messageLoop, | 136 base::test::ios::WaitUntilCondition(condition, messageLoop, |
| 163 base::TimeDelta::FromSeconds(10)); | 137 base::TimeDelta::FromSeconds(10)); |
| 164 } | 138 } |
| 165 | 139 |
| 166 NSString* WebTestWithWebController::EvaluateJavaScriptAsString( | 140 NSString* WebTestWithWebState::EvaluateJavaScriptAsString(NSString* script) { |
| 167 NSString* script) { | |
| 168 __block base::scoped_nsobject<NSString> evaluationResult; | 141 __block base::scoped_nsobject<NSString> evaluationResult; |
| 169 [webController_ evaluateJavaScript:script | 142 [webController_ evaluateJavaScript:script |
| 170 stringResultHandler:^(NSString* result, NSError*) { | 143 stringResultHandler:^(NSString* result, NSError*) { |
| 171 DCHECK([result isKindOfClass:[NSString class]]); | 144 DCHECK([result isKindOfClass:[NSString class]]); |
| 172 evaluationResult.reset([result copy]); | 145 evaluationResult.reset([result copy]); |
| 173 }]; | 146 }]; |
| 174 base::test::ios::WaitUntilCondition(^bool() { | 147 base::test::ios::WaitUntilCondition(^bool() { |
| 175 return evaluationResult; | 148 return evaluationResult; |
| 176 }); | 149 }); |
| 177 return [[evaluationResult retain] autorelease]; | 150 return [[evaluationResult retain] autorelease]; |
| 178 } | 151 } |
| 179 | 152 |
| 180 id WebTestWithWebController::ExecuteJavaScript(NSString* script) { | 153 id WebTestWithWebState::ExecuteJavaScript(NSString* script) { |
| 181 __block base::scoped_nsprotocol<id> executionResult; | 154 __block base::scoped_nsprotocol<id> executionResult; |
| 182 __block bool executionCompleted = false; | 155 __block bool executionCompleted = false; |
| 183 [webController_ executeJavaScript:script | 156 [webController_ executeJavaScript:script |
| 184 completionHandler:^(id result, NSError*) { | 157 completionHandler:^(id result, NSError*) { |
| 185 executionResult.reset([result copy]); | 158 executionResult.reset([result copy]); |
| 186 executionCompleted = true; | 159 executionCompleted = true; |
| 187 }]; | 160 }]; |
| 188 base::test::ios::WaitUntilCondition(^{ | 161 base::test::ios::WaitUntilCondition(^{ |
| 189 return executionCompleted; | 162 return executionCompleted; |
| 190 }); | 163 }); |
| 191 return [[executionResult retain] autorelease]; | 164 return [[executionResult retain] autorelease]; |
| 192 } | 165 } |
| 193 | 166 |
| 194 void WebTestWithWebController::WillProcessTask( | 167 web::WebState* WebTestWithWebState::web_state() { |
| 195 const base::PendingTask& pending_task) { | 168 return web_state_.get(); |
| 196 // Nothing to do. | |
| 197 } | 169 } |
| 198 | 170 |
| 199 void WebTestWithWebController::DidProcessTask( | 171 const web::WebState* WebTestWithWebState::web_state() const { |
| 200 const base::PendingTask& pending_task) { | 172 return web_state_.get(); |
| 201 processed_a_task_ = true; | |
| 202 } | 173 } |
| 203 | 174 |
| 204 web::WebState* WebTestWithWebController::web_state() { | 175 bool WebTestWithWebState::ResetPageIfNavigationStalled(NSString* load_check) { |
| 205 return web_state_impl_.get(); | |
| 206 } | |
| 207 | |
| 208 const web::WebState* WebTestWithWebController::web_state() const { | |
| 209 return web_state_impl_.get(); | |
| 210 } | |
| 211 | |
| 212 bool WebTestWithWebController::ResetPageIfNavigationStalled( | |
| 213 NSString* load_check) { | |
| 214 NSString* inner_html = EvaluateJavaScriptAsString( | 176 NSString* inner_html = EvaluateJavaScriptAsString( |
| 215 @"(document && document.body && document.body.innerHTML) || 'undefined'"); | 177 @"(document && document.body && document.body.innerHTML) || 'undefined'"); |
| 216 if ([inner_html rangeOfString:load_check].location == NSNotFound) { | 178 if ([inner_html rangeOfString:load_check].location == NSNotFound) { |
| 217 web_state_impl_->SetWebUsageEnabled(false); | 179 web_state_->SetWebUsageEnabled(false); |
| 218 web_state_impl_->SetWebUsageEnabled(true); | 180 web_state_->SetWebUsageEnabled(true); |
| 219 [webController_ triggerPendingLoad]; | 181 [webController_ triggerPendingLoad]; |
| 220 return true; | 182 return true; |
| 221 } | 183 } |
| 222 return false; | 184 return false; |
| 223 } | 185 } |
| 224 | 186 |
| 225 NSString* WebTestWithWebController::CreateLoadCheck() { | 187 NSString* WebTestWithWebState::CreateLoadCheck() { |
| 226 return [NSString stringWithFormat:@"<p style=\"display: none;\">%d</p>", | 188 return [NSString stringWithFormat:@"<p style=\"display: none;\">%d</p>", |
| 227 s_html_load_count++]; | 189 s_html_load_count++]; |
| 228 } | 190 } |
| 229 | 191 |
| 230 } // namespace web | 192 } // namespace web |
| OLD | NEW |