• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
3 // found in the LICENSE file.
4 
5 #include "chrome/test/base/web_ui_browser_test.h"
6 
7 #include <string>
8 #include <vector>
9 
10 #include "base/lazy_instance.h"
11 #include "base/memory/ref_counted_memory.h"
12 #include "base/path_service.h"
13 #include "base/strings/string_util.h"
14 #include "base/values.h"
15 #include "chrome/browser/chrome_content_browser_client.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/browser/ui/browser_commands.h"
19 #include "chrome/browser/ui/tabs/tab_strip_model.h"
20 #include "chrome/browser/ui/webui/web_ui_test_handler.h"
21 #include "chrome/common/chrome_paths.h"
22 #include "chrome/common/url_constants.h"
23 #include "chrome/test/base/test_chrome_web_ui_controller_factory.h"
24 #include "chrome/test/base/ui_test_utils.h"
25 #include "content/public/browser/url_data_source.h"
26 #include "content/public/browser/web_contents.h"
27 #include "content/public/browser/web_contents_observer.h"
28 #include "content/public/browser/web_ui_controller.h"
29 #include "content/public/browser/web_ui_message_handler.h"
30 #include "content/public/test/browser_test_utils.h"
31 #include "content/public/test/test_navigation_observer.h"
32 #include "net/base/filename_util.h"
33 #include "ui/base/resource/resource_handle.h"
34 
35 #if defined(ENABLE_FULL_PRINTING)
36 #include "chrome/browser/printing/print_preview_dialog_controller.h"
37 #endif
38 
39 using content::RenderViewHost;
40 using content::WebContents;
41 using content::WebUIController;
42 using content::WebUIMessageHandler;
43 
44 namespace {
45 
46 base::LazyInstance<std::vector<std::string> > error_messages_ =
47     LAZY_INSTANCE_INITIALIZER;
48 
49 // Intercepts all log messages.
LogHandler(int severity,const char * file,int line,size_t message_start,const std::string & str)50 bool LogHandler(int severity,
51                 const char* file,
52                 int line,
53                 size_t message_start,
54                 const std::string& str) {
55   if (severity == logging::LOG_ERROR && file &&
56       std::string("CONSOLE") == file) {
57     error_messages_.Get().push_back(str);
58   }
59 
60   return false;
61 }
62 
63 class WebUIJsInjectionReadyObserver : public content::WebContentsObserver {
64  public:
WebUIJsInjectionReadyObserver(content::WebContents * web_contents,WebUIBrowserTest * browser_test,const std::string & preload_test_fixture,const std::string & preload_test_name)65   WebUIJsInjectionReadyObserver(content::WebContents* web_contents,
66                                 WebUIBrowserTest* browser_test,
67                                 const std::string& preload_test_fixture,
68                                 const std::string& preload_test_name)
69       : content::WebContentsObserver(web_contents),
70         browser_test_(browser_test),
71         preload_test_fixture_(preload_test_fixture),
72         preload_test_name_(preload_test_name) {}
73 
RenderViewCreated(content::RenderViewHost * rvh)74   virtual void RenderViewCreated(content::RenderViewHost* rvh) OVERRIDE {
75     browser_test_->PreLoadJavascriptLibraries(
76         preload_test_fixture_, preload_test_name_, rvh);
77   }
78 
79  private:
80   WebUIBrowserTest* browser_test_;
81   std::string preload_test_fixture_;
82   std::string preload_test_name_;
83 };
84 
85 }  // namespace
86 
~WebUIBrowserTest()87 WebUIBrowserTest::~WebUIBrowserTest() {
88 }
89 
RunJavascriptFunction(const std::string & function_name)90 bool WebUIBrowserTest::RunJavascriptFunction(const std::string& function_name) {
91   ConstValueVector empty_args;
92   return RunJavascriptFunction(function_name, empty_args);
93 }
94 
RunJavascriptFunction(const std::string & function_name,base::Value * arg)95 bool WebUIBrowserTest::RunJavascriptFunction(const std::string& function_name,
96                                              base::Value* arg) {
97   ConstValueVector args;
98   args.push_back(arg);
99   return RunJavascriptFunction(function_name, args);
100 }
101 
RunJavascriptFunction(const std::string & function_name,base::Value * arg1,base::Value * arg2)102 bool WebUIBrowserTest::RunJavascriptFunction(const std::string& function_name,
103                                              base::Value* arg1,
104                                              base::Value* arg2) {
105   ConstValueVector args;
106   args.push_back(arg1);
107   args.push_back(arg2);
108   return RunJavascriptFunction(function_name, args);
109 }
110 
RunJavascriptFunction(const std::string & function_name,const ConstValueVector & function_arguments)111 bool WebUIBrowserTest::RunJavascriptFunction(
112     const std::string& function_name,
113     const ConstValueVector& function_arguments) {
114   return RunJavascriptUsingHandler(
115       function_name, function_arguments, false, false, NULL);
116 }
117 
RunJavascriptTestF(bool is_async,const std::string & test_fixture,const std::string & test_name)118 bool WebUIBrowserTest::RunJavascriptTestF(bool is_async,
119                                           const std::string& test_fixture,
120                                           const std::string& test_name) {
121   ConstValueVector args;
122   args.push_back(new base::StringValue(test_fixture));
123   args.push_back(new base::StringValue(test_name));
124 
125   if (is_async)
126     return RunJavascriptAsyncTest("RUN_TEST_F", args);
127   else
128     return RunJavascriptTest("RUN_TEST_F", args);
129 }
130 
RunJavascriptTest(const std::string & test_name)131 bool WebUIBrowserTest::RunJavascriptTest(const std::string& test_name) {
132   ConstValueVector empty_args;
133   return RunJavascriptTest(test_name, empty_args);
134 }
135 
RunJavascriptTest(const std::string & test_name,base::Value * arg)136 bool WebUIBrowserTest::RunJavascriptTest(const std::string& test_name,
137                                          base::Value* arg) {
138   ConstValueVector args;
139   args.push_back(arg);
140   return RunJavascriptTest(test_name, args);
141 }
142 
RunJavascriptTest(const std::string & test_name,base::Value * arg1,base::Value * arg2)143 bool WebUIBrowserTest::RunJavascriptTest(const std::string& test_name,
144                                          base::Value* arg1,
145                                          base::Value* arg2) {
146   ConstValueVector args;
147   args.push_back(arg1);
148   args.push_back(arg2);
149   return RunJavascriptTest(test_name, args);
150 }
151 
RunJavascriptTest(const std::string & test_name,const ConstValueVector & test_arguments)152 bool WebUIBrowserTest::RunJavascriptTest(
153     const std::string& test_name,
154     const ConstValueVector& test_arguments) {
155   return RunJavascriptUsingHandler(
156       test_name, test_arguments, true, false, NULL);
157 }
158 
RunJavascriptAsyncTest(const std::string & test_name)159 bool WebUIBrowserTest::RunJavascriptAsyncTest(const std::string& test_name) {
160   ConstValueVector empty_args;
161   return RunJavascriptAsyncTest(test_name, empty_args);
162 }
163 
RunJavascriptAsyncTest(const std::string & test_name,base::Value * arg)164 bool WebUIBrowserTest::RunJavascriptAsyncTest(const std::string& test_name,
165                                               base::Value* arg) {
166   ConstValueVector args;
167   args.push_back(arg);
168   return RunJavascriptAsyncTest(test_name, args);
169 }
170 
RunJavascriptAsyncTest(const std::string & test_name,base::Value * arg1,base::Value * arg2)171 bool WebUIBrowserTest::RunJavascriptAsyncTest(const std::string& test_name,
172                                               base::Value* arg1,
173                                               base::Value* arg2) {
174   ConstValueVector args;
175   args.push_back(arg1);
176   args.push_back(arg2);
177   return RunJavascriptAsyncTest(test_name, args);
178 }
179 
RunJavascriptAsyncTest(const std::string & test_name,base::Value * arg1,base::Value * arg2,base::Value * arg3)180 bool WebUIBrowserTest::RunJavascriptAsyncTest(const std::string& test_name,
181                                               base::Value* arg1,
182                                               base::Value* arg2,
183                                               base::Value* arg3) {
184   ConstValueVector args;
185   args.push_back(arg1);
186   args.push_back(arg2);
187   args.push_back(arg3);
188   return RunJavascriptAsyncTest(test_name, args);
189 }
190 
RunJavascriptAsyncTest(const std::string & test_name,const ConstValueVector & test_arguments)191 bool WebUIBrowserTest::RunJavascriptAsyncTest(
192     const std::string& test_name,
193     const ConstValueVector& test_arguments) {
194   return RunJavascriptUsingHandler(test_name, test_arguments, true, true, NULL);
195 }
196 
PreLoadJavascriptLibraries(const std::string & preload_test_fixture,const std::string & preload_test_name,RenderViewHost * preload_host)197 void WebUIBrowserTest::PreLoadJavascriptLibraries(
198     const std::string& preload_test_fixture,
199     const std::string& preload_test_name,
200     RenderViewHost* preload_host) {
201   ASSERT_FALSE(libraries_preloaded_);
202   ConstValueVector args;
203   args.push_back(new base::StringValue(preload_test_fixture));
204   args.push_back(new base::StringValue(preload_test_name));
205   RunJavascriptUsingHandler(
206       "preloadJavascriptLibraries", args, false, false, preload_host);
207   libraries_preloaded_ = true;
208 }
209 
BrowsePreload(const GURL & browse_to)210 void WebUIBrowserTest::BrowsePreload(const GURL& browse_to) {
211   content::WebContents* web_contents =
212       browser()->tab_strip_model()->GetActiveWebContents();
213   WebUIJsInjectionReadyObserver injection_observer(
214       web_contents, this, preload_test_fixture_, preload_test_name_);
215   content::TestNavigationObserver navigation_observer(web_contents);
216   chrome::NavigateParams params(
217       browser(), GURL(browse_to), ui::PAGE_TRANSITION_TYPED);
218   params.disposition = CURRENT_TAB;
219   chrome::Navigate(&params);
220   navigation_observer.Wait();
221 }
222 
223 #if defined(ENABLE_FULL_PRINTING)
224 
225 // This custom ContentBrowserClient is used to get notified when a WebContents
226 // for the print preview dialog gets created.
227 class PrintContentBrowserClient : public chrome::ChromeContentBrowserClient {
228  public:
PrintContentBrowserClient(WebUIBrowserTest * browser_test,const std::string & preload_test_fixture,const std::string & preload_test_name)229   PrintContentBrowserClient(WebUIBrowserTest* browser_test,
230                             const std::string& preload_test_fixture,
231                             const std::string& preload_test_name)
232       : browser_test_(browser_test),
233         preload_test_fixture_(preload_test_fixture),
234         preload_test_name_(preload_test_name),
235         preview_dialog_(NULL),
236         message_loop_runner_(new content::MessageLoopRunner) {}
237 
Wait()238   void Wait() {
239     message_loop_runner_->Run();
240     content::WaitForLoadStop(preview_dialog_);
241   }
242 
243  private:
244   // ChromeContentBrowserClient implementation:
GetWebContentsViewDelegate(content::WebContents * web_contents)245   virtual content::WebContentsViewDelegate* GetWebContentsViewDelegate(
246       content::WebContents* web_contents) OVERRIDE {
247     preview_dialog_ = web_contents;
248     observer_.reset(new WebUIJsInjectionReadyObserver(preview_dialog_,
249                                                       browser_test_,
250                                                       preload_test_fixture_,
251                                                       preload_test_name_));
252     message_loop_runner_->Quit();
253     return NULL;
254   }
255 
256   WebUIBrowserTest* browser_test_;
257   scoped_ptr<WebUIJsInjectionReadyObserver> observer_;
258   std::string preload_test_fixture_;
259   std::string preload_test_name_;
260   content::WebContents* preview_dialog_;
261   scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
262 };
263 #endif
264 
BrowsePrintPreload(const GURL & browse_to)265 void WebUIBrowserTest::BrowsePrintPreload(const GURL& browse_to) {
266 #if defined(ENABLE_FULL_PRINTING)
267   ui_test_utils::NavigateToURL(browser(), browse_to);
268 
269   PrintContentBrowserClient new_client(
270       this, preload_test_fixture_, preload_test_name_);
271   content::ContentBrowserClient* old_client =
272       SetBrowserClientForTesting(&new_client);
273 
274   chrome::Print(browser());
275   new_client.Wait();
276 
277   SetBrowserClientForTesting(old_client);
278 
279   printing::PrintPreviewDialogController* tab_controller =
280       printing::PrintPreviewDialogController::GetInstance();
281   ASSERT_TRUE(tab_controller);
282   WebContents* preview_dialog = tab_controller->GetPrintPreviewForContents(
283       browser()->tab_strip_model()->GetActiveWebContents());
284   ASSERT_TRUE(preview_dialog);
285   SetWebUIInstance(preview_dialog->GetWebUI());
286 #else
287   NOTREACHED();
288 #endif
289 }
290 
291 const char WebUIBrowserTest::kDummyURL[] = "chrome://DummyURL";
292 
WebUIBrowserTest()293 WebUIBrowserTest::WebUIBrowserTest()
294     : test_handler_(new WebUITestHandler()),
295       libraries_preloaded_(false),
296       override_selected_web_ui_(NULL) {
297 }
298 
set_preload_test_fixture(const std::string & preload_test_fixture)299 void WebUIBrowserTest::set_preload_test_fixture(
300     const std::string& preload_test_fixture) {
301   preload_test_fixture_ = preload_test_fixture;
302 }
303 
set_preload_test_name(const std::string & preload_test_name)304 void WebUIBrowserTest::set_preload_test_name(
305     const std::string& preload_test_name) {
306   preload_test_name_ = preload_test_name;
307 }
308 
309 namespace {
310 
311 // DataSource for the dummy URL.  If no data source is provided then an error
312 // page is shown. While this doesn't matter for most tests, without it,
313 // navigation to different anchors cannot be listened to (via the hashchange
314 // event).
315 class MockWebUIDataSource : public content::URLDataSource {
316  public:
MockWebUIDataSource()317   MockWebUIDataSource() {}
318 
319  private:
~MockWebUIDataSource()320   virtual ~MockWebUIDataSource() {}
321 
GetSource() const322   virtual std::string GetSource() const OVERRIDE { return "dummyurl"; }
323 
StartDataRequest(const std::string & path,int render_process_id,int render_frame_id,const content::URLDataSource::GotDataCallback & callback)324   virtual void StartDataRequest(
325       const std::string& path,
326       int render_process_id,
327       int render_frame_id,
328       const content::URLDataSource::GotDataCallback& callback) OVERRIDE {
329     std::string dummy_html = "<html><body>Dummy</body></html>";
330     scoped_refptr<base::RefCountedString> response =
331         base::RefCountedString::TakeString(&dummy_html);
332     callback.Run(response.get());
333   }
334 
GetMimeType(const std::string & path) const335   virtual std::string GetMimeType(const std::string& path) const OVERRIDE {
336     return "text/html";
337   }
338 
339   DISALLOW_COPY_AND_ASSIGN(MockWebUIDataSource);
340 };
341 
342 // WebUIProvider to allow attaching the DataSource for the dummy URL when
343 // testing.
344 class MockWebUIProvider
345     : public TestChromeWebUIControllerFactory::WebUIProvider {
346  public:
MockWebUIProvider()347   MockWebUIProvider() {}
348 
349   // Returns a new WebUI
NewWebUI(content::WebUI * web_ui,const GURL & url)350   virtual WebUIController* NewWebUI(content::WebUI* web_ui,
351                                     const GURL& url) OVERRIDE {
352     WebUIController* controller = new content::WebUIController(web_ui);
353     Profile* profile = Profile::FromWebUI(web_ui);
354     content::URLDataSource::Add(profile, new MockWebUIDataSource());
355     return controller;
356   }
357 
358  private:
359   DISALLOW_COPY_AND_ASSIGN(MockWebUIProvider);
360 };
361 
362 base::LazyInstance<MockWebUIProvider> mock_provider_ =
363     LAZY_INSTANCE_INITIALIZER;
364 
365 }  // namespace
366 
SetUpOnMainThread()367 void WebUIBrowserTest::SetUpOnMainThread() {
368   JavaScriptBrowserTest::SetUpOnMainThread();
369 
370   logging::SetLogMessageHandler(&LogHandler);
371 
372   AddLibrary(base::FilePath(kA11yAuditLibraryJSPath));
373 
374   content::WebUIControllerFactory::UnregisterFactoryForTesting(
375       ChromeWebUIControllerFactory::GetInstance());
376 
377   test_factory_.reset(new TestChromeWebUIControllerFactory);
378 
379   content::WebUIControllerFactory::RegisterFactory(test_factory_.get());
380 
381   test_factory_->AddFactoryOverride(GURL(kDummyURL).host(),
382                                     mock_provider_.Pointer());
383 }
384 
TearDownOnMainThread()385 void WebUIBrowserTest::TearDownOnMainThread() {
386   logging::SetLogMessageHandler(NULL);
387 
388   test_factory_->RemoveFactoryOverride(GURL(kDummyURL).host());
389   content::WebUIControllerFactory::UnregisterFactoryForTesting(
390       test_factory_.get());
391 
392   // This is needed to avoid a debug assert after the test completes, see stack
393   // trace in http://crrev.com/179347
394   content::WebUIControllerFactory::RegisterFactory(
395       ChromeWebUIControllerFactory::GetInstance());
396 
397   test_factory_.reset();
398 }
399 
SetWebUIInstance(content::WebUI * web_ui)400 void WebUIBrowserTest::SetWebUIInstance(content::WebUI* web_ui) {
401   override_selected_web_ui_ = web_ui;
402 }
403 
GetMockMessageHandler()404 WebUIMessageHandler* WebUIBrowserTest::GetMockMessageHandler() {
405   return NULL;
406 }
407 
RunJavascriptUsingHandler(const std::string & function_name,const ConstValueVector & function_arguments,bool is_test,bool is_async,RenderViewHost * preload_host)408 bool WebUIBrowserTest::RunJavascriptUsingHandler(
409     const std::string& function_name,
410     const ConstValueVector& function_arguments,
411     bool is_test,
412     bool is_async,
413     RenderViewHost* preload_host) {
414   // Get the user libraries. Preloading them individually is best, then
415   // we can assign each one a filename for better stack traces. Otherwise
416   // append them all to |content|.
417   base::string16 content;
418   std::vector<base::string16> libraries;
419   if (!libraries_preloaded_) {
420     BuildJavascriptLibraries(&libraries);
421     if (!preload_host) {
422       content = JoinString(libraries, '\n');
423       libraries.clear();
424     }
425   }
426 
427   if (!function_name.empty()) {
428     base::string16 called_function;
429     if (is_test) {
430       called_function =
431           BuildRunTestJSCall(is_async, function_name, function_arguments);
432     } else {
433       called_function = content::WebUI::GetJavascriptCall(
434           function_name, function_arguments.get());
435     }
436     content.append(called_function);
437   }
438 
439   if (!preload_host)
440     SetupHandlers();
441 
442   bool result = true;
443 
444   for (size_t i = 0; i < libraries.size(); ++i)
445     test_handler_->PreloadJavaScript(libraries[i], preload_host);
446 
447   if (is_test)
448     result = test_handler_->RunJavaScriptTestWithResult(content);
449   else if (preload_host)
450     test_handler_->PreloadJavaScript(content, preload_host);
451   else
452     test_handler_->RunJavaScript(content);
453 
454   if (error_messages_.Get().size() > 0) {
455     LOG(ERROR) << "Encountered javascript console error(s)";
456     result = false;
457     error_messages_.Get().clear();
458   }
459   return result;
460 }
461 
SetupHandlers()462 void WebUIBrowserTest::SetupHandlers() {
463   content::WebUI* web_ui_instance =
464       override_selected_web_ui_
465           ? override_selected_web_ui_
466           : browser()->tab_strip_model()->GetActiveWebContents()->GetWebUI();
467   ASSERT_TRUE(web_ui_instance != NULL);
468 
469   test_handler_->set_web_ui(web_ui_instance);
470   test_handler_->RegisterMessages();
471 
472   if (GetMockMessageHandler()) {
473     GetMockMessageHandler()->set_web_ui(web_ui_instance);
474     GetMockMessageHandler()->RegisterMessages();
475   }
476 }
477 
WebUITestDataPathToURL(const base::FilePath::StringType & path)478 GURL WebUIBrowserTest::WebUITestDataPathToURL(
479     const base::FilePath::StringType& path) {
480   base::FilePath dir_test_data;
481   EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &dir_test_data));
482   base::FilePath test_path(dir_test_data.Append(kWebUITestFolder).Append(path));
483   EXPECT_TRUE(base::PathExists(test_path));
484   return net::FilePathToFileURL(test_path);
485 }
486