// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <string>

#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/i18n/rtl.h"
#include "base/json/json_string_value_serializer.h"
#include "base/md5.h"
#include "base/memory/scoped_ptr.h"
#include "base/scoped_temp_dir.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "base/sys_info.h"
#include "base/test/test_timeouts.h"
#include "base/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/ui/view_ids.h"
#include "chrome/common/automation_messages.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/automation/automation_proxy_uitest.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/automation/proxy_launcher.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/automation/window_proxy.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chrome/test/ui/ui_test.h"
#include "content/test/net/url_request_slow_http_job.h"
#include "net/base/host_port_pair.h"
#include "net/base/net_util.h"
#include "net/test/test_server.h"
#define GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING
#include "testing/gmock_mutant.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/ui_base_switches.h"
#include "ui/base/ui_base_types.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/rect.h"

using ui_test_utils::TimedMessageLoopRunner;
using testing::CreateFunctor;
using testing::StrEq;
using testing::_;


// Replace the default automation proxy with our mock client.
class ExternalTabUITestMockLauncher : public ProxyLauncher {
 public:
  explicit ExternalTabUITestMockLauncher(ExternalTabUITestMockClient **mock)
      : mock_(mock) {
    channel_id_ = AutomationProxy::GenerateChannelID();
  }

  AutomationProxy* CreateAutomationProxy(int execution_timeout) {
    *mock_ = new ExternalTabUITestMockClient(execution_timeout);
    (*mock_)->InitializeChannel(channel_id_, false);
    return *mock_;
  }

  bool InitializeConnection(
      const LaunchState& state,
      bool wait_for_initial_loads) OVERRIDE WARN_UNUSED_RESULT {
    bool launch_browser_and_server =
        LaunchBrowserAndServer(state, wait_for_initial_loads);
    EXPECT_TRUE(launch_browser_and_server);
    return launch_browser_and_server;
  }

  void TerminateConnection() {
    CloseBrowserAndServer();
  }

  std::string PrefixedChannelID() const {
    return channel_id_;
  }

 private:
  ExternalTabUITestMockClient **mock_;
  std::string channel_id_;      // Channel id of automation proxy.
};

class AutomationProxyTest : public UITest {
 protected:
  AutomationProxyTest() {
    dom_automation_enabled_ = true;
    launch_arguments_.AppendSwitchASCII(switches::kLang, "en-US");
  }
};

TEST_F(AutomationProxyTest, GetBrowserWindowCount) {
  int window_count = 0;
  EXPECT_TRUE(automation()->GetBrowserWindowCount(&window_count));
  EXPECT_EQ(1, window_count);
#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
  ASSERT_FALSE(automation()->GetBrowserWindowCount(NULL));
#endif
}

TEST_F(AutomationProxyTest, GetBrowserWindow) {
  {
    scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
    ASSERT_TRUE(window.get());
  }

  {
    scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(-1));
    ASSERT_FALSE(window.get());
  }

  {
    scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(1));
    ASSERT_FALSE(window.get());
  }
};

#if defined(OS_MACOSX)
// Missing automation provider support: http://crbug.com/45892
#define MAYBE_WindowGetViewBounds FAILS_WindowGetViewBounds
#else
#define MAYBE_WindowGetViewBounds WindowGetViewBounds
#endif
TEST_F(AutomationProxyVisibleTest, MAYBE_WindowGetViewBounds) {
  {
    scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
    ASSERT_TRUE(browser.get());
    scoped_refptr<WindowProxy> window(browser->GetWindow());
    ASSERT_TRUE(window.get());

    scoped_refptr<TabProxy> tab1(browser->GetTab(0));
    ASSERT_TRUE(tab1.get());
    GURL tab1_url;
    ASSERT_TRUE(tab1->GetCurrentURL(&tab1_url));

    // Add another tab so we can simulate dragging.
    ASSERT_TRUE(browser->AppendTab(GURL(chrome::kChromeUIVersionURL)));

    scoped_refptr<TabProxy> tab2(browser->GetTab(1));
    ASSERT_TRUE(tab2.get());
    GURL tab2_url;
    ASSERT_TRUE(tab2->GetCurrentURL(&tab2_url));

    EXPECT_NE(tab1_url.spec(), tab2_url.spec());

    gfx::Rect bounds;
    ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_0, &bounds, false));
    EXPECT_GT(bounds.width(), 0);
    EXPECT_GT(bounds.height(), 0);

    gfx::Rect bounds2;
    ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_LAST, &bounds2, false));
    EXPECT_GT(bounds2.x(), 0);
    EXPECT_GT(bounds2.width(), 0);
    EXPECT_GT(bounds2.height(), 0);

    // The tab logic is mirrored in RTL locales, so what is to the right in
    // LTR locales is now on the left with RTL ones.
    string16 browser_locale;

    EXPECT_TRUE(automation()->GetBrowserLocale(&browser_locale));

    const std::string& locale_utf8 = UTF16ToUTF8(browser_locale);
    if (base::i18n::GetTextDirectionForLocale(locale_utf8.c_str()) ==
        base::i18n::RIGHT_TO_LEFT) {
      EXPECT_LT(bounds2.x(), bounds.x());
    } else {
      EXPECT_GT(bounds2.x(), bounds.x());
    }
    EXPECT_EQ(bounds2.y(), bounds.y());

    // Sometimes tests start the browser in full screen mode. Don't check the
    // location bar in such a case.
    bool fullscreen = false;
    if (browser->IsFullscreen(&fullscreen) && !fullscreen) {
      gfx::Rect urlbar_bounds;
      ASSERT_TRUE(window->GetViewBounds(VIEW_ID_LOCATION_BAR, &urlbar_bounds,
                                        false));
      EXPECT_GT(urlbar_bounds.x(), 0);
      EXPECT_GT(urlbar_bounds.y(), 0);
      EXPECT_GT(urlbar_bounds.width(), 0);
      EXPECT_GT(urlbar_bounds.height(), 0);
    }

    /*

    TODO(beng): uncomment this section or move to interactive_ui_tests post
    haste!

    // Now that we know where the tabs are, let's try dragging one.
    POINT start;
    POINT end;
    start.x = bounds.x() + bounds.width() / 2;
    start.y = bounds.y() + bounds.height() / 2;
    end.x = start.x + 2 * bounds.width() / 3;
    end.y = start.y;
    ASSERT_TRUE(browser->SimulateDrag(start, end, ui::EF_LEFT_MOUSE_BUTTON));

    // Check to see that the drag event successfully swapped the two tabs.
    tab1 = browser->GetTab(0);
    ASSERT_TRUE(tab1.get());
    GURL tab1_new_url;
    ASSERT_TRUE(tab1->GetCurrentURL(&tab1_new_url));

    tab2 = browser->GetTab(1);
    ASSERT_TRUE(tab2.get());
    GURL tab2_new_url;
    ASSERT_TRUE(tab2->GetCurrentURL(&tab2_new_url));

    EXPECT_EQ(tab1_url.spec(), tab2_new_url.spec());
    EXPECT_EQ(tab2_url.spec(), tab1_new_url.spec());

    */
  }
}

TEST_F(AutomationProxyTest, GetTabCount) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());

  int tab_count = 0;
  ASSERT_TRUE(window->GetTabCount(&tab_count));
  ASSERT_EQ(1, tab_count);
}

TEST_F(AutomationProxyTest, GetActiveTabIndex) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());

  int active_tab_index = -1;
  ASSERT_TRUE(window->GetActiveTabIndex(&active_tab_index));
  ASSERT_EQ(0, active_tab_index);
}

TEST_F(AutomationProxyVisibleTest, AppendTab) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());

  int original_tab_count;
  ASSERT_TRUE(window->GetTabCount(&original_tab_count));
  ASSERT_EQ(1, original_tab_count);  // By default there are 2 tabs opened.

  int original_active_tab_index;
  ASSERT_TRUE(window->GetActiveTabIndex(&original_active_tab_index));
  ASSERT_EQ(0, original_active_tab_index);  // By default 0-th tab is active

  ASSERT_TRUE(window->AppendTab(GURL("about:blank")));
  int tab_count;
  ASSERT_TRUE(window->GetTabCount(&tab_count));
  ASSERT_EQ(original_tab_count + 1, tab_count);

  int active_tab_index = -1;
  ASSERT_TRUE(window->GetActiveTabIndex(&active_tab_index));
  ASSERT_EQ(tab_count - 1, active_tab_index);
  ASSERT_NE(original_active_tab_index, active_tab_index);

  FilePath filename(test_data_directory_);
  filename = filename.AppendASCII("title2.html");
  ASSERT_TRUE(window->AppendTab(net::FilePathToFileURL(filename)));

  int appended_tab_index;
  // Append tab will also be active tab
  ASSERT_TRUE(window->GetActiveTabIndex(&appended_tab_index));

  scoped_refptr<TabProxy> tab(window->GetTab(appended_tab_index));
  ASSERT_TRUE(tab.get());
  std::wstring title;
  ASSERT_TRUE(tab->GetTabTitle(&title));
  ASSERT_STREQ(L"Title Of Awesomeness", title.c_str());
}

TEST_F(AutomationProxyTest, ActivateTab) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());

  ASSERT_TRUE(window->AppendTab(GURL("about:blank")));

  ASSERT_TRUE(window->ActivateTab(1));
  int active_tab_index = -1;
  ASSERT_TRUE(window->GetActiveTabIndex(&active_tab_index));
  ASSERT_EQ(1, active_tab_index);

  ASSERT_TRUE(window->ActivateTab(0));
  ASSERT_TRUE(window->GetActiveTabIndex(&active_tab_index));
  ASSERT_EQ(0, active_tab_index);
}

TEST_F(AutomationProxyTest, GetTab) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());
  {
    scoped_refptr<TabProxy> tab(window->GetTab(0));
    ASSERT_TRUE(tab.get());
    std::wstring title;
    ASSERT_TRUE(tab->GetTabTitle(&title));
    ASSERT_STREQ(L"about:blank", title.c_str());
  }

  {
    ASSERT_FALSE(window->GetTab(-1));
  }

  {
    scoped_refptr<TabProxy> tab(window->GetTab(1));
    ASSERT_FALSE(tab.get());
  }
};

TEST_F(AutomationProxyTest, NavigateToURL) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());
  scoped_refptr<TabProxy> tab(window->GetTab(0));
  ASSERT_TRUE(tab.get());

  std::wstring title;
  ASSERT_TRUE(tab->GetTabTitle(&title));
  ASSERT_STREQ(L"about:blank", title.c_str());

  FilePath filename(test_data_directory_);
  filename = filename.AppendASCII("title2.html");

  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
            tab->NavigateToURL(net::FilePathToFileURL(filename)));
  ASSERT_TRUE(tab->GetTabTitle(&title));
  ASSERT_STREQ(L"Title Of Awesomeness", title.c_str());

  // TODO(vibhor) : Add a test using testserver.
}

TEST_F(AutomationProxyTest, GoBackForward) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());
  scoped_refptr<TabProxy> tab(window->GetTab(0));
  ASSERT_TRUE(tab.get());

  std::wstring title;
  ASSERT_TRUE(tab->GetTabTitle(&title));
  ASSERT_STREQ(L"about:blank", title.c_str());

  ASSERT_FALSE(tab->GoBack());
  ASSERT_TRUE(tab->GetTabTitle(&title));
  ASSERT_STREQ(L"about:blank", title.c_str());

  FilePath filename(test_data_directory_);
  filename = filename.AppendASCII("title2.html");
  ASSERT_TRUE(tab->NavigateToURL(net::FilePathToFileURL(filename)));
  ASSERT_TRUE(tab->GetTabTitle(&title));
  ASSERT_STREQ(L"Title Of Awesomeness", title.c_str());

  ASSERT_TRUE(tab->GoBack());
  ASSERT_TRUE(tab->GetTabTitle(&title));
  ASSERT_STREQ(L"about:blank", title.c_str());

  ASSERT_TRUE(tab->GoForward());
  ASSERT_TRUE(tab->GetTabTitle(&title));
  ASSERT_STREQ(L"Title Of Awesomeness", title.c_str());

  ASSERT_FALSE(tab->GoForward());
  ASSERT_TRUE(tab->GetTabTitle(&title));
  ASSERT_STREQ(L"Title Of Awesomeness", title.c_str());
}

TEST_F(AutomationProxyTest, GetCurrentURL) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());
  scoped_refptr<TabProxy> tab(window->GetTab(0));
  ASSERT_TRUE(tab.get());
  GURL url;
  ASSERT_TRUE(tab->GetCurrentURL(&url));
  ASSERT_STREQ("about:blank", url.spec().c_str());

  FilePath filename(test_data_directory_);
  filename = filename.AppendASCII("cookie1.html");
  GURL newurl = net::FilePathToFileURL(filename);
  ASSERT_TRUE(tab->NavigateToURL(newurl));
  ASSERT_TRUE(tab->GetCurrentURL(&url));
  // compare canonical urls...
  ASSERT_STREQ(newurl.spec().c_str(), url.spec().c_str());
}

class AutomationProxyTest2 : public AutomationProxyVisibleTest {
 protected:
  AutomationProxyTest2() {
    document1_= test_data_directory_.AppendASCII("title1.html");

    document2_ = test_data_directory_.AppendASCII("title2.html");
    launch_arguments_ = CommandLine(CommandLine::NO_PROGRAM);
    launch_arguments_.AppendArgPath(document1_);
    launch_arguments_.AppendArgPath(document2_);
  }

  FilePath document1_;
  FilePath document2_;
};

TEST_F(AutomationProxyTest2, GetActiveTabIndex) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());

  int active_tab_index = -1;
  ASSERT_TRUE(window->GetActiveTabIndex(&active_tab_index));
  ASSERT_EQ(0, active_tab_index);

  ASSERT_TRUE(window->ActivateTab(1));
  ASSERT_TRUE(window->GetActiveTabIndex(&active_tab_index));
  ASSERT_EQ(1, active_tab_index);
}

// http://crbug.com/98071
#if defined(OS_MACOSX)
#define MAYBE_GetTabTitle DISABLED_GetTabTitle
#else
#define MAYBE_GetTabTitle GetTabTitle
#endif
TEST_F(AutomationProxyTest2, MAYBE_GetTabTitle) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());
  scoped_refptr<TabProxy> tab(window->GetTab(0));
  ASSERT_TRUE(tab.get());
  std::wstring title;
  ASSERT_TRUE(tab->GetTabTitle(&title));
  ASSERT_STREQ(L"title1.html", title.c_str());

  tab = window->GetTab(1);
  ASSERT_TRUE(tab.get());
  ASSERT_TRUE(tab->GetTabTitle(&title));
  ASSERT_STREQ(L"Title Of Awesomeness", title.c_str());
}

TEST_F(AutomationProxyTest, Cookies) {
  GURL url("http://mojo.jojo.google.com");
  std::string value_result;

  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());

  scoped_refptr<TabProxy> tab(window->GetTab(0));
  ASSERT_TRUE(tab.get());

  // test setting the cookie:
  ASSERT_TRUE(tab->SetCookie(url, "foo=baz"));

  ASSERT_TRUE(tab->GetCookieByName(url, "foo", &value_result));
  ASSERT_FALSE(value_result.empty());
  ASSERT_STREQ("baz", value_result.c_str());

  // test clearing the cookie:
  ASSERT_TRUE(tab->SetCookie(url, "foo="));

  ASSERT_TRUE(tab->GetCookieByName(url, "foo", &value_result));
  ASSERT_TRUE(value_result.empty());

  // now, test that we can get multiple cookies:
  ASSERT_TRUE(tab->SetCookie(url, "foo1=baz1"));
  ASSERT_TRUE(tab->SetCookie(url, "foo2=baz2"));

  ASSERT_TRUE(tab->GetCookies(url, &value_result));
  ASSERT_FALSE(value_result.empty());
  EXPECT_TRUE(value_result.find("foo1=baz1") != std::string::npos);
  EXPECT_TRUE(value_result.find("foo2=baz2") != std::string::npos);

  // test deleting cookie
  ASSERT_TRUE(tab->SetCookie(url, "foo3=deleteme"));

  ASSERT_TRUE(tab->GetCookieByName(url, "foo3", &value_result));
  ASSERT_FALSE(value_result.empty());
  ASSERT_STREQ("deleteme", value_result.c_str());

  ASSERT_TRUE(tab->DeleteCookie(url, "foo3"));
}

TEST_F(AutomationProxyTest, NavigateToURLAsync) {
  AutomationProxy* automation_object = automation();
  scoped_refptr<BrowserProxy> window(automation_object->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());
  scoped_refptr<TabProxy> tab(window->GetTab(0));
  ASSERT_TRUE(tab.get());

  FilePath filename(test_data_directory_);
  filename = filename.AppendASCII("cookie1.html");
  GURL newurl = net::FilePathToFileURL(filename);

  ASSERT_TRUE(tab->NavigateToURLAsync(newurl));
  std::string value = WaitUntilCookieNonEmpty(
      tab.get(), newurl, "foo", TestTimeouts::action_max_timeout_ms());
  ASSERT_STREQ("baz", value.c_str());
}

TEST_F(AutomationProxyTest, AcceleratorNewTab) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());

  int tab_count = -1;
  ASSERT_TRUE(window->RunCommand(IDC_NEW_TAB));
  ASSERT_TRUE(window->GetTabCount(&tab_count));
  EXPECT_EQ(2, tab_count);

  scoped_refptr<TabProxy> tab(window->GetTab(1));
  ASSERT_TRUE(tab.get());
}

TEST_F(AutomationProxyTest, AcceleratorDownloads) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());

  ASSERT_TRUE(window->RunCommand(IDC_SHOW_DOWNLOADS));

  // We expect the RunCommand above to wait until the title is updated.
  EXPECT_EQ(L"Downloads", GetActiveTabTitle());
}

// http://crbug.com/109642
#if defined(OS_MACOSX)
#define MAYBE_AcceleratorExtensions DISABLED_AcceleratorExtensions
#else
#define MAYBE_AcceleratorExtensions AcceleratorExtensions
#endif
TEST_F(AutomationProxyTest, MAYBE_AcceleratorExtensions) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());

  ASSERT_TRUE(window->RunCommand(IDC_MANAGE_EXTENSIONS));

  EXPECT_EQ("chrome://chrome/extensions", GetActiveTabURL().spec());
}

TEST_F(AutomationProxyTest, AcceleratorHistory) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());

  ASSERT_TRUE(window->RunCommand(IDC_SHOW_HISTORY));

  EXPECT_EQ(std::string(chrome::kChromeUIUberURL) +
                chrome::kChromeUIHistoryHost + "/",
            GetActiveTabURL().spec());
}

class AutomationProxyTest4 : public UITest {
 protected:
  AutomationProxyTest4() : UITest() {
    dom_automation_enabled_ = true;
  }
};

std::wstring CreateJSString(const std::wstring& value) {
  std::wstring jscript;
  base::SStringPrintf(&jscript,
      L"window.domAutomationController.send(%ls);",
      value.c_str());
  return jscript;
}

TEST_F(AutomationProxyTest4, StringValueIsEchoedByDomAutomationController) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());

  scoped_refptr<TabProxy> tab(window->GetTab(0));
  ASSERT_TRUE(tab.get());

  std::wstring expected(L"string");
  std::wstring jscript = CreateJSString(L"\"" + expected + L"\"");
  std::wstring actual;
  ASSERT_TRUE(tab->ExecuteAndExtractString(L"", jscript, &actual));
  ASSERT_STREQ(expected.c_str(), actual.c_str());
}

std::wstring BooleanToString(bool bool_value) {
  Value* value = Value::CreateBooleanValue(bool_value);
  std::string json_string;
  JSONStringValueSerializer serializer(&json_string);
  serializer.Serialize(*value);
  return UTF8ToWide(json_string);
}

TEST_F(AutomationProxyTest4, BooleanValueIsEchoedByDomAutomationController) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());

  scoped_refptr<TabProxy> tab(window->GetTab(0));
  ASSERT_TRUE(tab.get());

  bool expected = true;
  std::wstring jscript = CreateJSString(BooleanToString(expected));
  bool actual = false;
  ASSERT_TRUE(tab->ExecuteAndExtractBool(L"", jscript, &actual));
  ASSERT_EQ(expected, actual);
}

TEST_F(AutomationProxyTest4, NumberValueIsEchoedByDomAutomationController) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());

  scoped_refptr<TabProxy> tab(window->GetTab(0));
  ASSERT_TRUE(tab.get());

  int expected = 1;
  int actual = 0;
  std::wstring expected_string;
  base::SStringPrintf(&expected_string, L"%d", expected);
  std::wstring jscript = CreateJSString(expected_string);
  ASSERT_TRUE(tab->ExecuteAndExtractInt(L"", jscript, &actual));
  ASSERT_EQ(expected, actual);
}

// TODO(vibhor): Add a test for ExecuteAndExtractValue() for JSON Dictionary
// type value

class AutomationProxyTest3 : public UITest {
 protected:
  AutomationProxyTest3() : UITest() {
    document1_ = test_data_directory_;
    document1_ = document1_.AppendASCII("frame_dom_access");
    document1_ = document1_.AppendASCII("frame_dom_access.html");

    dom_automation_enabled_ = true;
    launch_arguments_ = CommandLine(CommandLine::NO_PROGRAM);
    launch_arguments_.AppendArgPath(document1_);
  }

  FilePath document1_;
};

std::wstring CreateJSStringForDOMQuery(const std::wstring& id) {
  std::wstring jscript(L"window.domAutomationController");
  base::StringAppendF(&jscript,
                      L".send(document.getElementById('%ls').nodeName);",
                      id.c_str());
  return jscript;
}

TEST_F(AutomationProxyTest3, FrameDocumentCanBeAccessed) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());

  scoped_refptr<TabProxy> tab(window->GetTab(0));
  ASSERT_TRUE(tab.get());

  std::wstring actual;
  std::wstring xpath1 = L"";  // top level frame
  std::wstring jscript1 = CreateJSStringForDOMQuery(L"myinput");
  ASSERT_TRUE(tab->ExecuteAndExtractString(xpath1, jscript1, &actual));
  ASSERT_EQ(L"INPUT", actual);

  std::wstring xpath2 = L"/html/body/iframe";
  std::wstring jscript2 = CreateJSStringForDOMQuery(L"myspan");
  ASSERT_TRUE(tab->ExecuteAndExtractString(xpath2, jscript2, &actual));
  ASSERT_EQ(L"SPAN", actual);

  std::wstring xpath3 = L"/html/body/iframe\n/html/body/iframe";
  std::wstring jscript3 = CreateJSStringForDOMQuery(L"mydiv");
  ASSERT_TRUE(tab->ExecuteAndExtractString(xpath3, jscript3, &actual));
  ASSERT_EQ(L"DIV", actual);
}

// Flaky, http://crbug.com/70937
TEST_F(AutomationProxyTest, DISABLED_BlockedPopupTest) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());

  scoped_refptr<TabProxy> tab(window->GetTab(0));
  ASSERT_TRUE(tab.get());

  FilePath filename(test_data_directory_);
  filename = filename.AppendASCII("constrained_files");
  filename = filename.AppendASCII("constrained_window.html");

  ASSERT_TRUE(tab->NavigateToURL(net::FilePathToFileURL(filename)));

  ASSERT_TRUE(tab->WaitForBlockedPopupCountToChangeTo(
      2, TestTimeouts::action_max_timeout_ms()));
}

// TODO(port): Remove HWND if possible
#if defined(OS_WIN)

const char simple_data_url[] =
    "data:text/html,<html><head><title>External tab test</title></head>"
    "<body>A simple page for testing a floating/invisible tab<br></div>"
    "</body></html>";

ExternalTabUITestMockClient::ExternalTabUITestMockClient(int execution_timeout)
    : AutomationProxy(execution_timeout, false),
      host_window_style_(WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_VISIBLE),
      host_window_(NULL) {
}

ExternalTabUITestMockClient::~ExternalTabUITestMockClient() {
  EXPECT_TRUE(host_window_ == NULL);
}

void ExternalTabUITestMockClient::ReplyStarted(
    const AutomationURLResponse* response,
    int tab_handle, int request_id) {
  AutomationProxy::Send(new AutomationMsg_RequestStarted(tab_handle,
      request_id, *response));
}

void ExternalTabUITestMockClient::ReplyData(
    const std::string* data, int tab_handle, int request_id) {
  AutomationProxy::Send(new AutomationMsg_RequestData(tab_handle,
                                                      request_id, *data));
}

void ExternalTabUITestMockClient::ReplyEOF(int tab_handle, int request_id) {
  ReplyEnd(net::URLRequestStatus(), tab_handle, request_id);
}

void ExternalTabUITestMockClient::ReplyEnd(const net::URLRequestStatus& status,
                                           int tab_handle, int request_id) {
  AutomationProxy::Send(new AutomationMsg_RequestEnd(tab_handle,
                                                     request_id, status));
}

void ExternalTabUITestMockClient::Reply404(int tab_handle, int request_id) {
  AutomationURLResponse notfound;
  notfound.headers = "HTTP/1.1 404\r\n\r\n";
  notfound.content_length = 0;
  notfound.redirect_status = 0;

  ReplyStarted(&notfound, tab_handle, request_id);
  ReplyEOF(tab_handle, request_id);
}

void ExternalTabUITestMockClient::ServeHTMLData(int tab_handle,
                                                const GURL& url,
                                                const std::string& data) {
  EXPECT_CALL(*this, OnRequestStart(tab_handle, testing::AllOf(
    testing::Field(&AutomationURLRequest::url, url.spec()),
      testing::Field(&AutomationURLRequest::method, StrEq("GET")))))
    .Times(1)
    .WillOnce(testing::WithArgs<0, 0>(testing::Invoke(CreateFunctor(this,
        &ExternalTabUITestMockClient::ReplyStarted, &GetHttp200Response()))));

  EXPECT_CALL(*this, OnRequestRead(tab_handle, testing::Gt(0)))
      .Times(2)
      .WillOnce(testing::WithArgs<0, 0>(testing::Invoke(CreateFunctor(this,
          &ExternalTabUITestMockClient::ReplyData, &data))))
      .WillOnce(testing::WithArgs<0, 0>(testing::Invoke(CreateFunctor(this,
          &ExternalTabUITestMockClient::ReplyEOF))));
}

void ExternalTabUITestMockClient::IgnoreFaviconNetworkRequest() {
  // Ignore favicon.ico
  EXPECT_CALL(*this, OnRequestStart(_, testing::AllOf(
          testing::Field(&AutomationURLRequest::url,
                         testing::EndsWith("favicon.ico")),
          testing::Field(&AutomationURLRequest::method, StrEq("GET")))))
      .Times(testing::AnyNumber())
      .WillRepeatedly(testing::WithArgs<0, 0>(testing::Invoke(
          CreateFunctor(this, &ExternalTabUITestMockClient::ReplyEnd,
                net::URLRequestStatus(net::URLRequestStatus::FAILED, 0)))));
}

void ExternalTabUITestMockClient::InvalidateHandle(
    const IPC::Message& message) {
  PickleIterator iter(message);
  int handle;
  ASSERT_TRUE(message.ReadInt(&iter, &handle));

  // Call base class
  AutomationProxy::InvalidateHandle(message);
  HandleClosed(handle);
}

// static
ExternalTabSettings ExternalTabUITestMockClient::GetDefaultSettings() {
  ExternalTabSettings settings;
  settings.parent = NULL;
  settings.style = WS_CHILD | WS_VISIBLE;
  settings.is_incognito = false;
  settings.load_requests_via_automation = true;
  settings.handle_top_level_requests = true;
  settings.infobars_enabled = false;
  settings.route_all_top_level_navigations = false;
  return settings;
}

// static
AutomationURLResponse ExternalTabUITestMockClient::GetHttp200Response() {
  AutomationURLResponse response;
  response.headers = "HTTP/0.9 200\r\n\r\n";
  response.content_length = 0;
  response.redirect_status = 0;
  return response;
}

bool ExternalTabUITestMockClient::OnMessageReceived(const IPC::Message& msg) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(ExternalTabUITestMockClient, msg)
    IPC_MESSAGE_HANDLER(AutomationMsg_DidNavigate, OnDidNavigate)
    IPC_MESSAGE_HANDLER(AutomationMsg_ForwardMessageToExternalHost,
        OnForwardMessageToExternalHost)
    IPC_MESSAGE_HANDLER(AutomationMsg_RequestStart, OnRequestStart)
    IPC_MESSAGE_HANDLER(AutomationMsg_RequestRead, OnRequestRead)
    IPC_MESSAGE_HANDLER(AutomationMsg_RequestEnd, OnRequestEnd)
    IPC_MESSAGE_HANDLER(AutomationMsg_SetCookieAsync, OnSetCookieAsync)
    IPC_MESSAGE_HANDLER(AutomationMsg_TabLoaded, OnLoad)
    IPC_MESSAGE_HANDLER(AutomationMsg_AttachExternalTab, OnAttachExternalTab)
    IPC_MESSAGE_HANDLER(AutomationMsg_OpenURL, OnOpenURL)
    IPC_MESSAGE_HANDLER(AutomationMsg_NavigationStateChanged,
                        OnNavigationStateChanged)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()
  return handled;
}

scoped_refptr<TabProxy> ExternalTabUITestMockClient::CreateHostWindowAndTab(
    const ExternalTabSettings& settings) {
  EXPECT_THAT(settings.parent, testing::IsNull());

  host_window_ = CreateWindowW(L"Button", NULL, host_window_style_,
      CW_USEDEFAULT,  CW_USEDEFAULT, CW_USEDEFAULT,  CW_USEDEFAULT,
      NULL, NULL, NULL, NULL);
  EXPECT_THAT(host_window_, testing::Truly(::IsWindow));
  RECT client_area = {0};
  ::GetClientRect(host_window_, &client_area);

  ExternalTabSettings s = settings;
  s.parent = host_window_;
  s.dimensions = client_area;

  HWND container_wnd = NULL;
  HWND tab_wnd = NULL;
  scoped_refptr<TabProxy> tab(CreateExternalTab(s, &container_wnd, &tab_wnd));

  EXPECT_TRUE(tab != NULL);
  EXPECT_NE(FALSE, ::IsWindow(container_wnd));
  EXPECT_NE(FALSE, ::IsWindow(tab_wnd));
  return tab;
}

scoped_refptr<TabProxy> ExternalTabUITestMockClient::CreateTabWithUrl(
    const GURL& initial_url) {
  ExternalTabSettings settings = GetDefaultSettings();
  settings.initial_url = initial_url;
  return CreateHostWindowAndTab(settings);
}

void ExternalTabUITestMockClient::NavigateInExternalTab(int tab_handle,
    const GURL& url, const GURL& referrer /* = GURL()*/) {
  channel_->ChannelProxy::Send(new AutomationMsg_NavigateInExternalTab(
        tab_handle, url, referrer, NULL));
}

void ExternalTabUITestMockClient::ConnectToExternalTab(gfx::NativeWindow parent,
    const AttachExternalTabParams& attach_params) {
  gfx::NativeWindow tab_container = NULL;
  gfx::NativeWindow tab_window = NULL;
  int tab_handle = 0;
  int session_id = -1;

  IPC::SyncMessage* message = new AutomationMsg_ConnectExternalTab(
      attach_params.cookie, true, NULL, &tab_container, &tab_window,
      &tab_handle, &session_id);
  channel_->Send(message);

  RECT rect;
  ::GetClientRect(parent, &rect);
  Reposition_Params params;
  params.window = tab_container;
  params.flags = SWP_NOZORDER | SWP_SHOWWINDOW;
  params.width = rect.right - rect.left;
  params.height = rect.bottom - rect.top;
  params.set_parent = true;
  params.parent_window = parent;

  channel_->Send(new AutomationMsg_TabReposition(tab_handle, params));
  ::ShowWindow(parent, SW_SHOWNORMAL);
}

void ExternalTabUITestMockClient::NavigateThroughUserGesture() {
  ASSERT_THAT(host_window_, testing::Truly(::IsWindow));
  HWND tab_container = ::GetWindow(host_window_, GW_CHILD);
  ASSERT_THAT(tab_container, testing::Truly(::IsWindow));
  HWND tab = ::GetWindow(tab_container, GW_CHILD);
  ASSERT_THAT(tab, testing::Truly(::IsWindow));
  HWND renderer_window = ::GetWindow(tab, GW_CHILD);
  ASSERT_THAT(renderer_window, testing::Truly(::IsWindow));
  ::SetFocus(renderer_window);
  ::PostMessage(renderer_window, WM_KEYDOWN, VK_TAB, 0);
  ::PostMessage(renderer_window, WM_KEYUP, VK_TAB, 0);
  ::PostMessage(renderer_window, WM_KEYDOWN, VK_RETURN, 0);
  ::PostMessage(renderer_window, WM_KEYUP, VK_RETURN, 0);
}

void ExternalTabUITestMockClient::DestroyHostWindow() {
  ::DestroyWindow(host_window_);
  host_window_ = NULL;
}

bool ExternalTabUITestMockClient::HostWindowExists() {
  return (host_window_ != NULL) && ::IsWindow(host_window_);
}

// Handy macro
#define QUIT_LOOP(loop) testing::InvokeWithoutArgs(\
    CreateFunctor(loop, &TimedMessageLoopRunner::Quit))
#define QUIT_LOOP_SOON(loop, ms) testing::InvokeWithoutArgs(\
    CreateFunctor(loop, &TimedMessageLoopRunner::QuitAfter, ms))

template <typename T> T** ReceivePointer(scoped_ptr<T>& p) {  // NOLINT
  return reinterpret_cast<T**>(&p);
}

template <typename T> T** ReceivePointer(scoped_refptr<T>& p) {  // NOLINT
  return reinterpret_cast<T**>(&p);
}

ExternalTabUITest::ExternalTabUITest() : UITest(MessageLoop::TYPE_UI) {}

// Replace the default automation proxy with our mock client.
ProxyLauncher* ExternalTabUITest::CreateProxyLauncher() {
  return new ExternalTabUITestMockLauncher(&mock_);
}

// Create with specifying a url
// Flaky, http://crbug.com/32293
TEST_F(ExternalTabUITest, DISABLED_CreateExternalTab1) {
  scoped_refptr<TabProxy> tab;
  TimedMessageLoopRunner loop(MessageLoop::current());
  ASSERT_THAT(mock_, testing::NotNull());

  EXPECT_CALL(*mock_, OnNavigationStateChanged(_, _))
      .Times(testing::AnyNumber());

  EXPECT_CALL(*mock_, OnDidNavigate(testing::_))
      .Times(1)
      .WillOnce(testing::InvokeWithoutArgs(mock_,
                &ExternalTabUITestMockClient::DestroyHostWindow));

  EXPECT_CALL(*mock_, HandleClosed(1))
      .Times(1)
      .WillOnce(QUIT_LOOP(&loop));

  tab = mock_->CreateTabWithUrl(GURL(simple_data_url));
  loop.RunFor(TestTimeouts::action_max_timeout_ms());
}

// Create with empty url and then navigate
// Flaky, http://crbug.com/32293
TEST_F(ExternalTabUITest, DISABLED_CreateExternalTab2) {
  scoped_refptr<TabProxy> tab;
  TimedMessageLoopRunner loop(MessageLoop::current());
  ASSERT_THAT(mock_, testing::NotNull());

  EXPECT_CALL(*mock_, OnNavigationStateChanged(_, _))
      .Times(testing::AnyNumber());

  EXPECT_CALL(*mock_, OnDidNavigate(testing::_))
    .Times(1)
    .WillOnce(testing::InvokeWithoutArgs(mock_,
              &ExternalTabUITestMockClient::DestroyHostWindow));

  EXPECT_CALL(*mock_, HandleClosed(1))
    .Times(1)
    .WillOnce(QUIT_LOOP(&loop));

  tab = mock_->CreateTabWithUrl(GURL());
  mock_->NavigateInExternalTab(tab->handle(), GURL(simple_data_url));
  loop.RunFor(TestTimeouts::action_max_timeout_ms());
}

// FLAKY: http://crbug.com/60409
TEST_F(ExternalTabUITest, DISABLED_IncognitoMode) {
  scoped_refptr<TabProxy> tab;
  TimedMessageLoopRunner loop(MessageLoop::current());
  ASSERT_THAT(mock_, testing::NotNull());

  GURL url("http://anatomyofmelancholy.net");
  std::string cookie = "robert=burton; expires=Thu, 13 Oct 2011 05:04:03 UTC;";

  EXPECT_CALL(*mock_, HandleClosed(1)).Times(1);

  ExternalTabSettings incognito =
      ExternalTabUITestMockClient::GetDefaultSettings();
  incognito.is_incognito = true;
  // SetCookie is a sync call and deadlock can happen if window is visible,
  // since it shares same thread with AutomationProxy.
  mock_->host_window_style_ &= ~WS_VISIBLE;
  tab = mock_->CreateHostWindowAndTab(incognito);
  std::string value_result;

  EXPECT_TRUE(tab->SetCookie(url, cookie));
  EXPECT_TRUE(tab->GetCookieByName(url, "robert", &value_result));
  EXPECT_EQ("burton", value_result);
  mock_->DestroyHostWindow();
  CloseBrowserAndServer();
  tab = NULL;

  value_result.clear();
  clear_profile_ = false;
  LaunchBrowserAndServer();
  // SetCookie is a sync call and deadlock can happen if window is visible,
  // since it shares same thread with AutomationProxy.
  mock_->host_window_style_ &= ~WS_VISIBLE;
  tab = mock_->CreateTabWithUrl(GURL());
  EXPECT_TRUE(tab->GetCookieByName(url, "robert", &value_result));
  EXPECT_EQ("", value_result);
  EXPECT_CALL(*mock_, HandleClosed(1)).Times(1);
  mock_->DestroyHostWindow();
  CloseBrowserAndServer();
  tab = NULL;
}

// FLAKY: http://crbug.com/44617
TEST_F(ExternalTabUITest, DISABLED_TabPostMessage) {
  scoped_refptr<TabProxy> tab;
  TimedMessageLoopRunner loop(MessageLoop::current());
  ASSERT_THAT(mock_, testing::NotNull());

  EXPECT_CALL(*mock_, OnNavigationStateChanged(_, _))
      .Times(testing::AnyNumber());
  EXPECT_CALL(*mock_, OnLoad(_)).Times(testing::AnyNumber());

  std::string content =
      "data:text/html,<html><head><script>"
      "function onload() {"
      "  window.externalHost.onmessage = onMessage;"
      "}"
      "function onMessage(evt) {"
      "  window.externalHost.postMessage(evt.data, '*');"
      "}"
      "</script></head>"
      "<body onload='onload()'>external tab test<br></div>"
      "</body></html>";

  EXPECT_CALL(*mock_, OnDidNavigate(testing::_))
      .Times(1)
      .WillOnce(testing::InvokeWithoutArgs(CreateFunctor(
          ReceivePointer(tab),
          &TabProxy::HandleMessageFromExternalHost,
          std::string("Hello from gtest"),
          std::string("null"), std::string("*"))));

  EXPECT_CALL(*mock_, OnForwardMessageToExternalHost(
          testing::StrEq("Hello from gtest"), testing::_, testing::_))
      .Times(1)
      .WillOnce(testing::DoAll(
          testing::InvokeWithoutArgs(CreateFunctor(mock_,
              &ExternalTabUITestMockClient::DestroyHostWindow)),
          QUIT_LOOP_SOON(&loop, 50)));

  EXPECT_CALL(*mock_, HandleClosed(1)).Times(1);

  tab = mock_->CreateTabWithUrl(GURL(content));
  loop.RunFor(TestTimeouts::action_max_timeout_ms());
}

// Flaky: http://crbug.com/62143
TEST_F(ExternalTabUITest, DISABLED_PostMessageTarget)  {
  net::TestServer test_server(
      net::TestServer::TYPE_HTTP,
      net::TestServer::kLocalhost,
      FilePath(FILE_PATH_LITERAL("chrome/test/data/external_tab")));
  ASSERT_TRUE(test_server.Start());

  scoped_refptr<TabProxy> tab;
  TimedMessageLoopRunner loop(MessageLoop::current());
  ASSERT_THAT(mock_, testing::NotNull());
  EXPECT_CALL(*mock_, OnNavigationStateChanged(_, _))
      .Times(testing::AnyNumber());
  EXPECT_CALL(*mock_, OnLoad(_)).Times(testing::AnyNumber());

  std::string kTestMessage("Hello from gtest");
  std::string kTestOrigin("http://www.external.tab");

  EXPECT_CALL(*mock_, OnDidNavigate(testing::_))
      .Times(1)
      .WillOnce(testing::InvokeWithoutArgs(CreateFunctor(
          ReceivePointer(tab),
          &TabProxy::HandleMessageFromExternalHost,
          kTestMessage, kTestOrigin, std::string("http://localhost:1337/"))));

  EXPECT_CALL(*mock_, OnForwardMessageToExternalHost(
                    testing::StrEq(kTestMessage),
                    testing::_,
                    testing::StrEq(GURL(kTestOrigin).GetOrigin().spec())))
      .Times(1)
      .WillOnce(testing::DoAll(
          testing::InvokeWithoutArgs(CreateFunctor(mock_,
              &ExternalTabUITestMockClient::DestroyHostWindow)),
          QUIT_LOOP_SOON(&loop, 50)));

  EXPECT_CALL(*mock_, HandleClosed(1)).Times(1);

  ExternalTabSettings s = ExternalTabUITestMockClient::GetDefaultSettings();
  s.load_requests_via_automation = false;
  s.initial_url = GURL("http://localhost:1337/files/post_message.html");
  tab = mock_->CreateHostWindowAndTab(s);
  loop.RunFor(TestTimeouts::action_max_timeout_ms());
}

// Disabled, http://crbug.com/42545.
TEST_F(ExternalTabUITest, DISABLED_HostNetworkStack) {
  scoped_refptr<TabProxy> tab;
  TimedMessageLoopRunner loop(MessageLoop::current());
  ASSERT_THAT(mock_, testing::NotNull());
  EXPECT_CALL(*mock_, OnNavigationStateChanged(_, _))
      .Times(testing::AnyNumber());
  EXPECT_CALL(*mock_, OnLoad(_)).Times(testing::AnyNumber());

  std::string url = "http://placetogo.org";

  testing::InSequence sequence;
  EXPECT_CALL(*mock_, OnRequestStart(2, testing::AllOf(
          testing::Field(&AutomationURLRequest::url, StrEq(url + "/")),
          testing::Field(&AutomationURLRequest::method, StrEq("GET")))))
      .Times(1)
      // We can simply do CreateFunctor(1, 2, &GetHttp200Response()) since we
      // know the tab handle and request id, but using WithArgs<> is much more
      // fancy :)
      .WillOnce(testing::WithArgs<0, 0>(testing::Invoke(CreateFunctor(mock_,
          &ExternalTabUITestMockClient::ReplyStarted,
          &ExternalTabUITestMockClient::GetHttp200Response()))));

  // Return some trivial page, that have a link to a "logo.gif" image
  const std::string data = "<!DOCTYPE html><title>Hello</title>"
                           "<img src=\"logo.gif\">";

  EXPECT_CALL(*mock_, OnRequestRead(2, testing::Gt(0)))
      .Times(2)
      .WillOnce(testing::InvokeWithoutArgs(CreateFunctor(mock_,
          &ExternalTabUITestMockClient::ReplyData, &data, 1, 2)))
      .WillOnce(testing::WithArgs<0, 0>(testing::Invoke(CreateFunctor(mock_,
          &ExternalTabUITestMockClient::ReplyEOF))));

  // Expect navigation is ok.
  EXPECT_CALL(*mock_, OnDidNavigate(testing::Field(&NavigationInfo::url,
                                                   GURL(url))))
      .Times(1);

  // Expect GET request for logo.gif
  EXPECT_CALL(*mock_, OnRequestStart(3, testing::AllOf(
      testing::Field(&AutomationURLRequest::url, StrEq(url + "/logo.gif")),
      testing::Field(&AutomationURLRequest::method, StrEq("GET")))))
      .Times(1)
      .WillOnce(testing::InvokeWithoutArgs(CreateFunctor(mock_,
          &ExternalTabUITestMockClient::Reply404, 1, 3)));

  EXPECT_CALL(*mock_, OnRequestRead(3, testing::Gt(0)))
    .Times(1);

  // Chrome makes a brave request for favicon.ico
  EXPECT_CALL(*mock_, OnRequestStart(4, testing::AllOf(
      testing::Field(&AutomationURLRequest::url,
                     StrEq(url + "/favicon.ico")),
      testing::Field(&AutomationURLRequest::method, StrEq("GET")))))
      .Times(1)
      .WillOnce(testing::DoAll(
          testing::InvokeWithoutArgs(CreateFunctor(mock_,
              &ExternalTabUITestMockClient::Reply404, 1, 4)),
          testing::InvokeWithoutArgs(CreateFunctor(mock_,
              &ExternalTabUITestMockClient::DestroyHostWindow))));

  EXPECT_CALL(*mock_, HandleClosed(1)).Times(1);

  EXPECT_CALL(*mock_, OnRequestRead(4, testing::Gt(0)))
    .Times(1)
    .WillOnce(QUIT_LOOP_SOON(&loop, 300));

  tab = mock_->CreateTabWithUrl(GURL(url));
  loop.RunFor(TestTimeouts::action_max_timeout_ms());
}

// Disabled, http://crbug.com/61023.
TEST_F(ExternalTabUITest, DISABLED_HostNetworkStackAbortRequest) {
  scoped_refptr<TabProxy> tab;
  TimedMessageLoopRunner loop(MessageLoop::current());
  ASSERT_THAT(mock_, testing::NotNull());
  EXPECT_CALL(*mock_, OnNavigationStateChanged(_, _))
      .Times(testing::AnyNumber());

  std::string url = "http://placetogo.org";

  testing::InSequence sequence;
  EXPECT_CALL(*mock_, OnRequestStart(2, testing::AllOf(
          testing::Field(&AutomationURLRequest::url, StrEq(url + "/")),
          testing::Field(&AutomationURLRequest::method, StrEq("GET")))))
      .Times(1)
      // We can simply do CreateFunctor(1, 2, &GetHttp200Response()) since we
      // know the tab handle and request id, but using WithArgs<> is much more
      // fancy :)
      .WillOnce(testing::WithArgs<0, 0>(testing::Invoke(CreateFunctor(mock_,
          &ExternalTabUITestMockClient::ReplyStarted,
          &ExternalTabUITestMockClient::GetHttp200Response()))));

  // Return some trivial page, that have a link to a "logo.gif" image
  const std::string data = "<!DOCTYPE html><title>Hello";

  EXPECT_CALL(*mock_, OnRequestRead(2, testing::Gt(0)))
      .Times(2)
      .WillOnce(testing::InvokeWithoutArgs(CreateFunctor(mock_,
          &ExternalTabUITestMockClient::ReplyData, &data, 1, 2)))
      .WillOnce(testing::WithArgs<0, 0>(
          testing::InvokeWithoutArgs(CreateFunctor(mock_,
              &ExternalTabUITestMockClient::DestroyHostWindow))));

  EXPECT_CALL(*mock_, HandleClosed(1)).Times(1);

  EXPECT_CALL(*mock_, OnRequestEnd(2, testing::_))
      .Times(1)
      .WillOnce(QUIT_LOOP_SOON(&loop, 300));

  tab = mock_->CreateTabWithUrl(GURL(url));
  loop.RunFor(TestTimeouts::action_max_timeout_ms());
}

// Disabled, http://crbug.com/61023.
TEST_F(ExternalTabUITest, DISABLED_HostNetworkStackUnresponsiveRenderer) {
  scoped_refptr<TabProxy> tab;
  TimedMessageLoopRunner loop(MessageLoop::current());
  ASSERT_THAT(mock_, testing::NotNull());
  EXPECT_CALL(*mock_, OnNavigationStateChanged(_, _))
      .Times(testing::AnyNumber());
  EXPECT_CALL(*mock_, OnLoad(_)).Times(testing::AnyNumber());

  std::string url = "http://placetogo.org";

  EXPECT_CALL(*mock_, OnRequestStart(3, testing::_))
      .Times(testing::AnyNumber());
  EXPECT_CALL(*mock_, OnDidNavigate(_)).Times(testing::AnyNumber());

  testing::InSequence sequence;
  EXPECT_CALL(*mock_, OnRequestStart(2, testing::AllOf(
          testing::Field(&AutomationURLRequest::url, StrEq(url + "/")),
          testing::Field(&AutomationURLRequest::method, StrEq("GET")))))
      .Times(1)
      // We can simply do CreateFunctor(1, 2, &GetHttp200Response()) since we
      // know the tab handle and request id, but using WithArgs<> is much more
      // fancy :)
      .WillOnce(testing::WithArgs<0, 0>(testing::Invoke(CreateFunctor(mock_,
          &ExternalTabUITestMockClient::ReplyStarted,
          &ExternalTabUITestMockClient::GetHttp200Response()))));

  const std::string head = "<html><title>Hello</title><body>";

  const std::string data = "<table border=\"1\"><tr><th>Month</th>"
                           "<th>Savings</th></tr><tr><td>January</td>"
                           "<td>$100</td></tr><tr><td>February</td>"
                           "<td>$100</td></tr><tr><td>March</td>"
                           "<td>$100</td></tr><tr><td>April</td>"
                           "<td>$100</td></tr><tr><td>May</td>"
                           "<td>$100</td></tr><tr><td>June</td>"
                           "<td>$100</td></tr><tr><td>July</td>"
                           "<td>$100</td></tr><tr><td>Aug</td>"
                           "<td>$100</td></tr><tr><td>Sept</td>"
                           "<td>$100</td></tr><tr><td>Oct</td>"
                           "<td>$100</td></tr><tr><td>Nov</td>"
                           "<td>$100</td></tr><tr><td>Dec</td>"
                           "<td>$100</td></tr></table>";

  const std::string tail = "</body></html>";

  EXPECT_CALL(*mock_, OnRequestRead(2, testing::Gt(0)))
      .Times(1)
      .WillOnce(testing::InvokeWithoutArgs(CreateFunctor(mock_,
          &ExternalTabUITestMockClient::ReplyData, &head, 1, 2)));

  EXPECT_CALL(*mock_, OnRequestRead(2, testing::Gt(0)))
      .Times(100)
      .WillRepeatedly(testing::InvokeWithoutArgs(CreateFunctor(mock_,
              &ExternalTabUITestMockClient::ReplyData, &data, 1, 2)));

  EXPECT_CALL(*mock_, OnRequestRead(2, testing::Gt(0)))
      .Times(testing::AnyNumber())
      .WillOnce(testing::DoAll(
          testing::InvokeWithoutArgs(CreateFunctor(mock_,
              &ExternalTabUITestMockClient::ReplyData, &tail, 1, 2)),
          testing::InvokeWithoutArgs(CreateFunctor(mock_,
              &ExternalTabUITestMockClient::ReplyEOF, 1, 2)),
          QUIT_LOOP_SOON(&loop, 300)));
  EXPECT_CALL(*mock_, HandleClosed(1)).Times(1);

  tab = mock_->CreateTabWithUrl(GURL(url));
  loop.RunFor(TestTimeouts::action_max_timeout_ms());
  mock_->DestroyHostWindow();
}

class ExternalTabUITestPopupEnabled : public ExternalTabUITest {
 public:
  ExternalTabUITestPopupEnabled() {
    launch_arguments_.AppendSwitch(switches::kDisablePopupBlocking);
  }
};

#if defined(OS_WIN)
// http://crbug.com/61023 - Fails on one popular operating system.
#define MAYBE_WindowDotOpen DISABLED_WindowDotOpen
#define MAYBE_UserGestureTargetBlank DISABLED_UserGestureTargetBlank
#else
#define MAYBE_WindowDotOpen WindowDotOpen
#define MAYBE_UserGestureTargetBlank UserGestureTargetBlank
#endif

// Testing AutomationMsg_AttachExternalTab callback from Chrome.
// Open a popup window with window.open() call. The created popup window opens
// another popup window (again using window.open() call).
TEST_F(ExternalTabUITestPopupEnabled, MAYBE_WindowDotOpen) {
  TimedMessageLoopRunner loop(MessageLoop::current());
  ASSERT_THAT(mock_, testing::NotNull());
  mock_->IgnoreFaviconNetworkRequest();
  // Ignore navigation state changes.
  EXPECT_CALL(*mock_, OnNavigationStateChanged(_, _))
      .Times(testing::AnyNumber());
  EXPECT_CALL(*mock_, OnDidNavigate(_)).Times(testing::AnyNumber());

  GURL main_url("http://placetogo.com/");
  std::string main_html =
    "<html><head><script type='text/javascript' language='JavaScript'>"
    "window.open('popup1.html','','toolbar=no,menubar=no,location=yes,"
    "height=320,width=300,left=1');"
    "</script></head><body>Main.</body></html>";
  mock_->ServeHTMLData(1, main_url, main_html);
  EXPECT_CALL(*mock_, OnLoad(_)).Times(1);

  GURL popup1_url("http://placetogo.com/popup1.html");
  std::string popup1_html =
    "<html><head><script type='text/javascript' language='JavaScript'>"
    "window.open('popup2.html','','');"
    "</script></head><body>Popup1.</body></html>";
  mock_->ServeHTMLData(2, popup1_url, popup1_html);
  EXPECT_CALL(*mock_, OnLoad(_)).Times(1);

  GURL popup2_url("http://placetogo.com/popup2.html");
  std::string popup2_html = "<html><body>Popup2.</body></html>";
  mock_->ServeHTMLData(3, popup2_url, popup2_html);
  EXPECT_CALL(*mock_, OnLoad(_)).Times(1)
      .WillOnce(QUIT_LOOP_SOON(&loop, 500));

  DWORD style = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN;
  HWND popup1_host = CreateWindowW(L"Button", L"popup1_host", style,
      CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
      NULL, NULL, NULL, NULL);

  HWND popup2_host = CreateWindowW(L"Button", L"popup2_host", style,
      CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,  CW_USEDEFAULT,
      NULL, NULL, NULL, NULL);

  EXPECT_CALL(*mock_, OnAttachExternalTab(_))
      .Times(1)
      .WillOnce(testing::WithArgs<0>(testing::Invoke(CreateFunctor(mock_,
          &ExternalTabUITestMockClient::ConnectToExternalTab, popup1_host))));

  EXPECT_CALL(*mock_, OnAttachExternalTab(_))
    .Times(1)
    .WillOnce(testing::WithArgs<0>(testing::Invoke(CreateFunctor(mock_,
        &ExternalTabUITestMockClient::ConnectToExternalTab, popup2_host))));

  mock_->CreateTabWithUrl(main_url);

  loop.RunFor(TestTimeouts::action_max_timeout_ms());

  EXPECT_CALL(*mock_, HandleClosed(1));
  EXPECT_CALL(*mock_, HandleClosed(2));
  EXPECT_CALL(*mock_, HandleClosed(3));

  mock_->DestroyHostWindow();
  ::DestroyWindow(popup1_host);
  ::DestroyWindow(popup2_host);
}

// Open a new window by simulating a user gesture through keyboard.
TEST_F(ExternalTabUITestPopupEnabled, MAYBE_UserGestureTargetBlank) {
  TimedMessageLoopRunner loop(MessageLoop::current());
  ASSERT_THAT(mock_, testing::NotNull());
  mock_->IgnoreFaviconNetworkRequest();
  // Ignore navigation state changes.
  EXPECT_CALL(*mock_, OnNavigationStateChanged(_, _))
      .Times(testing::AnyNumber());
  EXPECT_CALL(*mock_, OnDidNavigate(_)).Times(testing::AnyNumber());

  GURL main_url("http://placetogo.com/");
  std::string main_html = "<!DOCTYPE html><title>Hello</title>"
      "<a href='http://foo.com/' target='_blank'>Link</a>";
  mock_->ServeHTMLData(1, main_url, main_html);

  HWND foo_host = CreateWindowW(L"Button", L"foo_host",
      WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT,
      CW_USEDEFAULT,  CW_USEDEFAULT, NULL, NULL, NULL, NULL);

  testing::InSequence s;
  EXPECT_CALL(*mock_, OnLoad(_))
      .WillOnce(testing::InvokeWithoutArgs(testing::CreateFunctor(mock_,
          &ExternalTabUITestMockClient::NavigateThroughUserGesture)));

  EXPECT_CALL(*mock_, OnAttachExternalTab(_))
      .Times(1)
      .WillOnce(QUIT_LOOP_SOON(&loop, 500));

  mock_->CreateTabWithUrl(main_url);
  loop.RunFor(TestTimeouts::action_max_timeout_ms());

  EXPECT_CALL(*mock_, HandleClosed(1));
  ::DestroyWindow(foo_host);
  mock_->DestroyHostWindow();
}
#endif  // defined(OS_WIN)

// Flaky especially on Windows. See crbug.com/25039.
TEST_F(AutomationProxyTest, DISABLED_AppModalDialogTest) {
  scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser.get());
  scoped_refptr<TabProxy> tab(browser->GetTab(0));
  ASSERT_TRUE(tab.get());

  bool modal_dialog_showing = false;
  ui::DialogButton button = ui::DIALOG_BUTTON_NONE;
  EXPECT_TRUE(automation()->GetShowingAppModalDialog(&modal_dialog_showing,
                                                     &button));
  EXPECT_FALSE(modal_dialog_showing);
  EXPECT_EQ(ui::DIALOG_BUTTON_NONE, button);

  // Show a simple alert.
  std::string content =
      "data:text/html,<html><head><script>function onload() {"
      "setTimeout(\"alert('hello');\", 1000); }</script></head>"
      "<body onload='onload()'></body></html>";
  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
            tab->NavigateToURL(GURL(content)));
  EXPECT_TRUE(automation()->WaitForAppModalDialog());
  EXPECT_TRUE(automation()->GetShowingAppModalDialog(&modal_dialog_showing,
                                                     &button));
  EXPECT_TRUE(modal_dialog_showing);
  EXPECT_EQ(ui::DIALOG_BUTTON_OK, button);

  // Test that clicking missing button fails graciously and does not close the
  // dialog.
  EXPECT_FALSE(automation()->ClickAppModalDialogButton(
      ui::DIALOG_BUTTON_CANCEL));
  EXPECT_TRUE(automation()->GetShowingAppModalDialog(&modal_dialog_showing,
                                                     &button));
  EXPECT_TRUE(modal_dialog_showing);

  // Now click OK, that should close the dialog.
  EXPECT_TRUE(automation()->ClickAppModalDialogButton(
      ui::DIALOG_BUTTON_OK));
  EXPECT_TRUE(automation()->GetShowingAppModalDialog(&modal_dialog_showing,
                                                     &button));
  EXPECT_FALSE(modal_dialog_showing);

  // Show a confirm dialog.
  content =
      "data:text/html,<html><head><script>var result = -1; function onload() {"
      "setTimeout(\"result = confirm('hello') ? 0 : 1;\", 1000);} </script>"
      "</head><body onload='onload()'></body></html>";
  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
            tab->NavigateToURL(GURL(content)));
  EXPECT_TRUE(automation()->WaitForAppModalDialog());
  EXPECT_TRUE(automation()->GetShowingAppModalDialog(&modal_dialog_showing,
                                                     &button));
  EXPECT_TRUE(modal_dialog_showing);
  EXPECT_EQ(ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL, button);

  // Click OK.
  EXPECT_TRUE(automation()->ClickAppModalDialogButton(ui::DIALOG_BUTTON_OK));
  EXPECT_TRUE(automation()->GetShowingAppModalDialog(&modal_dialog_showing,
                                                     &button));
  EXPECT_FALSE(modal_dialog_showing);
  int result = -1;
  EXPECT_TRUE(tab->ExecuteAndExtractInt(
      std::wstring(),
      L"window.domAutomationController.send(result);", &result));
  EXPECT_EQ(0, result);

  // Try again.
  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
            tab->NavigateToURL(GURL(content)));
  EXPECT_TRUE(automation()->WaitForAppModalDialog());
  EXPECT_TRUE(automation()->GetShowingAppModalDialog(&modal_dialog_showing,
                                                     &button));
  EXPECT_TRUE(modal_dialog_showing);
  EXPECT_EQ(ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL, button);

  // Click Cancel this time.
  EXPECT_TRUE(automation()->ClickAppModalDialogButton(
      ui::DIALOG_BUTTON_CANCEL));
  EXPECT_TRUE(automation()->GetShowingAppModalDialog(&modal_dialog_showing,
                                                     &button));
  EXPECT_FALSE(modal_dialog_showing);
  EXPECT_TRUE(tab->ExecuteAndExtractInt(
      std::wstring(),
      L"window.domAutomationController.send(result);", &result));
  EXPECT_EQ(1, result);
}

class AutomationProxyTest5 : public UITest {
 protected:
  AutomationProxyTest5() {
    show_window_ = true;
    dom_automation_enabled_ = true;
    // We need to disable popup blocking to ensure that the RenderView
    // instance for the popup actually closes.
    launch_arguments_.AppendSwitch(switches::kDisablePopupBlocking);
  }
};

TEST_F(AutomationProxyTest5, TestLifetimeOfDomAutomationController) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());

  scoped_refptr<TabProxy> tab(window->GetTab(0));
  ASSERT_TRUE(tab.get());

  FilePath filename(test_data_directory_);
  filename = filename.AppendASCII("dom_automation_test_with_popup.html");

  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
            tab->NavigateToURL(net::FilePathToFileURL(filename)));

  // Allow some time for the popup to show up and close.
  base::PlatformThread::Sleep(TestTimeouts::action_timeout());

  std::wstring expected(L"string");
  std::wstring jscript = CreateJSString(L"\"" + expected + L"\"");
  std::wstring actual;
  ASSERT_TRUE(tab->ExecuteAndExtractString(L"", jscript, &actual));
  ASSERT_EQ(expected, actual);
}

class AutomationProxySnapshotTest : public UITest {
 protected:
  AutomationProxySnapshotTest() {
    dom_automation_enabled_ = true;
    if (!snapshot_dir_.CreateUniqueTempDir())
      ADD_FAILURE() << "Unable to create temporary directory";
    else
      snapshot_path_ = snapshot_dir_.path().AppendASCII("snapshot.png");
  }

  // Asserts that the given png file can be read and decoded into the given
  // bitmap.
  void AssertReadPNG(const FilePath& filename, std::string* data) {
    ASSERT_TRUE(file_util::PathExists(filename));

    int64 size64;
    ASSERT_TRUE(file_util::GetFileSize(filename, &size64));
    // Check that the file is not too big to read in (less than 100 MB).
    ASSERT_LT(size64, 1024 * 1024 * 100);

    // Read and decode image.
    int size = static_cast<int>(size64);
    data->resize(size);
    int bytes_read = file_util::ReadFile(filename, &(*data)[0], size);
    ASSERT_EQ(size, bytes_read);
  }

  // Returns the file path for the directory for these tests appended with
  // the given relative path.
  FilePath GetTestFilePath(const std::string& relative_path) {
    FilePath filename(test_data_directory_);
    return filename.AppendASCII("automation_proxy_snapshot")
        .AppendASCII(relative_path);
  }

  GURL GetTestUrl(const std::string& relative_path, const std::string& query) {
    FilePath file_path = GetTestFilePath(relative_path);
    return ui_test_utils::GetFileUrlWithQuery(file_path, query);
  }

  FilePath snapshot_path_;
  ScopedTempDir snapshot_dir_;
};

// Tests that taking a snapshot when the content is larger than the view
// produces a snapshot equal to the content size.
TEST_F(AutomationProxySnapshotTest, ContentLargerThanView) {
  const char kReferenceMd5[] = "3d594850fd25cb116338cb3610afe18e";
  scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser.get());
  scoped_refptr<TabProxy> tab(browser->GetTab(0));
  ASSERT_TRUE(tab.get());

  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
            tab->NavigateToURL(GetTestUrl("set_size.html", "2000,2500")));

  ASSERT_TRUE(tab->CaptureEntirePageAsPNG(snapshot_path_));

  std::string data;
  ASSERT_NO_FATAL_FAILURE(AssertReadPNG(snapshot_path_, &data));
  EXPECT_STREQ(kReferenceMd5, base::MD5String(data).c_str());
  if (CommandLine::ForCurrentProcess()->HasSwitch("dump-test-image")) {
    FilePath path(FILE_PATH_LITERAL("snapshot.png"));
    EXPECT_EQ(file_util::WriteFile(path, &data[0], data.length()),
              static_cast<int>(data.length()));
  }
}