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