1 // Copyright (c) 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 #include "chrome/browser/printing/print_dialog_cloud.h" 6 #include "chrome/browser/printing/print_dialog_cloud_internal.h" 7 8 #include <functional> 9 10 #include "base/bind.h" 11 #include "base/file_util.h" 12 #include "base/files/file_path.h" 13 #include "base/memory/singleton.h" 14 #include "base/path_service.h" 15 #include "base/strings/utf_string_conversions.h" 16 #include "base/threading/thread_restrictions.h" 17 #include "base/values.h" 18 #include "chrome/browser/printing/cloud_print/cloud_print_url.h" 19 #include "chrome/browser/profiles/profile.h" 20 #include "chrome/browser/ui/browser.h" 21 #include "chrome/browser/ui/browser_window.h" 22 #include "chrome/common/chrome_paths.h" 23 #include "chrome/common/url_constants.h" 24 #include "chrome/test/base/in_process_browser_test.h" 25 #include "chrome/test/base/ui_test_utils.h" 26 #include "content/public/browser/notification_service.h" 27 #include "content/public/browser/notification_types.h" 28 #include "content/public/browser/render_view_host.h" 29 #include "content/public/browser/web_contents.h" 30 #include "content/public/test/test_browser_thread.h" 31 #include "net/url_request/url_request.h" 32 #include "net/url_request/url_request_filter.h" 33 #include "net/url_request/url_request_test_job.h" 34 #include "net/url_request/url_request_test_util.h" 35 #include "ui/base/test/ui_controls.h" 36 37 using content::BrowserThread; 38 39 namespace { 40 41 class TestData { 42 public: 43 static TestData* GetInstance() { 44 return Singleton<TestData>::get(); 45 } 46 47 const char* GetTestData() { 48 // Fetching this data blocks the IO thread, but we don't really care because 49 // this is a test. 50 base::ThreadRestrictions::ScopedAllowIO allow_io; 51 52 if (test_data_.empty()) { 53 base::FilePath test_data_directory; 54 PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory); 55 base::FilePath test_file = 56 test_data_directory.AppendASCII("printing/cloud_print_uitest.html"); 57 file_util::ReadFileToString(test_file, &test_data_); 58 } 59 return test_data_.c_str(); 60 } 61 private: 62 TestData() {} 63 64 std::string test_data_; 65 66 friend struct DefaultSingletonTraits<TestData>; 67 }; 68 69 // A simple test net::URLRequestJob. We don't care what it does, only that 70 // whether it starts and finishes. 71 class SimpleTestJob : public net::URLRequestTestJob { 72 public: 73 SimpleTestJob(net::URLRequest* request, 74 net::NetworkDelegate* network_delegate) 75 : net::URLRequestTestJob(request, 76 network_delegate, 77 test_headers(), 78 TestData::GetInstance()->GetTestData(), 79 true) {} 80 81 virtual void GetResponseInfo(net::HttpResponseInfo* info) OVERRIDE { 82 net::URLRequestTestJob::GetResponseInfo(info); 83 if (request_->url().SchemeIsSecure()) { 84 // Make up a fake certificate for this response since we don't have 85 // access to the real SSL info. 86 const char* kCertIssuer = "Chrome Internal"; 87 const int kLifetimeDays = 100; 88 89 info->ssl_info.cert = 90 new net::X509Certificate(request_->url().GetWithEmptyPath().spec(), 91 kCertIssuer, 92 base::Time::Now(), 93 base::Time::Now() + 94 base::TimeDelta::FromDays(kLifetimeDays)); 95 info->ssl_info.cert_status = 0; 96 info->ssl_info.security_bits = -1; 97 } 98 } 99 100 private: 101 virtual ~SimpleTestJob() {} 102 }; 103 104 class TestController { 105 public: 106 static TestController* GetInstance() { 107 return Singleton<TestController>::get(); 108 } 109 void set_result(bool value) { 110 result_ = value; 111 } 112 bool result() { 113 return result_; 114 } 115 void set_expected_url(const GURL& url) { 116 expected_url_ = url; 117 } 118 const GURL expected_url() { 119 return expected_url_; 120 } 121 void set_delegate(net::TestDelegate* delegate) { 122 delegate_ = delegate; 123 } 124 net::TestDelegate* delegate() { 125 return delegate_; 126 } 127 void set_use_delegate(bool value) { 128 use_delegate_ = value; 129 } 130 bool use_delegate() { 131 return use_delegate_; 132 } 133 private: 134 TestController() 135 : result_(false), 136 use_delegate_(false), 137 delegate_(NULL) {} 138 139 bool result_; 140 bool use_delegate_; 141 GURL expected_url_; 142 net::TestDelegate* delegate_; 143 144 friend struct DefaultSingletonTraits<TestController>; 145 }; 146 147 } // namespace 148 149 class PrintDialogCloudTest : public InProcessBrowserTest { 150 public: 151 PrintDialogCloudTest() : handler_added_(false) { 152 PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory_); 153 } 154 155 // Must be static for handing into AddHostnameHandler. 156 static net::URLRequest::ProtocolFactory Factory; 157 158 class AutoQuitDelegate : public net::TestDelegate { 159 public: 160 AutoQuitDelegate() {} 161 162 virtual void OnResponseCompleted(net::URLRequest* request) OVERRIDE { 163 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 164 base::MessageLoop::QuitClosure()); 165 } 166 }; 167 168 virtual void SetUp() OVERRIDE { 169 TestController::GetInstance()->set_result(false); 170 InProcessBrowserTest::SetUp(); 171 } 172 173 virtual void TearDown() OVERRIDE { 174 if (handler_added_) { 175 BrowserThread::PostTask( 176 BrowserThread::IO, FROM_HERE, 177 base::Bind(UnregisterTestHandlers, scheme_, host_name_)); 178 handler_added_ = false; 179 TestController::GetInstance()->set_delegate(NULL); 180 } 181 InProcessBrowserTest::TearDown(); 182 } 183 184 // Normally this is something I would expect could go into SetUp(), 185 // but there seems to be some timing or ordering related issue with 186 // the test harness that made that flaky. Calling this from the 187 // individual test functions seems to fix that. 188 void AddTestHandlers() { 189 if (!handler_added_) { 190 GURL cloud_print_service_url = 191 CloudPrintURL(browser()->profile()). 192 GetCloudPrintServiceURL(); 193 scheme_ = cloud_print_service_url.scheme(); 194 host_name_ = cloud_print_service_url.host(); 195 BrowserThread::PostTask( 196 BrowserThread::IO, FROM_HERE, 197 base::Bind(RegisterTestHandlers, scheme_, host_name_)); 198 handler_added_ = true; 199 200 GURL cloud_print_dialog_url = 201 CloudPrintURL(browser()->profile()). 202 GetCloudPrintServiceDialogURL(); 203 TestController::GetInstance()->set_expected_url(cloud_print_dialog_url); 204 TestController::GetInstance()->set_delegate(&delegate_); 205 } 206 207 CreateDialogForTest(); 208 } 209 210 void CreateDialogForTest() { 211 base::FilePath path_to_pdf = 212 test_data_directory_.AppendASCII("printing/cloud_print_uitest.pdf"); 213 BrowserThread::PostTask( 214 BrowserThread::UI, FROM_HERE, 215 base::Bind(&print_dialog_cloud::CreatePrintDialogForFile, 216 browser()->profile(), browser()->window()->GetNativeWindow(), 217 path_to_pdf, string16(), string16(), 218 std::string("application/pdf"), false)); 219 } 220 221 private: 222 static void RegisterTestHandlers(const std::string& scheme, 223 const std::string& host_name) { 224 net::URLRequestFilter::GetInstance()->AddHostnameHandler( 225 scheme, host_name, &PrintDialogCloudTest::Factory); 226 } 227 static void UnregisterTestHandlers(const std::string& scheme, 228 const std::string& host_name) { 229 net::URLRequestFilter::GetInstance()->RemoveHostnameHandler(scheme, 230 host_name); 231 } 232 233 bool handler_added_; 234 std::string scheme_; 235 std::string host_name_; 236 base::FilePath test_data_directory_; 237 AutoQuitDelegate delegate_; 238 }; 239 240 net::URLRequestJob* PrintDialogCloudTest::Factory( 241 net::URLRequest* request, 242 net::NetworkDelegate* network_delegate, 243 const std::string& scheme) { 244 if (request && 245 (request->url() == TestController::GetInstance()->expected_url())) { 246 if (TestController::GetInstance()->use_delegate()) 247 request->set_delegate(TestController::GetInstance()->delegate()); 248 TestController::GetInstance()->set_result(true); 249 return new SimpleTestJob(request, network_delegate); 250 } 251 return new net::URLRequestTestJob(request, 252 network_delegate, 253 net::URLRequestTestJob::test_headers(), 254 std::string(), 255 true); 256 } 257 258 IN_PROC_BROWSER_TEST_F(PrintDialogCloudTest, HandlersRegistered) { 259 AddTestHandlers(); 260 261 TestController::GetInstance()->set_use_delegate(true); 262 263 content::RunMessageLoop(); 264 265 ASSERT_TRUE(TestController::GetInstance()->result()); 266 267 // Close the dialog before finishing the test. 268 content::WindowedNotificationObserver tab_closed_observer( 269 content::NOTIFICATION_WEB_CONTENTS_DESTROYED, 270 content::NotificationService::AllSources()); 271 272 // Can't use ui_test_utils::SendKeyPressSync or 273 // ui_test_utils::SendKeyPressAndWait due to a race condition with closing 274 // the window. See http://crbug.com/111269 275 BrowserWindow* window = browser()->window(); 276 ASSERT_TRUE(window); 277 gfx::NativeWindow native_window = window->GetNativeWindow(); 278 ASSERT_TRUE(native_window); 279 bool key_sent = ui_controls::SendKeyPress(native_window, ui::VKEY_ESCAPE, 280 false, false, false, false); 281 EXPECT_TRUE(key_sent); 282 if (key_sent) 283 tab_closed_observer.Wait(); 284 } 285