Home | History | Annotate | Download | only in browser
      1 // Copyright 2013 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 "content/shell/browser/shell_browser_main.h"
      6 
      7 #include <iostream>
      8 
      9 #include "base/command_line.h"
     10 #include "base/file_util.h"
     11 #include "base/files/file_path.h"
     12 #include "base/files/scoped_temp_dir.h"
     13 #include "base/logging.h"
     14 #include "base/memory/scoped_ptr.h"
     15 #include "base/message_loop/message_loop.h"
     16 #include "base/strings/sys_string_conversions.h"
     17 #include "base/strings/utf_string_conversions.h"
     18 #include "base/threading/thread_restrictions.h"
     19 #include "content/public/browser/browser_main_runner.h"
     20 #include "content/shell/browser/shell.h"
     21 #include "content/shell/browser/webkit_test_controller.h"
     22 #include "content/shell/common/shell_switches.h"
     23 #include "content/shell/common/webkit_test_helpers.h"
     24 #include "content/test/webkit_support.h"
     25 #include "net/base/net_util.h"
     26 
     27 #if defined(OS_ANDROID)
     28 #include "base/run_loop.h"
     29 #include "content/shell/browser/shell_layout_tests_android.h"
     30 #endif
     31 
     32 namespace {
     33 
     34 GURL GetURLForLayoutTest(const std::string& test_name,
     35                          base::FilePath* current_working_directory,
     36                          bool* enable_pixel_dumping,
     37                          std::string* expected_pixel_hash) {
     38   // A test name is formated like file:///path/to/test'--pixel-test'pixelhash
     39   std::string path_or_url = test_name;
     40   std::string pixel_switch;
     41   std::string pixel_hash;
     42   std::string::size_type separator_position = path_or_url.find('\'');
     43   if (separator_position != std::string::npos) {
     44     pixel_switch = path_or_url.substr(separator_position + 1);
     45     path_or_url.erase(separator_position);
     46   }
     47   separator_position = pixel_switch.find('\'');
     48   if (separator_position != std::string::npos) {
     49     pixel_hash = pixel_switch.substr(separator_position + 1);
     50     pixel_switch.erase(separator_position);
     51   }
     52   if (enable_pixel_dumping) {
     53     *enable_pixel_dumping =
     54         (pixel_switch == "--pixel-test" || pixel_switch == "-p");
     55   }
     56   if (expected_pixel_hash)
     57     *expected_pixel_hash = pixel_hash;
     58 
     59   GURL test_url;
     60 #if defined(OS_ANDROID)
     61   if (content::GetTestUrlForAndroid(path_or_url, &test_url))
     62     return test_url;
     63 #endif
     64 
     65   test_url = GURL(path_or_url);
     66   if (!(test_url.is_valid() && test_url.has_scheme())) {
     67     // We're outside of the message loop here, and this is a test.
     68     base::ThreadRestrictions::ScopedAllowIO allow_io;
     69 #if defined(OS_WIN)
     70     std::wstring wide_path_or_url =
     71         base::SysNativeMBToWide(path_or_url);
     72     base::FilePath local_file(wide_path_or_url);
     73 #else
     74     base::FilePath local_file(path_or_url);
     75 #endif
     76     if (!base::PathExists(local_file)) {
     77       local_file = content::GetWebKitRootDirFilePath()
     78           .Append(FILE_PATH_LITERAL("LayoutTests")).Append(local_file);
     79     }
     80     test_url = net::FilePathToFileURL(base::MakeAbsoluteFilePath(local_file));
     81   }
     82   base::FilePath local_path;
     83   if (current_working_directory) {
     84     // We're outside of the message loop here, and this is a test.
     85     base::ThreadRestrictions::ScopedAllowIO allow_io;
     86     if (net::FileURLToFilePath(test_url, &local_path))
     87       *current_working_directory = local_path.DirName();
     88     else
     89       file_util::GetCurrentDirectory(current_working_directory);
     90   }
     91   return test_url;
     92 }
     93 
     94 bool GetNextTest(const CommandLine::StringVector& args,
     95                  size_t* position,
     96                  std::string* test) {
     97   if (*position >= args.size())
     98     return false;
     99   if (args[*position] == FILE_PATH_LITERAL("-"))
    100     return !!std::getline(std::cin, *test, '\n');
    101 #if defined(OS_WIN)
    102   *test = WideToUTF8(args[(*position)++]);
    103 #else
    104   *test = args[(*position)++];
    105 #endif
    106   return true;
    107 }
    108 
    109 }  // namespace
    110 
    111 // Main routine for running as the Browser process.
    112 int ShellBrowserMain(
    113     const content::MainFunctionParams& parameters,
    114     const scoped_ptr<content::BrowserMainRunner>& main_runner) {
    115   bool layout_test_mode =
    116       CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree);
    117   base::ScopedTempDir browser_context_path_for_layout_tests;
    118 
    119   if (layout_test_mode) {
    120     CHECK(browser_context_path_for_layout_tests.CreateUniqueTempDir());
    121     CHECK(!browser_context_path_for_layout_tests.path().MaybeAsASCII().empty());
    122     CommandLine::ForCurrentProcess()->AppendSwitchASCII(
    123         switches::kContentShellDataPath,
    124         browser_context_path_for_layout_tests.path().MaybeAsASCII());
    125 
    126 #if defined(OS_ANDROID)
    127     content::EnsureInitializeForAndroidLayoutTests();
    128 #endif
    129   }
    130 
    131   int exit_code = main_runner->Initialize(parameters);
    132   DCHECK_LT(exit_code, 0)
    133       << "BrowserMainRunner::Initialize failed in ShellBrowserMain";
    134 
    135   if (exit_code >= 0)
    136     return exit_code;
    137 
    138   if (CommandLine::ForCurrentProcess()->HasSwitch(
    139         switches::kCheckLayoutTestSysDeps)) {
    140     base::MessageLoop::current()->PostTask(FROM_HERE,
    141                                            base::MessageLoop::QuitClosure());
    142     main_runner->Run();
    143     content::Shell::CloseAllWindows();
    144     main_runner->Shutdown();
    145     return 0;
    146   }
    147 
    148   if (layout_test_mode) {
    149     content::WebKitTestController test_controller;
    150     {
    151       // We're outside of the message loop here, and this is a test.
    152       base::ThreadRestrictions::ScopedAllowIO allow_io;
    153       base::FilePath temp_path;
    154       base::GetTempDir(&temp_path);
    155       test_controller.SetTempPath(temp_path);
    156     }
    157     std::string test_string;
    158     CommandLine::StringVector args =
    159         CommandLine::ForCurrentProcess()->GetArgs();
    160     size_t command_line_position = 0;
    161     bool ran_at_least_once = false;
    162 
    163     std::cout << "#READY\n";
    164     std::cout.flush();
    165 
    166     while (GetNextTest(args, &command_line_position, &test_string)) {
    167       if (test_string.empty())
    168         continue;
    169       if (test_string == "QUIT")
    170         break;
    171 
    172       bool enable_pixel_dumps;
    173       std::string pixel_hash;
    174       base::FilePath cwd;
    175       GURL test_url = GetURLForLayoutTest(
    176           test_string, &cwd, &enable_pixel_dumps, &pixel_hash);
    177       if (!content::WebKitTestController::Get()->PrepareForLayoutTest(
    178               test_url, cwd, enable_pixel_dumps, pixel_hash)) {
    179         break;
    180       }
    181 
    182       ran_at_least_once = true;
    183 #if defined(OS_ANDROID)
    184       // The message loop on Android is provided by the system, and does not
    185       // offer a blocking Run() method. For layout tests, use a nested loop
    186       // together with a base::RunLoop so it can block until a QuitClosure.
    187       base::RunLoop run_loop;
    188       run_loop.Run();
    189 #else
    190       main_runner->Run();
    191 #endif
    192 
    193       if (!content::WebKitTestController::Get()->ResetAfterLayoutTest())
    194         break;
    195 
    196 #if defined(OS_ANDROID)
    197       // There will be left-over tasks in the queue for Android because the
    198       // main window is being destroyed. Run them before starting the next test.
    199       base::MessageLoop::current()->RunUntilIdle();
    200 #endif
    201     }
    202     if (!ran_at_least_once) {
    203       base::MessageLoop::current()->PostTask(FROM_HERE,
    204                                              base::MessageLoop::QuitClosure());
    205       main_runner->Run();
    206     }
    207 
    208 #if defined(OS_ANDROID)
    209     // Android should only execute Shutdown() here when running layout tests.
    210     main_runner->Shutdown();
    211 #endif
    212 
    213     exit_code = 0;
    214   }
    215 
    216 #if !defined(OS_ANDROID)
    217   if (!layout_test_mode)
    218     exit_code = main_runner->Run();
    219 
    220   main_runner->Shutdown();
    221 #endif
    222 
    223   return exit_code;
    224 }
    225