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_frame/test/net/fake_external_tab.h"
6
7#include <atlbase.h>
8#include <atlcom.h>
9#include <exdisp.h>
10#include <Winsock2.h>
11
12#include "base/bind.h"
13#include "base/command_line.h"
14#include "base/debug/debugger.h"
15#include "base/file_util.h"
16#include "base/file_version_info.h"
17#include "base/files/scoped_temp_dir.h"
18#include "base/i18n/icu_util.h"
19#include "base/lazy_instance.h"
20#include "base/memory/scoped_ptr.h"
21#include "base/path_service.h"
22#include "base/prefs/json_pref_store.h"
23#include "base/prefs/pref_registry_simple.h"
24#include "base/prefs/pref_service.h"
25#include "base/strings/string_piece.h"
26#include "base/strings/string_util.h"
27#include "base/strings/stringprintf.h"
28#include "base/system_monitor/system_monitor.h"
29#include "base/test/test_timeouts.h"
30#include "base/threading/platform_thread.h"
31#include "base/threading/thread.h"
32#include "base/win/scoped_comptr.h"
33#include "base/win/scoped_handle.h"
34#include "chrome/app/chrome_main_delegate.h"
35#include "chrome/browser/automation/automation_provider_list.h"
36#include "chrome/browser/chrome_content_browser_client.h"
37#include "chrome/browser/prefs/browser_prefs.h"
38#include "chrome/browser/prefs/proxy_config_dictionary.h"
39#include "chrome/browser/process_singleton.h"
40#include "chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.h"
41#include "chrome/browser/profiles/profile_manager.h"
42#include "chrome/browser/profiles/profiles_state.h"
43#include "chrome/browser/renderer_host/web_cache_manager.h"
44#include "chrome/common/chrome_constants.h"
45#include "chrome/common/chrome_content_client.h"
46#include "chrome/common/chrome_paths.h"
47#include "chrome/common/chrome_paths_internal.h"
48#include "chrome/common/chrome_switches.h"
49#include "chrome/common/pref_names.h"
50#include "chrome/renderer/chrome_content_renderer_client.h"
51#include "chrome/test/base/ui_test_utils.h"
52#include "chrome/test/logging/win/file_logger.h"
53#include "chrome/test/logging/win/log_file_printer.h"
54#include "chrome/test/logging/win/test_log_collector.h"
55#include "chrome_frame/crash_server_init.h"
56#include "chrome_frame/test/chrome_frame_test_utils.h"
57#include "chrome_frame/test/ie_configurator.h"
58#include "chrome_frame/test/net/test_automation_resource_message_filter.h"
59#include "chrome_frame/test/simulate_input.h"
60#include "chrome_frame/test/win_event_receiver.h"
61#include "chrome_frame/utils.h"
62#include "content/public/app/content_main.h"
63#include "content/public/app/startup_helper_win.h"
64#include "content/public/browser/browser_thread.h"
65#include "content/public/browser/notification_service.h"
66#include "content/public/browser/render_process_host.h"
67#include "content/public/common/content_client.h"
68#include "content/public/common/content_paths.h"
69#include "net/base/net_util.h"
70#include "net/url_request/url_request_test_util.h"
71#include "sandbox/win/src/sandbox_types.h"
72#include "testing/gtest/include/gtest/gtest.h"
73#include "ui/base/resource/resource_bundle.h"
74#include "ui/base/resource/resource_bundle_win.h"
75#include "ui/base/ui_base_paths.h"
76
77#if defined(USE_AURA)
78#include "ui/gfx/screen.h"
79#include "ui/views/widget/desktop_aura/desktop_screen.h"
80#endif
81
82using content::BrowserThread;
83
84namespace {
85
86// We must store this globally so that our main delegate can set it.
87static CFUrlRequestUnittestRunner* g_test_suite = NULL;
88
89// Copied here for access by CreateBrowserMainParts and InitGoogleTest.
90static int g_argc = 0;
91static char** g_argv = NULL;
92
93// A special command line switch to allow developers to manually launch the
94// browser and debug CF inside the browser.
95const char kManualBrowserLaunch[] = "manual-browser";
96
97// Pops up a message box after the test environment has been set up
98// and before tearing it down.  Useful for when debugging tests and not
99// the test environment that's been set up.
100const char kPromptAfterSetup[] = "prompt-after-setup";
101
102const int kTestServerPort = 4666;
103// The test HTML we use to initialize Chrome Frame.
104// Note that there's a little trick in there to avoid an extra URL request
105// that the browser will otherwise make for the site's favicon.
106// If we don't do this the browser will create a new URL request after
107// the CF page has been initialized and that URL request will confuse the
108// global URL instance counter in the unit tests and subsequently trip
109// some DCHECKs.
110const char kChromeFrameHtml[] = "<html><head>"
111    "<meta http-equiv=\"X-UA-Compatible\" content=\"chrome=1\" />"
112    "<link rel=\"shortcut icon\" href=\"file://c:\\favicon.ico\"/>"
113    "</head><body>Chrome Frame should now be loaded</body></html>";
114
115// Uses the IAccessible interface for the window to set the focus.
116// This can be useful when you don't have control over the thread that
117// owns the window.
118// NOTE: this depends on oleacc.lib which the net tests already depend on
119// but other unit tests don't depend on oleacc so we can't just add the method
120// directly into chrome_frame_test_utils.cc (without adding a
121// #pragma comment(lib, "oleacc.lib")).
122bool SetFocusToAccessibleWindow(HWND hwnd) {
123  bool ret = false;
124  base::win::ScopedComPtr<IAccessible> acc;
125  AccessibleObjectFromWindow(hwnd, OBJID_WINDOW, IID_IAccessible,
126      reinterpret_cast<void**>(acc.Receive()));
127  if (acc) {
128    VARIANT self = { VT_I4 };
129    self.lVal = CHILDID_SELF;
130    ret = SUCCEEDED(acc->accSelect(SELFLAG_TAKEFOCUS, self));
131  }
132  return ret;
133}
134
135class FakeContentBrowserClient : public chrome::ChromeContentBrowserClient {
136 public:
137  virtual ~FakeContentBrowserClient() {}
138
139  virtual content::BrowserMainParts* CreateBrowserMainParts(
140      const content::MainFunctionParams& parameters) OVERRIDE;
141};
142
143base::LazyInstance<chrome::ChromeContentClient>
144    g_chrome_content_client = LAZY_INSTANCE_INITIALIZER;
145
146// Override the default ContentBrowserClient to let Chrome participate in
147// content logic.  Must be done before any tabs are created.
148base::LazyInstance<FakeContentBrowserClient>
149    g_browser_client = LAZY_INSTANCE_INITIALIZER;
150
151base::LazyInstance<chrome::ChromeContentRendererClient>
152    g_renderer_client = LAZY_INSTANCE_INITIALIZER;
153
154class FakeMainDelegate : public content::ContentMainDelegate {
155 public:
156  virtual ~FakeMainDelegate() {}
157
158  virtual bool BasicStartupComplete(int* exit_code) OVERRIDE {
159    logging_win::InstallTestLogCollector(
160        testing::UnitTest::GetInstance());
161
162    content::SetContentClient(&g_chrome_content_client.Get());
163    content::SetRendererClientForTesting(&g_renderer_client.Get());
164    return false;
165  }
166
167  // Override the default ContentBrowserClient to let Chrome participate in
168  // content logic.  We use a subclass of Chrome's implementation,
169  // FakeContentBrowserClient, to override CreateBrowserMainParts.  Must
170  // be done before any tabs are created.
171  virtual content::ContentBrowserClient* CreateContentBrowserClient() OVERRIDE {
172    return &g_browser_client.Get();
173  };
174};
175
176void FilterDisabledTests() {
177  if (::testing::FLAGS_gtest_filter.length() &&
178      ::testing::FLAGS_gtest_filter != "*") {
179    // Don't override user specified filters.
180    return;
181  }
182
183  const char* disabled_tests[] = {
184    // Tests disabled since they're testing the same functionality used
185    // by the TestAutomationProvider.
186    "URLRequestTest.Intercept",
187    "URLRequestTest.InterceptNetworkError",
188    "URLRequestTest.InterceptRestartRequired",
189    "URLRequestTest.InterceptRespectsCancelMain",
190    "URLRequestTest.InterceptRespectsCancelRedirect",
191    "URLRequestTest.InterceptRespectsCancelFinal",
192    "URLRequestTest.InterceptRespectsCancelInRestart",
193    "URLRequestTest.InterceptRedirect",
194    "URLRequestTest.InterceptServerError",
195    "URLRequestTestFTP.*",
196
197    // Tests that are currently not working:
198
199    // Temporarily disabled because they needs user input (login dialog).
200    "URLRequestTestHTTP.BasicAuth",
201    "URLRequestTestHTTP.BasicAuthWithCookies",
202
203    // ChromeFrame does not support load timing.
204    "URLRequestTestHTTP.BasicAuthLoadTiming",
205    "URLRequestTestHTTP.GetTestLoadTiming",
206    "URLRequestTestHTTP.RedirectLoadTiming",
207
208    // HTTPS tests temporarily disabled due to the certificate error dialog.
209    // TODO(tommi): The tests currently fail though, so need to fix.
210    "HTTPSRequestTest.HTTPSMismatchedTest",
211    "HTTPSRequestTest.HTTPSExpiredTest",
212    "HTTPSRequestTest.ClientAuthTest",
213
214    // More HTTPS tests failing due to certificate dialogs.
215    // http://crbug.com/102991
216    "URLRequestTestHTTP.HTTPSToHTTPRedirectNoRefererTest",
217    "HTTPSRequestTest.HTTPSGetTest",
218
219    // Tests chrome's network stack's cache (might not apply to CF).
220    "URLRequestTestHTTP.VaryHeader",
221    "URLRequestTestHTTP.GetZippedTest",
222
223    // Tests that requests can be blocked asynchronously in states
224    // OnBeforeURLRequest, OnBeforeSendHeaders and OnHeadersReceived. At least
225    // the second state is not supported by CF.
226    "URLRequestTestHTTP.NetworkDelegateBlockAsynchronously",
227
228    // Tests for cancelling requests in states OnBeforeSendHeaders and
229    // OnHeadersReceived, which do not seem supported by CF.
230    "URLRequestTestHTTP.NetworkDelegateCancelRequestSynchronously2",
231    "URLRequestTestHTTP.NetworkDelegateCancelRequestSynchronously3",
232    "URLRequestTestHTTP.NetworkDelegateCancelRequestAsynchronously2",
233    "URLRequestTestHTTP.NetworkDelegateCancelRequestAsynchronously3",
234
235    // Tests that requests can be cancelled while blocking in
236    // OnBeforeSendHeaders state. But this state is not supported by CF.
237    "URLRequestTestHTTP.NetworkDelegateCancelWhileWaiting2",
238
239    // Tests that requests can be cancelled while blocking in
240    // OnHeadersRecevied state. At first glance, this state does not appear to
241    // be supported by CF.
242    "URLRequestTestHTTP.NetworkDelegateCancelWhileWaiting3",
243
244    // Tests that requests can be cancelled while blocking in OnAuthRequired
245    // state. At first glance, this state does not appear to be supported by CF.
246    // IE displays a credentials prompt during this test - I (erikwright)
247    // believe that, from Chrome's point of view this is not a state change. In
248    // any case, I also believe that we do not have support for handling the
249    // credentials dialog during tests.
250    "URLRequestTestHTTP.NetworkDelegateCancelWhileWaiting4",
251
252    // I suspect we can only get this one to work (if at all) on IE8 and
253    // later by using the new INTERNET_OPTION_SUPPRESS_BEHAVIOR flags
254    // See http://msdn.microsoft.com/en-us/library/aa385328(VS.85).aspx
255    "URLRequestTest.DoNotSaveCookies",
256    "URLRequestTest.DelayedCookieCallback",
257
258    // TODO(ananta): This test has been consistently failing. Disabling it for
259    // now.
260    "URLRequestTestHTTP.GetTest_NoCache",
261
262    // These tests use HTTPS, and IE's trust store does not have the test
263    // certs. So these tests time out waiting for user input. The
264    // functionality they test (HTTP Strict Transport Security and
265    // HTTP-based Public Key Pinning) does not work in Chrome Frame anyway.
266    "URLRequestTestHTTP.ProcessPKP",
267    "URLRequestTestHTTP.ProcessSTS",
268    "URLRequestTestHTTP.ProcessSTSOnce",
269    "URLRequestTestHTTP.ProcessSTSAndPKP",
270    "URLRequestTestHTTP.ProcessSTSAndPKP2",
271
272    // These tests have been disabled as the Chrome cookie policies don't make
273    // sense or have not been implemented for the host network stack.
274    "URLRequestTest.DoNotSaveCookies_ViaPolicy",
275    "URLRequestTest.DoNotSendCookies_ViaPolicy",
276    "URLRequestTest.DoNotSaveCookies_ViaPolicy_Async",
277    "URLRequestTest.CookiePolicy_ForceSession",
278    "URLRequestTest.DoNotSendCookies",
279    "URLRequestTest.DoNotSendCookies_ViaPolicy_Async",
280    "URLRequestTest.CancelTest_During_OnGetCookies",
281    "URLRequestTest.CancelTest_During_OnSetCookie",
282
283    // These tests are disabled as they rely on functionality provided by
284    // Chrome's HTTP stack like the ability to set the proxy for a URL, etc.
285    "URLRequestTestHTTP.ProxyTunnelRedirectTest",
286    "URLRequestTestHTTP.NetworkDelegateTunnelConnectionFailed",
287    "URLRequestTestHTTP.UnexpectedServerAuthTest",
288
289    // These tests are disabled as they expect an empty UA to be echoed back
290    // from the server which is not the case in ChromeFrame.
291    "URLRequestTestHTTP.DefaultUserAgent",
292    "URLRequestTestHTTP.EmptyHttpUserAgentSettings",
293    // This test modifies the UploadData object after it has been marshaled to
294    // ChromeFrame. We don't support this.
295    "URLRequestTestHTTP.TestPostChunkedDataAfterStart",
296
297    // Do not work in CF, it may well be that IE is unconditionally
298    // adding Accept-Encoding header by default to outgoing requests.
299    "URLRequestTestHTTP.DefaultAcceptEncoding",
300    "URLRequestTestHTTP.OverrideAcceptEncoding",
301
302    // Not supported in ChromeFrame as we use IE's network stack.
303    "URLRequestTest.NetworkDelegateProxyError",
304    "URLRequestTest.AcceptClockSkewCookieWithWrongDateTimezone",
305
306    // URLRequestAutomationJob needs to support NeedsAuth.
307    // http://crbug.com/98446
308    "URLRequestTestHTTP.NetworkDelegateOnAuthRequiredSyncNoAction",
309    "URLRequestTestHTTP.NetworkDelegateOnAuthRequiredSyncSetAuth",
310    "URLRequestTestHTTP.NetworkDelegateOnAuthRequiredSyncCancel",
311    "URLRequestTestHTTP.NetworkDelegateOnAuthRequiredAsyncNoAction",
312    "URLRequestTestHTTP.NetworkDelegateOnAuthRequiredAsyncSetAuth",
313    "URLRequestTestHTTP.NetworkDelegateOnAuthRequiredAsyncCancel",
314
315    // Flaky on the tryservers, http://crbug.com/103097
316    "URLRequestTestHTTP.MultipleRedirectTest",
317    "URLRequestTestHTTP.NetworkDelegateRedirectRequest",
318
319    // These tests are unsupported in CF.
320    "HTTPSRequestTest.HTTPSPreloadedHSTSTest",
321    "HTTPSRequestTest.HTTPSErrorsNoClobberTSSTest",
322    "HTTPSRequestTest.HSTSPreservesPosts",
323    "HTTPSRequestTest.ResumeTest",
324    "HTTPSRequestTest.SSLSessionCacheShardTest",
325    "HTTPSRequestTest.SSLSessionCacheShardTest",
326    "HTTPSRequestTest.SSLv3Fallback",
327    "HTTPSRequestTest.TLSv1Fallback",
328    "HTTPSHardFailTest.*",
329    "HTTPSOCSPTest.*",
330    "HTTPSEVCRLSetTest.*",
331    "HTTPSCRLSetTest.*",
332
333    // Chrome Frame doesn't support GetFullRequestHeaders.
334    "URLRequestTest*.*_GetFullRequestHeaders"
335  };
336
337  const char* ie9_disabled_tests[] = {
338    // These always hang on Joi's box with IE9, http://crbug.com/105435.
339    // Several other tests, e.g. URLRequestTestHTTP.CancelTest2, 3 and
340    // 5, often hang but not always.
341    "URLRequestTestHTTP.NetworkDelegateRedirectRequestPost",
342    "URLRequestTestHTTP.GetTest",
343    "HTTPSRequestTest.HTTPSPreloadedHSTSTest",
344    // This always hangs on erikwright's box with IE9.
345    "URLRequestTestHTTP.Redirect302Tests"
346  };
347
348  std::string filter("-");  // All following filters will be negative.
349  for (int i = 0; i < arraysize(disabled_tests); ++i) {
350    if (i > 0)
351      filter += ":";
352
353    // If the rule has the form TestSuite.TestCase, also filter out
354    // TestSuite.FLAKY_TestCase . This way the exclusion rules above
355    // don't need to be updated when a test is marked flaky.
356    base::StringPiece test_name(disabled_tests[i]);
357    size_t dot_index = test_name.find('.');
358    if (dot_index != base::StringPiece::npos &&
359        dot_index + 1 < test_name.size()) {
360      test_name.substr(0, dot_index).AppendToString(&filter);
361      filter += ".FLAKY_";
362      test_name.substr(dot_index + 1).AppendToString(&filter);
363      filter += ":";
364    }
365    filter += disabled_tests[i];
366  }
367
368  if (chrome_frame_test::GetInstalledIEVersion() >= IE_9) {
369    for (int i = 0; i < arraysize(ie9_disabled_tests); ++i) {
370      filter += ":";
371      filter += ie9_disabled_tests[i];
372    }
373  }
374
375  ::testing::FLAGS_gtest_filter = filter;
376}
377
378}  // namespace
379
380// Same as BrowserProcessImpl, but uses custom profile manager.
381class FakeBrowserProcessImpl : public BrowserProcessImpl {
382 public:
383  FakeBrowserProcessImpl(base::SequencedTaskRunner* local_state_task_runner,
384                         const CommandLine& command_line)
385      : BrowserProcessImpl(local_state_task_runner, command_line) {
386    profiles_dir_.CreateUniqueTempDir();
387  }
388
389  virtual ~FakeBrowserProcessImpl() {}
390
391  virtual ProfileManager* profile_manager() OVERRIDE {
392    if (!profile_manager_.get()) {
393      profile_manager_.reset(
394          new ProfileManagerWithoutInit(profiles_dir_.path()));
395    }
396    return profile_manager_.get();
397  }
398
399  virtual MetricsService* metrics_service() OVERRIDE {
400    return NULL;
401  }
402
403  void DestroyProfileManager() {
404    profile_manager_.reset();
405  }
406
407 private:
408  base::ScopedTempDir profiles_dir_;
409  scoped_ptr<ProfileManager> profile_manager_;
410};
411
412class SupplyProxyCredentials : public WindowObserver {
413 public:
414  SupplyProxyCredentials(const char* username, const char* password);
415
416 protected:
417  struct DialogProps {
418    HWND username_;
419    HWND password_;
420  };
421
422  virtual void OnWindowOpen(HWND hwnd);
423  virtual void OnWindowClose(HWND hwnd);
424  static BOOL CALLBACK EnumChildren(HWND hwnd, LPARAM param);
425
426 protected:
427  std::string username_;
428  std::string password_;
429};
430
431
432SupplyProxyCredentials::SupplyProxyCredentials(const char* username,
433                                               const char* password)
434    : username_(username), password_(password) {
435}
436
437void SupplyProxyCredentials::OnWindowClose(HWND hwnd) { }
438
439void SupplyProxyCredentials::OnWindowOpen(HWND hwnd) {
440  DialogProps props = {0};
441  ::EnumChildWindows(hwnd, EnumChildren, reinterpret_cast<LPARAM>(&props));
442  DCHECK(::IsWindow(props.username_));
443  DCHECK(::IsWindow(props.password_));
444
445  // We can't use SetWindowText to set the username/password, so simulate
446  // keyboard input instead.
447  simulate_input::ForceSetForegroundWindow(hwnd);
448  CHECK(SetFocusToAccessibleWindow(props.username_));
449  simulate_input::SendStringA(username_.c_str());
450  Sleep(100);
451
452  simulate_input::SendCharA(VK_TAB, simulate_input::NONE);
453  Sleep(100);
454  simulate_input::SendStringA(password_.c_str());
455
456  Sleep(100);
457  simulate_input::SendCharA(VK_RETURN, simulate_input::NONE);
458}
459
460// static
461BOOL SupplyProxyCredentials::EnumChildren(HWND hwnd, LPARAM param) {
462  if (!::IsWindowVisible(hwnd))
463    return TRUE;  // Ignore but continue to enumerate.
464
465  DialogProps* props = reinterpret_cast<DialogProps*>(param);
466
467  char class_name[MAX_PATH] = {0};
468  ::GetClassNameA(hwnd, class_name, arraysize(class_name));
469  if (lstrcmpiA(class_name, "Edit") == 0) {
470    if (props->username_ == NULL || props->username_ == hwnd) {
471      props->username_ = hwnd;
472    } else if (props->password_ == NULL) {
473      props->password_ = hwnd;
474    }
475  } else {
476    EnumChildWindows(hwnd, EnumChildren, param);
477  }
478
479  return TRUE;
480}
481
482FakeExternalTab::FakeExternalTab() {
483  user_data_dir_ = chrome_frame_test::GetProfilePathForIE();
484
485  if (base::PathExists(user_data_dir_)) {
486    VLOG(1) << __FUNCTION__ << " deleting IE Profile user data directory "
487            << user_data_dir_.value();
488    bool deleted = base::DeleteFile(user_data_dir_, true);
489    LOG_IF(ERROR, !deleted) << "Failed to delete user data directory directory "
490                            << user_data_dir_.value();
491  }
492
493  PathService::Get(chrome::DIR_USER_DATA, &overridden_user_dir_);
494  PathService::Override(chrome::DIR_USER_DATA, user_data_dir_);
495}
496
497FakeExternalTab::~FakeExternalTab() {
498  if (!overridden_user_dir_.empty()) {
499    PathService::Override(chrome::DIR_USER_DATA, overridden_user_dir_);
500  }
501}
502
503void FakeExternalTab::Initialize() {
504  DCHECK(g_browser_process == NULL);
505
506  TestTimeouts::Initialize();
507
508  // Load Chrome.dll as our resource dll.
509  base::FilePath dll;
510  PathService::Get(base::DIR_MODULE, &dll);
511  dll = dll.Append(chrome::kBrowserResourcesDll);
512  HMODULE res_mod = ::LoadLibraryExW(dll.value().c_str(),
513      NULL, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);
514  DCHECK(res_mod);
515  _AtlBaseModule.SetResourceInstance(res_mod);
516
517  // Point the ResourceBundle at chrome.dll.
518  ui::SetResourcesDataDLL(_AtlBaseModule.GetResourceInstance());
519
520  ResourceBundle::InitSharedInstanceWithLocale("en-US", NULL);
521
522  CommandLine* cmd = CommandLine::ForCurrentProcess();
523  cmd->AppendSwitch(switches::kDisableWebResources);
524  cmd->AppendSwitch(switches::kSingleProcess);
525
526  base::FilePath local_state_path;
527  CHECK(PathService::Get(chrome::FILE_LOCAL_STATE, &local_state_path));
528  scoped_refptr<base::SequencedTaskRunner> local_state_task_runner =
529      JsonPrefStore::GetTaskRunnerForFile(local_state_path,
530                                          BrowserThread::GetBlockingPool());
531  browser_process_.reset(new FakeBrowserProcessImpl(local_state_task_runner,
532                                                    *cmd));
533  // BrowserProcessImpl's constructor should set g_browser_process.
534  DCHECK(g_browser_process);
535  g_browser_process->SetApplicationLocale("en-US");
536
537  content::RenderProcessHost::SetRunRendererInProcess(true);
538
539  // TODO(joi): Registration should be done up front via browser_prefs.cc
540  scoped_refptr<PrefRegistrySimple> registry = static_cast<PrefRegistrySimple*>(
541      browser_process_->local_state()->DeprecatedGetPrefRegistry());
542  if (!browser_process_->local_state()->FindPreference(
543          prefs::kMetricsReportingEnabled)) {
544    registry->RegisterBooleanPref(prefs::kMetricsReportingEnabled, false);
545  }
546}
547
548void FakeExternalTab::InitializePostThreadsCreated() {
549#if defined(USE_AURA)
550  gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE,
551                                 views::CreateDesktopScreen());
552#endif
553  base::FilePath profile_path(
554      profiles::GetDefaultProfileDir(user_data()));
555  Profile* profile =
556      g_browser_process->profile_manager()->GetProfile(profile_path);
557}
558
559void FakeExternalTab::Shutdown() {
560  browser_process_.reset();
561  g_browser_process = NULL;
562
563  ResourceBundle::CleanupSharedInstance();
564}
565
566FakeBrowserProcessImpl* FakeExternalTab::browser_process() const {
567  return browser_process_.get();
568}
569
570CFUrlRequestUnittestRunner::CFUrlRequestUnittestRunner(int argc, char** argv)
571    : NetTestSuite(argc, argv, false),
572      chrome_frame_html_("/chrome_frame", kChromeFrameHtml),
573      registrar_(chrome_frame_test::GetTestBedType()),
574      test_result_(0),
575      launch_browser_(
576          !CommandLine::ForCurrentProcess()->HasSwitch(kManualBrowserLaunch)),
577      prompt_after_setup_(
578          CommandLine::ForCurrentProcess()->HasSwitch(kPromptAfterSetup)),
579      tests_ran_(false) {
580}
581
582CFUrlRequestUnittestRunner::~CFUrlRequestUnittestRunner() {
583}
584
585void CFUrlRequestUnittestRunner::StartChromeFrameInHostBrowser() {
586  if (!launch_browser_)
587    return;
588
589  chrome_frame_test::CloseAllIEWindows();
590
591  // Tweak IE settings to make it amenable to testing before launching it.
592  ie_configurator_.reset(chrome_frame_test::CreateConfigurator());
593  if (ie_configurator_.get() != NULL) {
594    ie_configurator_->Initialize();
595    ie_configurator_->ApplySettings();
596  }
597
598  test_http_server_.reset(new test_server::SimpleWebServer("127.0.0.1",
599                                                           kTestServerPort));
600  test_http_server_->AddResponse(&chrome_frame_html_);
601  std::wstring url(base::StringPrintf(L"http://localhost:%i/chrome_frame",
602                                      kTestServerPort));
603
604  // Launch IE.  This launches IE correctly on Vista too.
605  base::win::ScopedHandle ie_process(chrome_frame_test::LaunchIE(url));
606  EXPECT_TRUE(ie_process.IsValid());
607
608  // NOTE: If you're running IE8 and CF is not being loaded, you need to
609  // disable IE8's prebinding until CF properly handles that situation.
610  //
611  // HKCU\Software\Microsoft\Internet Explorer\Main
612  // Value name: EnablePreBinding (REG_DWORD)
613  // Value: 0
614}
615
616void CFUrlRequestUnittestRunner::ShutDownHostBrowser() {
617  if (launch_browser_)
618    chrome_frame_test::CloseAllIEWindows();
619}
620
621void CFUrlRequestUnittestRunner::OnIEShutdownFailure() {
622  LOG(ERROR) << "Failed to shutdown IE and npchrome_frame cleanly after test "
623                "execution.";
624
625  if (ie_configurator_.get() != NULL)
626    ie_configurator_->RevertSettings();
627
628  StopFileLogger(true);
629  ::ExitProcess(0);
630}
631
632// Override virtual void Initialize to not call icu initialize.
633void CFUrlRequestUnittestRunner::Initialize() {
634  DCHECK(::GetCurrentThreadId() == test_thread_id_);
635
636  // Start by replicating some of the steps that would otherwise be
637  // done by TestSuite::Initialize.  We can't call the base class
638  // directly because it will attempt to initialize some things such as
639  // ICU that have already been initialized for this process.
640  CFUrlRequestUnittestRunner::InitializeLogging();
641
642  SuppressErrorDialogs();
643  base::debug::SetSuppressDebugUI(true);
644  logging::SetLogAssertHandler(UnitTestAssertHandler);
645
646  // Next, do some initialization for NetTestSuite.
647  NetTestSuite::InitializeTestThreadNoNetworkChangeNotifier();
648
649  // Finally, override the host used by the HTTP tests. See
650  // http://crbug.com/114369 .
651  OverrideHttpHost();
652}
653
654void CFUrlRequestUnittestRunner::Shutdown() {
655  DCHECK(::GetCurrentThreadId() == test_thread_id_);
656  NetTestSuite::Shutdown();
657  OleUninitialize();
658}
659
660void CFUrlRequestUnittestRunner::OnInitialTabLoaded() {
661  test_http_server_.reset();
662  BrowserThread::PostTask(
663      BrowserThread::UI,
664      FROM_HERE,
665      base::Bind(&CFUrlRequestUnittestRunner::CancelInitializationTimeout,
666                 base::Unretained(this)));
667  StartTests();
668}
669
670void CFUrlRequestUnittestRunner::OnProviderDestroyed() {
671  if (tests_ran_) {
672    StopFileLogger(false);
673
674    if (ie_configurator_.get() != NULL)
675      ie_configurator_->RevertSettings();
676
677    if (crash_service_)
678      base::KillProcess(crash_service_, 0, false);
679
680    ::ExitProcess(test_result());
681  } else {
682    DLOG(ERROR) << "Automation Provider shutting down before test execution "
683                   "has completed.";
684  }
685}
686
687void CFUrlRequestUnittestRunner::StartTests() {
688  if (prompt_after_setup_)
689    MessageBoxA(NULL, "click ok to run", "", MB_OK);
690
691  DCHECK_EQ(test_thread_.IsValid(), false);
692  StopFileLogger(false);
693  test_thread_.Set(::CreateThread(NULL, 0, RunAllUnittests, this, 0,
694                                  &test_thread_id_));
695  DCHECK(test_thread_.IsValid());
696}
697
698// static
699DWORD CFUrlRequestUnittestRunner::RunAllUnittests(void* param) {
700  base::PlatformThread::SetName("CFUrlRequestUnittestRunner");
701  CFUrlRequestUnittestRunner* me =
702      reinterpret_cast<CFUrlRequestUnittestRunner*>(param);
703  me->test_result_ = me->Run();
704  me->tests_ran_ = true;
705  BrowserThread::PostTask(
706      BrowserThread::UI,
707      FROM_HERE,
708      base::Bind(&CFUrlRequestUnittestRunner::TakeDownBrowser,
709                 base::Unretained(me)));
710  return 0;
711}
712
713void CFUrlRequestUnittestRunner::TakeDownBrowser() {
714  if (prompt_after_setup_)
715    MessageBoxA(NULL, "click ok to exit", "", MB_OK);
716
717  // Start capturing logs from npchrome_frame and the in-process Chrome to help
718  // diagnose failures in IE shutdown. This will be Stopped in either
719  // OnIEShutdownFailure or OnProviderDestroyed.
720  StartFileLogger();
721
722  // AddRef to ensure that IE going away does not trigger the Chrome shutdown
723  // process.
724  // IE shutting down will, however, trigger the automation channel to shut
725  // down, at which time we will exit the process (see OnProviderDestroyed).
726  g_browser_process->AddRefModule();
727  ShutDownHostBrowser();
728
729  // In case IE is somehow hung, make sure we don't sit around until a try-bot
730  // kills us. OnIEShutdownFailure will log and exit with an error.
731  BrowserThread::PostDelayedTask(
732      BrowserThread::UI,
733      FROM_HERE,
734      base::Bind(&CFUrlRequestUnittestRunner::OnIEShutdownFailure,
735                 base::Unretained(this)),
736      TestTimeouts::action_max_timeout());
737}
738
739void CFUrlRequestUnittestRunner::InitializeLogging() {
740  base::FilePath exe;
741  PathService::Get(base::FILE_EXE, &exe);
742  base::FilePath log_filename = exe.ReplaceExtension(FILE_PATH_LITERAL("log"));
743  logging::LoggingSettings settings;
744  settings.logging_dest = logging::LOG_TO_ALL;
745  settings.log_file = log_filename.value().c_str();
746  settings.delete_old = logging::DELETE_OLD_LOG_FILE;
747  logging::InitLogging(settings);
748  // We want process and thread IDs because we may have multiple processes.
749  // Note: temporarily enabled timestamps in an effort to catch bug 6361.
750  logging::SetLogItems(true, true, true, true);
751}
752
753void CFUrlRequestUnittestRunner::CancelInitializationTimeout() {
754  timeout_closure_.Cancel();
755}
756
757void CFUrlRequestUnittestRunner::StartInitializationTimeout() {
758  timeout_closure_.Reset(
759      base::Bind(&CFUrlRequestUnittestRunner::OnInitializationTimeout,
760                 base::Unretained(this)));
761  base::MessageLoop::current()->PostDelayedTask(
762      FROM_HERE,
763      timeout_closure_.callback(),
764      TestTimeouts::action_max_timeout());
765}
766
767void CFUrlRequestUnittestRunner::OnInitializationTimeout() {
768  LOG(ERROR) << "Failed to start Chrome Frame in the host browser.";
769
770  base::FilePath snapshot;
771  if (ui_test_utils::SaveScreenSnapshotToDesktop(&snapshot))
772    LOG(ERROR) << "Screen snapshot saved to " << snapshot.value();
773
774  StopFileLogger(true);
775
776  if (launch_browser_)
777    chrome_frame_test::CloseAllIEWindows();
778
779  if (ie_configurator_.get() != NULL)
780    ie_configurator_->RevertSettings();
781
782  if (crash_service_)
783    base::KillProcess(crash_service_, 0, false);
784
785  ::ExitProcess(1);
786}
787
788void CFUrlRequestUnittestRunner::OverrideHttpHost() {
789  override_http_host_.reset(
790      new net::ScopedCustomUrlRequestTestHttpHost(
791          chrome_frame_test::GetLocalIPv4Address()));
792}
793
794void CFUrlRequestUnittestRunner::PreEarlyInitialization() {
795  testing::InitGoogleTest(&g_argc, g_argv);
796  FilterDisabledTests();
797  StartFileLogger();
798}
799
800int CFUrlRequestUnittestRunner::PreCreateThreads() {
801  fake_chrome_.reset(new FakeExternalTab());
802  fake_chrome_->Initialize();
803  fake_chrome_->browser_process()->PreCreateThreads();
804  ProcessSingleton::NotificationCallback callback(
805      base::Bind(
806          &CFUrlRequestUnittestRunner::ProcessSingletonNotificationCallback,
807          base::Unretained(this)));
808  process_singleton_.reset(new ProcessSingleton(fake_chrome_->user_data(),
809                                                callback));
810  return 0;
811}
812
813bool CFUrlRequestUnittestRunner::ProcessSingletonNotificationCallback(
814    const CommandLine& command_line, const base::FilePath& current_directory) {
815  std::string channel_id = command_line.GetSwitchValueASCII(
816      switches::kAutomationClientChannelID);
817  EXPECT_FALSE(channel_id.empty());
818
819  Profile* profile = g_browser_process->profile_manager()->GetLastUsedProfile(
820      fake_chrome_->user_data());
821
822  AutomationProviderList* list = g_browser_process->GetAutomationProviderList();
823  DCHECK(list);
824  list->AddProvider(
825      TestAutomationProvider::NewAutomationProvider(profile, channel_id, this));
826  return true;
827}
828
829void CFUrlRequestUnittestRunner::PreMainMessageLoopRun() {
830  fake_chrome_->InitializePostThreadsCreated();
831  // Call Create directly instead of NotifyOtherProcessOrCreate as failure is
832  // prefered to notifying another process here.
833  if (!process_singleton_->Create()) {
834    LOG(FATAL) << "Failed to start up ProcessSingleton. Is another test "
835               << "executable or Chrome Frame running?";
836    if (crash_service_)
837      base::KillProcess(crash_service_, 0, false);
838    ::ExitProcess(1);
839  }
840}
841
842bool CFUrlRequestUnittestRunner::MainMessageLoopRun(int* result_code) {
843  DCHECK(base::MessageLoop::current());
844  DCHECK(base::MessageLoop::current()->type() == base::MessageLoop::TYPE_UI);
845
846  // We need to allow IO on the main thread for these tests.
847  base::ThreadRestrictions::SetIOAllowed(true);
848  StartChromeFrameInHostBrowser();
849  StartInitializationTimeout();
850  return false;
851}
852
853void CFUrlRequestUnittestRunner::PostMainMessageLoopRun() {
854  process_singleton_->Cleanup();
855  fake_chrome_->browser_process()->StartTearDown();
856
857  // Must do this separately as the mock profile_manager_ is not the
858  // same member as BrowserProcessImpl::StartTearDown resets.
859  fake_chrome_->browser_process()->DestroyProfileManager();
860
861  if (crash_service_)
862    base::KillProcess(crash_service_, 0, false);
863
864  base::KillProcesses(chrome_frame_test::kIEImageName, 0, NULL);
865  base::KillProcesses(chrome_frame_test::kIEBrokerImageName, 0, NULL);
866}
867
868void CFUrlRequestUnittestRunner::PostDestroyThreads() {
869  process_singleton_.reset();
870  fake_chrome_->browser_process()->PostDestroyThreads();
871  fake_chrome_->Shutdown();
872  fake_chrome_.reset();
873
874#ifndef NDEBUG
875  // Avoid CRT cleanup in debug test runs to ensure that webkit ASSERTs which
876  // check if globals are created and destroyed on the same thread don't fire.
877  // Webkit global objects are created on the inproc renderer thread.
878  ::ExitProcess(test_result());
879#endif
880}
881
882void CFUrlRequestUnittestRunner::StartFileLogger() {
883  if (file_util::CreateTemporaryFile(&log_file_)) {
884    file_logger_.reset(new logging_win::FileLogger());
885    file_logger_->Initialize();
886    file_logger_->StartLogging(log_file_);
887  } else {
888    LOG(ERROR) << "Failed to create an ETW log file";
889  }
890}
891
892void CFUrlRequestUnittestRunner::StopFileLogger(bool print) {
893  if (file_logger_.get() != NULL && file_logger_->is_logging()) {
894    file_logger_->StopLogging();
895
896    if (print) {
897      // Flushing stdout should prevent unrelated output from being interleaved
898      // with the log file output.
899      std::cout.flush();
900      // Dump the log to stderr.
901      logging_win::PrintLogFile(log_file_, &std::cerr);
902      std::cerr.flush();
903    }
904  }
905
906  if (!log_file_.empty() && !base::DeleteFile(log_file_, false))
907    LOG(ERROR) << "Failed to delete log file " << log_file_.value();
908
909  log_file_.clear();
910  file_logger_.reset();
911}
912
913const char* IEVersionToString(IEVersion version) {
914  switch (version) {
915    case IE_6:
916      return "IE6";
917    case IE_7:
918      return "IE7";
919    case IE_8:
920      return "IE8";
921    case IE_9:
922      return "IE9";
923    case IE_10:
924      return "IE10";
925    case IE_UNSUPPORTED:
926      return "Unknown IE Version";
927    case NON_IE:
928      return "Could not find IE";
929    default:
930      return "Error.";
931  }
932}
933
934content::BrowserMainParts* FakeContentBrowserClient::CreateBrowserMainParts(
935    const content::MainFunctionParams& parameters) {
936  // Normally this would happen during browser startup, but for tests
937  // we need to trigger creation of Profile-related services.
938  ChromeBrowserMainExtraPartsProfiles::
939      EnsureBrowserContextKeyedServiceFactoriesBuilt();
940
941  // We never delete this, as the content module takes ownership.
942  //
943  // We must not construct this earlier, or we will have out-of-order
944  // AtExitManager creation/destruction.
945  g_test_suite = new CFUrlRequestUnittestRunner(g_argc, g_argv);
946  g_test_suite->set_crash_service(chrome_frame_test::StartCrashService());
947  return g_test_suite;
948}
949
950// We must provide a few functions content/ expects to link with.  They
951// are never called for this executable.
952int PluginMain(const content::MainFunctionParams& parameters) {
953  NOTREACHED();
954  return 0;
955}
956
957int PpapiBrokerMain(const content::MainFunctionParams& parameters) {
958  return PluginMain(parameters);
959}
960
961int PpapiPluginMain(const content::MainFunctionParams& parameters) {
962  return PluginMain(parameters);
963}
964
965int WorkerMain(const content::MainFunctionParams& parameters) {
966  return PluginMain(parameters);
967}
968
969int main(int argc, char** argv) {
970  ScopedChromeFrameRegistrar::RegisterAndExitProcessIfDirected();
971  g_argc = argc;
972  g_argv = argv;
973
974  google_breakpad::scoped_ptr<google_breakpad::ExceptionHandler> breakpad(
975      InitializeCrashReporting(HEADLESS));
976
977  // Display the IE version we run with. This must be done after
978  // CFUrlRequestUnittestRunner is constructed since that initializes logging.
979  IEVersion ie_version = chrome_frame_test::GetInstalledIEVersion();
980  LOG(INFO) << "Running CF net tests with IE version: "
981            << IEVersionToString(ie_version);
982
983  // See url_request_unittest.cc for these credentials.
984  SupplyProxyCredentials credentials("user", "secret");
985  WindowWatchdog watchdog;
986  watchdog.AddObserver(&credentials, "Windows Security", "");
987
988  sandbox::SandboxInterfaceInfo sandbox_info = {0};
989  content::InitializeSandboxInfo(&sandbox_info);
990  FakeMainDelegate delegate;
991  content::ContentMain(
992      reinterpret_cast<HINSTANCE>(GetModuleHandle(NULL)),
993      &sandbox_info,
994      &delegate);
995
996  // Note:  In debug builds, we ExitProcess during PostDestroyThreads.
997  return g_test_suite->test_result();
998}
999