1// Copyright (c) 2011 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/file_path.h"
11#include "base/file_util.h"
12#include "base/memory/singleton.h"
13#include "base/path_service.h"
14#include "base/threading/thread_restrictions.h"
15#include "base/utf_string_conversions.h"
16#include "base/values.h"
17#include "chrome/browser/printing/cloud_print/cloud_print_url.h"
18#include "chrome/browser/ui/browser_list.h"
19#include "chrome/browser/ui/webui/chrome_url_data_manager.h"
20#include "chrome/common/chrome_paths.h"
21#include "chrome/common/url_constants.h"
22#include "chrome/test/in_process_browser_test.h"
23#include "chrome/test/ui_test_utils.h"
24#include "content/browser/browser_thread.h"
25#include "content/browser/renderer_host/render_view_host.h"
26#include "content/browser/tab_contents/tab_contents.h"
27#include "net/url_request/url_request_filter.h"
28#include "net/url_request/url_request_test_job.h"
29#include "net/url_request/url_request_test_util.h"
30
31namespace {
32
33class TestData {
34 public:
35  static TestData* GetInstance() {
36    return Singleton<TestData>::get();
37  }
38
39  const char* GetTestData() {
40    // Fetching this data blocks the IO thread, but we don't really care because
41    // this is a test.
42    base::ThreadRestrictions::ScopedAllowIO allow_io;
43
44    if (test_data_.empty()) {
45      FilePath test_data_directory;
46      PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory);
47      FilePath test_file =
48          test_data_directory.AppendASCII("printing/cloud_print_uitest.html");
49      file_util::ReadFileToString(test_file, &test_data_);
50    }
51    return test_data_.c_str();
52  }
53 private:
54  TestData() {}
55
56  std::string test_data_;
57
58  friend struct DefaultSingletonTraits<TestData>;
59};
60
61// A simple test net::URLRequestJob. We don't care what it does, only that
62// whether it starts and finishes.
63class SimpleTestJob : public net::URLRequestTestJob {
64 public:
65  explicit SimpleTestJob(net::URLRequest* request)
66      : net::URLRequestTestJob(request, test_headers(),
67                               TestData::GetInstance()->GetTestData(), true) {}
68
69  virtual void GetResponseInfo(net::HttpResponseInfo* info) {
70    net::URLRequestTestJob::GetResponseInfo(info);
71    if (request_->url().SchemeIsSecure()) {
72      // Make up a fake certificate for this response since we don't have
73      // access to the real SSL info.
74      const char* kCertIssuer = "Chrome Internal";
75      const int kLifetimeDays = 100;
76
77      info->ssl_info.cert =
78          new net::X509Certificate(request_->url().GetWithEmptyPath().spec(),
79                                   kCertIssuer,
80                                   base::Time::Now(),
81                                   base::Time::Now() +
82                                   base::TimeDelta::FromDays(kLifetimeDays));
83      info->ssl_info.cert_status = 0;
84      info->ssl_info.security_bits = -1;
85    }
86  }
87
88 private:
89  ~SimpleTestJob() {}
90};
91
92class TestController {
93 public:
94  static TestController* GetInstance() {
95    return Singleton<TestController>::get();
96  }
97  void set_result(bool value) {
98    result_ = value;
99  }
100  bool result() {
101    return result_;
102  }
103  void set_expected_url(const GURL& url) {
104    expected_url_ = url;
105  }
106  const GURL expected_url() {
107    return expected_url_;
108  }
109  void set_delegate(TestDelegate* delegate) {
110    delegate_ = delegate;
111  }
112  TestDelegate* delegate() {
113    return delegate_;
114  }
115  void set_use_delegate(bool value) {
116    use_delegate_ = value;
117  }
118  bool use_delegate() {
119    return use_delegate_;
120  }
121 private:
122  TestController()
123      : result_(false),
124        use_delegate_(false),
125        delegate_(NULL) {}
126
127  bool result_;
128  bool use_delegate_;
129  GURL expected_url_;
130  TestDelegate* delegate_;
131
132  friend struct DefaultSingletonTraits<TestController>;
133};
134
135}  // namespace
136
137class PrintDialogCloudTest : public InProcessBrowserTest {
138 public:
139  PrintDialogCloudTest() : handler_added_(false) {
140    PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory_);
141  }
142
143  // Must be static for handing into AddHostnameHandler.
144  static net::URLRequest::ProtocolFactory Factory;
145
146  class AutoQuitDelegate : public TestDelegate {
147   public:
148    AutoQuitDelegate() {}
149
150    virtual void OnResponseCompleted(net::URLRequest* request) {
151      BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
152                              new MessageLoop::QuitTask());
153    }
154  };
155
156  virtual void SetUp() {
157    TestController::GetInstance()->set_result(false);
158    InProcessBrowserTest::SetUp();
159  }
160
161  virtual void TearDown() {
162    if (handler_added_) {
163      net::URLRequestFilter* filter = net::URLRequestFilter::GetInstance();
164      filter->RemoveHostnameHandler(scheme_, host_name_);
165      handler_added_ = false;
166      TestController::GetInstance()->set_delegate(NULL);
167    }
168    InProcessBrowserTest::TearDown();
169  }
170
171  // Normally this is something I would expect could go into SetUp(),
172  // but there seems to be some timing or ordering related issue with
173  // the test harness that made that flaky.  Calling this from the
174  // individual test functions seems to fix that.
175  void AddTestHandlers() {
176    if (!handler_added_) {
177      net::URLRequestFilter* filter = net::URLRequestFilter::GetInstance();
178      GURL cloud_print_service_url =
179          CloudPrintURL(browser()->profile()).
180          GetCloudPrintServiceURL();
181      scheme_ = cloud_print_service_url.scheme();
182      host_name_ = cloud_print_service_url.host();
183      filter->AddHostnameHandler(scheme_, host_name_,
184                                 &PrintDialogCloudTest::Factory);
185      handler_added_ = true;
186
187      GURL cloud_print_dialog_url =
188          CloudPrintURL(browser()->profile()).
189          GetCloudPrintServiceDialogURL();
190      TestController::GetInstance()->set_expected_url(cloud_print_dialog_url);
191      TestController::GetInstance()->set_delegate(&delegate_);
192    }
193
194    CreateDialogForTest();
195  }
196
197  void CreateDialogForTest() {
198    FilePath path_to_pdf =
199        test_data_directory_.AppendASCII("printing/cloud_print_uitest.pdf");
200    BrowserThread::PostTask(
201        BrowserThread::UI, FROM_HERE,
202        NewRunnableFunction(&internal_cloud_print_helpers::CreateDialogImpl,
203                            path_to_pdf,
204                            string16(),
205                            std::string("application/pdf"),
206                            true));
207  }
208
209  bool handler_added_;
210  std::string scheme_;
211  std::string host_name_;
212  FilePath test_data_directory_;
213  AutoQuitDelegate delegate_;
214};
215
216net::URLRequestJob* PrintDialogCloudTest::Factory(net::URLRequest* request,
217                                                  const std::string& scheme) {
218  if (TestController::GetInstance()->use_delegate())
219    request->set_delegate(TestController::GetInstance()->delegate());
220  if (request &&
221      (request->url() == TestController::GetInstance()->expected_url())) {
222    TestController::GetInstance()->set_result(true);
223  }
224  return new SimpleTestJob(request);
225}
226
227IN_PROC_BROWSER_TEST_F(PrintDialogCloudTest, HandlersRegistered) {
228  BrowserList::SetLastActive(browser());
229  ASSERT_TRUE(BrowserList::GetLastActive());
230
231  AddTestHandlers();
232
233  TestController::GetInstance()->set_use_delegate(true);
234
235  ui_test_utils::RunMessageLoop();
236
237  ASSERT_TRUE(TestController::GetInstance()->result());
238}
239
240#if defined(OS_CHROMEOS)
241// Disabled until the extern URL is live so that the Print menu item
242// can be enabled for Chromium OS.
243IN_PROC_BROWSER_TEST_F(PrintDialogCloudTest, DISABLED_DialogGrabbed) {
244  BrowserList::SetLastActive(browser());
245  ASSERT_TRUE(BrowserList::GetLastActive());
246
247  AddTestHandlers();
248
249  // This goes back one step further for the Chrome OS case, to making
250  // sure 'window.print()' gets to the right place.
251  ASSERT_TRUE(browser()->GetSelectedTabContents());
252  ASSERT_TRUE(browser()->GetSelectedTabContents()->render_view_host());
253
254  string16 window_print = ASCIIToUTF16("window.print()");
255  browser()->GetSelectedTabContents()->render_view_host()->
256      ExecuteJavascriptInWebFrame(string16(), window_print);
257
258  ui_test_utils::RunMessageLoop();
259
260  ASSERT_TRUE(TestController::GetInstance()->result());
261}
262#endif
263