// Copyright (c) 2010 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 "chrome/browser/renderer_host/test/test_render_view_host.h"

#include "chrome/app/chrome_dll_resource.h"
#include "chrome/browser/renderer_host/mock_render_process_host.h"
#include "chrome/browser/tab_contents/render_view_context_menu.h"
#include "chrome/browser/translate/translate_infobar_delegate2.h"
#include "chrome/browser/translate/translate_manager2.h"
#include "chrome/browser/translate/translate_prefs.h"
#include "chrome/common/ipc_test_sink.h"
#include "chrome/common/notification_details.h"
#include "chrome/common/notification_observer_mock.h"
#include "chrome/common/notification_registrar.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/notification_type.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/render_messages.h"
#include "chrome/common/net/test_url_fetcher_factory.h"
#include "chrome/test/testing_browser_process.h"
#include "grit/generated_resources.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/cld/languages/public/languages.h"

using testing::_;
using testing::Pointee;
using testing::Property;

class TestTranslateManager2 : public TranslateManager2 {
 public:
  TestTranslateManager2() {}
};

class TranslateManager2Test : public RenderViewHostTestHarness,
                              public NotificationObserver {
 public:
  TranslateManager2Test() {}

  // Simluates navigating to a page and getting the page contents and language
  // for that navigation.
  void SimulateNavigation(const GURL& url, int page_id,
                          const std::wstring& contents,
                          const std::string& lang) {
    NavigateAndCommit(url);
    SimulateOnPageContents(url, page_id, contents, lang);
  }

  void SimulateOnPageContents(const GURL& url, int page_id,
                              const std::wstring& contents,
                              const std::string& lang) {
    rvh()->TestOnMessageReceived(ViewHostMsg_PageContents(0, url, page_id,
                                                          contents, lang));
  }

  bool GetTranslateMessage(int* page_id,
                           std::string* original_lang,
                           std::string* target_lang) {
    const IPC::Message* message =
        process()->sink().GetFirstMessageMatching(ViewMsg_TranslatePage::ID);
    if (!message)
      return false;
    Tuple4<int, std::string, std::string, std::string> translate_param;
    ViewMsg_TranslatePage::Read(message, &translate_param);
    if (page_id)
      *page_id = translate_param.a;
    // Ignore translate_param.b which is the script injected in the page.
    if (original_lang)
      *original_lang = translate_param.c;
    if (target_lang)
      *target_lang = translate_param.d;
    return true;
  }

  // Returns the translate infobar if there is 1 infobar and it is a translate
  // infobar.
  TranslateInfoBarDelegate2* GetTranslateInfoBar() {
    if (contents()->infobar_delegate_count() != 1)
      return NULL;
    return contents()->GetInfoBarDelegateAt(0)->AsTranslateInfoBarDelegate2();
  }

  // If there is 1 infobar and it is a translate infobar, closes it and returns
  // true.  Returns false otherwise.
  bool CloseTranslateInfoBar() {
    TranslateInfoBarDelegate2* infobar = GetTranslateInfoBar();
    if (!infobar)
      return false;
    infobar->InfoBarDismissed();  // Simulates closing the infobar.
    contents()->RemoveInfoBar(infobar);
    return true;
  }

  // Checks whether |infobar| has been removed and clears the removed infobar
  // list.
  bool CheckInfoBarRemovedAndReset(InfoBarDelegate* infobar) {
    bool found = std::find(removed_infobars_.begin(), removed_infobars_.end(),
                           infobar) != removed_infobars_.end();
    removed_infobars_.clear();
    return found;
  }

  // Returns true if at least one infobar was closed.
  bool InfoBarRemoved() {
    return !removed_infobars_.empty();
  }

  // Clears the list of stored removed infobars.
  void ClearRemovedInfoBars() {
    removed_infobars_.clear();
  }

  // If there is 1 infobar and it is a translate infobar, deny translation and
  // returns true.  Returns false otherwise.
  bool DenyTranslation() {
    TranslateInfoBarDelegate2* infobar = GetTranslateInfoBar();
    if (!infobar)
      return false;
    infobar->TranslationDeclined();
    contents()->RemoveInfoBar(infobar);
    return true;
  }

  virtual void Observe(NotificationType type,
                       const NotificationSource& source,
                       const NotificationDetails& details) {
    DCHECK(type == NotificationType::TAB_CONTENTS_INFOBAR_REMOVED);
    removed_infobars_.push_back(Details<InfoBarDelegate>(details).ptr());
  }

 protected:
  virtual void SetUp() {
    URLFetcher::set_factory(&url_fetcher_factory_);

    // Access the TranslateManager singleton so it is created before we call
    // RenderViewHostTestHarness::SetUp() to match what's done in Chrome, where
    // the TranslateManager is created before the TabContents.  This matters as
    // they both register for similar events and we want the notifications to
    // happen in the same sequence (TranslateManager first, TabContents second).
    // Also clears the translate script so it is fetched everytime.
    Singleton<TranslateManager2>::get()->ClearTranslateScript();

    RenderViewHostTestHarness::SetUp();

    notification_registrar_.Add(
        this,
        NotificationType::TAB_CONTENTS_INFOBAR_REMOVED,
        Source<TabContents>(contents()));
  }

  virtual void TearDown() {
    notification_registrar_.Remove(
        this,
        NotificationType::TAB_CONTENTS_INFOBAR_REMOVED,
        Source<TabContents>(contents()));

    RenderViewHostTestHarness::TearDown();

    URLFetcher::set_factory(NULL);
  }

  void SimulateURLFetch(bool success) {
    TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0);
    ASSERT_TRUE(fetcher);
    URLRequestStatus status;
    status.set_status(success ? URLRequestStatus::SUCCESS :
                                URLRequestStatus::FAILED);
    fetcher->delegate()->OnURLFetchComplete(fetcher, fetcher->original_url(),
                                            status, success ? 200 : 500,
                                            ResponseCookies(),
                                            std::string());
  }

  void SetPrefObserverExpectation(const wchar_t* path) {
    EXPECT_CALL(
        pref_observer_,
        Observe(NotificationType(NotificationType::PREF_CHANGED),
                _,
                Property(&Details<std::wstring>::ptr, Pointee(path))));
  }

  NotificationObserverMock pref_observer_;

 private:
  NotificationRegistrar notification_registrar_;
  scoped_ptr<TestTranslateManager2> translate_manager_;
  TestURLFetcherFactory url_fetcher_factory_;

  // The list of infobars that have been removed.
  // WARNING: the pointers points to deleted objects, use only for comparison.
  std::vector<InfoBarDelegate*> removed_infobars_;

  DISALLOW_COPY_AND_ASSIGN(TranslateManager2Test);
};

// An observer that keeps track of whether a navigation entry was committed.
class NavEntryCommittedObserver : public NotificationObserver {
 public:
  explicit NavEntryCommittedObserver(TabContents* tab_contents) {
    registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED,
                   Source<NavigationController>(&tab_contents->controller()));
  }

  virtual void Observe(NotificationType type,
                       const NotificationSource& source,
                       const NotificationDetails& details) {
    DCHECK(type == NotificationType::NAV_ENTRY_COMMITTED);
    details_ =
        *(Details<NavigationController::LoadCommittedDetails>(details).ptr());
  }

  const NavigationController::LoadCommittedDetails&
      get_load_commited_details() const {
    return details_;
  }

 private:
  NavigationController::LoadCommittedDetails details_;
  NotificationRegistrar registrar_;

  DISALLOW_COPY_AND_ASSIGN(NavEntryCommittedObserver);
};

class TestRenderViewContextMenu : public RenderViewContextMenu {
 public:
  static TestRenderViewContextMenu* CreateContextMenu(
      TabContents* tab_contents) {
    ContextMenuParams params;
    params.media_type = WebKit::WebContextMenuData::MediaTypeNone;
    params.x = 0;
    params.y = 0;
    params.is_image_blocked = false;
    params.media_flags = 0;
    params.spellcheck_enabled = false;;
    params.is_editable = false;
    params.page_url = tab_contents->controller().GetActiveEntry()->url();
#if defined(OS_MACOSX)
    params.writing_direction_default = 0;
    params.writing_direction_left_to_right = 0;
    params.writing_direction_right_to_left = 0;
#endif  // OS_MACOSX
    params.edit_flags = 0;
    return new TestRenderViewContextMenu(tab_contents, params);
  }

  bool IsItemPresent(int id) {
    return menu_model_.GetIndexOfCommandId(id) != -1;
  }

  virtual void PlatformInit() { }
  virtual bool GetAcceleratorForCommandId(
      int command_id,
      menus::Accelerator* accelerator) { return false; }

 private:
  TestRenderViewContextMenu(TabContents* tab_contents,
                            const ContextMenuParams& params)
      : RenderViewContextMenu(tab_contents, params) {
  }

  DISALLOW_COPY_AND_ASSIGN(TestRenderViewContextMenu);
};

TEST_F(TranslateManager2Test, NormalTranslate) {
  // Simulate navigating to a page.
  SimulateNavigation(GURL("http://www.google.fr"), 0, L"Le Google", "fr");

  // We should have an infobar.
  TranslateInfoBarDelegate2* infobar = GetTranslateInfoBar();
  ASSERT_TRUE(infobar != NULL);
  EXPECT_EQ(TranslateInfoBarDelegate2::BEFORE_TRANSLATE, infobar->type());

  // Simulate clicking translate.
  process()->sink().ClearMessages();
  infobar->Translate();
  
  // Simulate the translate script being retrieved (it only needs to be done
  // once in the test as it is cached).
  SimulateURLFetch(true);

  // Test that we sent the right message to the renderer.
  int page_id = 0;
  std::string original_lang, target_lang;
  EXPECT_TRUE(GetTranslateMessage(&page_id, &original_lang, &target_lang));
  EXPECT_EQ(0, page_id);
  EXPECT_EQ("fr", original_lang);
  EXPECT_EQ("en", target_lang);

  // The "Translating..." infobar should be showing.
  infobar = GetTranslateInfoBar();
  ASSERT_TRUE(infobar != NULL);
  EXPECT_EQ(TranslateInfoBarDelegate2::TRANSLATING, infobar->type());

  // Simulate the render notifying the translation has been done.
  rvh()->TestOnMessageReceived(ViewHostMsg_PageTranslated(0, 0, "fr", "en",
      TranslateErrors::NONE));

  // The after translate infobar should be showing.
  infobar = GetTranslateInfoBar();
  ASSERT_TRUE(infobar != NULL);
  EXPECT_EQ(TranslateInfoBarDelegate2::AFTER_TRANSLATE, infobar->type());

  // Simulate translating again from there but with 2 different languages.
  infobar->SetOriginalLanguage(0);
  infobar->SetTargetLanguage(1);
  std::string new_original_lang = infobar->GetOriginalLanguageCode();
  std::string new_target_lang = infobar->GetTargetLanguageCode();
  process()->sink().ClearMessages();
  infobar->Translate();

  // Test that we sent the right message to the renderer.
  EXPECT_TRUE(GetTranslateMessage(&page_id, &original_lang, &target_lang));
  EXPECT_EQ(0, page_id);
  EXPECT_EQ(new_original_lang, original_lang);
  EXPECT_EQ(new_target_lang, target_lang);
}

TEST_F(TranslateManager2Test, TranslateScriptNotAvailable) {
  // Simulate navigating to a page.
  SimulateNavigation(GURL("http://www.google.fr"), 0, L"Le Google", "fr");

  // We should have an infobar.
  TranslateInfoBarDelegate2* infobar = GetTranslateInfoBar();
  ASSERT_TRUE(infobar != NULL);
  EXPECT_EQ(TranslateInfoBarDelegate2::BEFORE_TRANSLATE, infobar->type());

  // Simulate clicking translate.
  process()->sink().ClearMessages();
  infobar->Translate();
  // Simulate a failure retrieving the translate script.
  SimulateURLFetch(false);

  // We should not have sent any message to translate to the renderer.
  EXPECT_FALSE(GetTranslateMessage(NULL, NULL, NULL));

  // And we should have an error infobar showing.
  infobar = GetTranslateInfoBar();
  ASSERT_TRUE(infobar != NULL);
  EXPECT_EQ(TranslateInfoBarDelegate2::TRANSLATION_ERROR, infobar->type());
}

// Tests that we show/don't show an info-bar for all languages the CLD can
// report.
TEST_F(TranslateManager2Test, TestAllLanguages) {
  // The index in kExpectation are the Language enum (see languages.pb.h).
  // true if we expect a translate infobar for that language.
  // Note the supported languages are in translation_service.cc, see
  // kSupportedLanguages.
  bool kExpectations[] = {
    // 0-9
    false, true, true, true, true, true, true, true, true, true,
    // 10-19
    true, true, true, true, true, true, true, true, true, true,
    // 20-29
    true, true, true, true, true, false, false, true, true, true,
    // 30-39
    true, true, true, true, true, true, true, false, true, false,
    // 40-49
    true, false, true, false, false, true, false, true, false, false,
    // 50-59
    false, false, false, true, true, true, false, false, false, false,
    // 60-69
    false, false, true, true, false, true, true, false, true, true,
    // 70-79
    false, false, false, false, false, false, false, true, false, false,
    // 80-89
    false, false, false, false, false, false, false, false, false, false,
    // 90-99
    false, true, false, false, false, false, false, false, false, false,
    // 100-109
    false, true, false, false, false, false, false, false, false, false,
    // 110-119
    false, false, false, false, false, false, false, false, false, false,
    // 120-129
    false, false, false, false, false, false, false, false, false, false,
    // 130-139
    false, false, false, false, false, false, false, false, false, false,
    // 140-149
    false, false, false, false, false, false, false, false, false, false,
    // 150-159
    false, false, false, false, false, false, false, false, false, false,
    // 160
    false
  };

  GURL url("http://www.google.com");
  for (size_t i = 0; i < arraysize(kExpectations); ++i) {
    ASSERT_LT(i, static_cast<size_t>(NUM_LANGUAGES));

    std::string lang = LanguageCodeWithDialects(static_cast<Language>(i));
    SCOPED_TRACE(::testing::Message::Message() << "Iteration " << i <<
        " language=" << lang);

    // We should not have a translate infobar.
    TranslateInfoBarDelegate2* infobar = GetTranslateInfoBar();
    ASSERT_TRUE(infobar == NULL);

    // Simulate navigating to a page.
    NavigateAndCommit(url);
    SimulateOnPageContents(url, i, L"", lang);

    // Verify we have/don't have an info-bar as expected.
    infobar = GetTranslateInfoBar();
    EXPECT_EQ(kExpectations[i], infobar != NULL);

    // Close the info-bar if applicable.
    if (infobar != NULL)
      EXPECT_TRUE(CloseTranslateInfoBar());
  }
}

// Tests auto-translate on page.
TEST_F(TranslateManager2Test, AutoTranslateOnNavigate) {
  // Simulate navigating to a page and getting its language.
  SimulateNavigation(GURL("http://www.google.fr"), 0, L"Le Google", "fr");

  // Simulate the user translating.
  TranslateInfoBarDelegate2* infobar = GetTranslateInfoBar();
  ASSERT_TRUE(infobar != NULL);
  infobar->Translate();
  SimulateURLFetch(true);  // Simulate the translate script being retrieved.

  rvh()->TestOnMessageReceived(ViewHostMsg_PageTranslated(0, 0, "fr", "en",
      TranslateErrors::NONE));

  // Now navigate to a new page in the same language.
  process()->sink().ClearMessages();
  SimulateNavigation(GURL("http://news.google.fr"), 1, L"Les news", "fr");

  // This should have automatically triggered a translation.
  int page_id = 0;
  std::string original_lang, target_lang;
  EXPECT_TRUE(GetTranslateMessage(&page_id, &original_lang, &target_lang));
  EXPECT_EQ(1, page_id);
  EXPECT_EQ("fr", original_lang);
  EXPECT_EQ("en", target_lang);

  // Now navigate to a page in a different language.
  process()->sink().ClearMessages();
  SimulateNavigation(GURL("http://news.google.es"), 1, L"Las news", "es");

  // This should not have triggered a translate.
  EXPECT_FALSE(GetTranslateMessage(&page_id, &original_lang, &target_lang));
}

// Tests that multiple OnPageContents do not cause multiple infobars.
TEST_F(TranslateManager2Test, MultipleOnPageContents) {
  // Simulate navigating to a page and getting its language.
  SimulateNavigation(GURL("http://www.google.fr"), 0, L"Le Google", "fr");

  // Simulate clicking 'Nope' (don't translate).
  EXPECT_TRUE(DenyTranslation());
  EXPECT_EQ(0, contents()->infobar_delegate_count());

  // Send a new PageContents, we should not show an infobar.
  SimulateOnPageContents(GURL("http://www.google.fr"), 0, L"Le Google", "fr");
  EXPECT_EQ(0, contents()->infobar_delegate_count());

  // Do the same steps but simulate closing the infobar this time.
  SimulateNavigation(GURL("http://www.youtube.fr"), 1, L"Le YouTube", "fr");
  EXPECT_TRUE(CloseTranslateInfoBar());
  EXPECT_EQ(0, contents()->infobar_delegate_count());
  SimulateOnPageContents(GURL("http://www.youtube.fr"), 1, L"Le YouTube", "fr");
  EXPECT_EQ(0, contents()->infobar_delegate_count());
}

// Test that reloading the page brings back the infobar.
TEST_F(TranslateManager2Test, Reload) {
  // Simulate navigating to a page and getting its language.
  SimulateNavigation(GURL("http://www.google.fr"), 0, L"Le Google", "fr");

  // Close the infobar.
  EXPECT_TRUE(CloseTranslateInfoBar());

  // Reload should bring back the infobar.
  NavEntryCommittedObserver nav_observer(contents());
  Reload();

  // Ensures it is really handled a reload.
  const NavigationController::LoadCommittedDetails& nav_details =
      nav_observer.get_load_commited_details();
  EXPECT_TRUE(nav_details.entry != NULL);  // There was a navigation.
  EXPECT_EQ(NavigationType::EXISTING_PAGE, nav_details.type);

  // The TranslateManager class processes the navigation entry committed
  // notification in a posted task; process that task.
  MessageLoop::current()->RunAllPending();
  EXPECT_TRUE(GetTranslateInfoBar() != NULL);
}

// Test that reloading the page by way of typing again the URL in the
// location bar brings back the infobar.
TEST_F(TranslateManager2Test, ReloadFromLocationBar) {
  GURL url("http://www.google.fr");

  // Simulate navigating to a page and getting its language.
  SimulateNavigation(url, 0, L"Le Google", "fr");

  // Close the infobar.
  EXPECT_TRUE(CloseTranslateInfoBar());

  // Create a pending navigation and simulate a page load.  That should be the
  // equivalent of typing the URL again in the location bar.
  NavEntryCommittedObserver nav_observer(contents());
  contents()->controller().LoadURL(url, GURL(), PageTransition::TYPED);
  rvh()->SendNavigate(0, url);

  // Test that we are really getting a same page navigation, the test would be
  // useless if it was not the case.
  const NavigationController::LoadCommittedDetails& nav_details =
      nav_observer.get_load_commited_details();
  EXPECT_TRUE(nav_details.entry != NULL);  // There was a navigation.
  EXPECT_EQ(NavigationType::SAME_PAGE, nav_details.type);

  // The TranslateManager class processes the navigation entry committed
  // notification in a posted task; process that task.
  MessageLoop::current()->RunAllPending();
  EXPECT_TRUE(GetTranslateInfoBar() != NULL);
}

// Tests that a close translate infobar does not reappear when navigating
// in-page.
TEST_F(TranslateManager2Test, CloseInfoBarInPageNavigation) {
  // Simulate navigating to a page and getting its language.
  SimulateNavigation(GURL("http://www.google.fr"), 0, L"Le Google", "fr");

  // Close the infobar.
  EXPECT_TRUE(CloseTranslateInfoBar());

  // Navigate in page, no infobar should be shown.
  SimulateNavigation(GURL("http://www.google.fr/#ref1"), 0, L"Le Google", "fr");
  EXPECT_TRUE(GetTranslateInfoBar() == NULL);

  // Navigate out of page, a new infobar should show.
  SimulateNavigation(GURL("http://www.google.fr/foot"), 0, L"Le Google", "fr");
  EXPECT_TRUE(GetTranslateInfoBar() != NULL);
}

// Tests that denying translation is sticky when navigating in page.
TEST_F(TranslateManager2Test, DenyTranslateInPageNavigation) {
  // Simulate navigating to a page and getting its language.
  SimulateNavigation(GURL("http://www.google.fr"), 0, L"Le Google", "fr");

  // Simulate clicking 'Nope' (don't translate).
  EXPECT_TRUE(DenyTranslation());

  // Navigate in page, no infobar should be shown.
  SimulateNavigation(GURL("http://www.google.fr/#ref1"), 0, L"Le Google", "fr");
  EXPECT_TRUE(GetTranslateInfoBar() == NULL);

  // Navigate out of page, a new infobar should show.
  SimulateNavigation(GURL("http://www.google.fr/foot"), 0, L"Le Google", "fr");
  EXPECT_TRUE(GetTranslateInfoBar() != NULL);
}

// Tests that after translating and closing the infobar, the infobar does not
// return when navigating in page.
TEST_F(TranslateManager2Test, TranslateCloseInfoBarInPageNavigation) {
  // Simulate navigating to a page and getting its language.
  SimulateNavigation(GURL("http://www.google.fr"), 0, L"Le Google", "fr");

  // Simulate the user translating.
  TranslateInfoBarDelegate2* infobar = GetTranslateInfoBar();
  ASSERT_TRUE(infobar != NULL);
  infobar->Translate();
  SimulateURLFetch(true);  // Simulate the translate script being retrieved.
  rvh()->TestOnMessageReceived(ViewHostMsg_PageTranslated(0, 0, "fr", "en",
      TranslateErrors::NONE));

  // Close the infobar.
  EXPECT_TRUE(CloseTranslateInfoBar());

  // Navigate in page, no infobar should be shown.
  SimulateNavigation(GURL("http://www.google.fr/#ref1"), 0, L"Le Google", "fr");
  EXPECT_TRUE(GetTranslateInfoBar() == NULL);

  // Navigate out of page, a new infobar should show.
  // Note that we navigate to a page in a different language so we don't trigger
  // the auto-translate feature (it would translate the page automatically and
  // the before translate inforbar would not be shown).
  SimulateNavigation(GURL("http://www.google.de"), 0, L"Das Google", "de");
  EXPECT_TRUE(GetTranslateInfoBar() != NULL);
}

// Tests that the after translate the infobar still shows when navigating
// in-page.
TEST_F(TranslateManager2Test, TranslateInPageNavigation) {
  // Simulate navigating to a page and getting its language.
  SimulateNavigation(GURL("http://www.google.fr"), 0, L"Le Google", "fr");

  // Simulate the user translating.
  TranslateInfoBarDelegate2* infobar = GetTranslateInfoBar();
  ASSERT_TRUE(infobar != NULL);
  infobar->Translate();
  SimulateURLFetch(true);  // Simulate the translate script being retrieved.
  rvh()->TestOnMessageReceived(ViewHostMsg_PageTranslated(0, 0, "fr", "en",
      TranslateErrors::NONE));
  // The after translate infobar is showing.
  infobar = GetTranslateInfoBar();
  ASSERT_TRUE(infobar != NULL);

  // Navigate in page, the same infobar should still be shown.
  ClearRemovedInfoBars();
  SimulateNavigation(GURL("http://www.google.fr/#ref1"), 0, L"Le Google", "fr");
  EXPECT_FALSE(InfoBarRemoved());
  EXPECT_EQ(infobar, GetTranslateInfoBar());

  // Navigate out of page, a new infobar should show.
  // See note in TranslateCloseInfoBarInPageNavigation test on why it is
  // important to navigate to a page in a different language for this test.
  SimulateNavigation(GURL("http://www.google.de"), 0, L"Das Google", "de");
  // The old infobar is gone.
  EXPECT_TRUE(CheckInfoBarRemovedAndReset(infobar));
  // And there is a new one.
  EXPECT_TRUE(GetTranslateInfoBar() != NULL);
}

// Tests that no translate infobar is shown when navigating to a page in an
// unsupported language.
TEST_F(TranslateManager2Test, UnsupportedPageLanguage) {
  // Simulate navigating to a page and getting an unsupported language.
  SimulateNavigation(GURL("http://www.google.com"), 0, L"Google", "qbz");

  // No info-bar should be shown.
  EXPECT_TRUE(GetTranslateInfoBar() == NULL);
}

// Tests that no translate infobar is shown when Chrome is in a language that
// the translate server does not support.
TEST_F(TranslateManager2Test, UnsupportedUILanguage) {
  TestingBrowserProcess* browser_process =
      static_cast<TestingBrowserProcess*>(g_browser_process);
  std::string original_lang = browser_process->GetApplicationLocale();
  browser_process->SetApplicationLocale("qbz");

  // Simulate navigating to a page in a language supported by the translate
  // server.
  SimulateNavigation(GURL("http://www.google.com"), 0, L"Google", "en");

  // No info-bar should be shown.
  EXPECT_TRUE(GetTranslateInfoBar() == NULL);

  browser_process->SetApplicationLocale(original_lang);
}

// Tests that the translate enabled preference is honored.
TEST_F(TranslateManager2Test, TranslateEnabledPref) {
  // Make sure the pref allows translate.
  PrefService* prefs = contents()->profile()->GetPrefs();
  prefs->SetBoolean(prefs::kEnableTranslate, true);

  // Simulate navigating to a page and getting its language.
  SimulateNavigation(GURL("http://www.google.fr"), 0, L"Le Google", "fr");

  // An infobar should be shown.
  TranslateInfoBarDelegate2* infobar = GetTranslateInfoBar();
  EXPECT_TRUE(infobar != NULL);

  // Disable translate.
  prefs->SetBoolean(prefs::kEnableTranslate, false);

  // Navigate to a new page, that should close the previous infobar.
  GURL url("http://www.youtube.fr");
  NavigateAndCommit(url);
  infobar = GetTranslateInfoBar();
  EXPECT_TRUE(infobar == NULL);

  // Simulate getting the page contents and language, that should not trigger
  // a translate infobar.
  SimulateOnPageContents(url, 1, L"Le YouTube", "fr");
  infobar = GetTranslateInfoBar();
  EXPECT_TRUE(infobar == NULL);
}

// Tests the "Never translate <language>" pref.
TEST_F(TranslateManager2Test, NeverTranslateLanguagePref) {
  // Simulate navigating to a page and getting its language.
  GURL url("http://www.google.fr");
  SimulateNavigation(url, 0, L"Le Google", "fr");

  // An infobar should be shown.
  EXPECT_TRUE(GetTranslateInfoBar() != NULL);

  // Select never translate this language.
  PrefService* prefs = contents()->profile()->GetPrefs();
  prefs->AddPrefObserver(TranslatePrefs::kPrefTranslateLanguageBlacklist,
                         &pref_observer_);
  TranslatePrefs translate_prefs(prefs);
  EXPECT_FALSE(translate_prefs.IsLanguageBlacklisted("fr"));
  EXPECT_TRUE(translate_prefs.CanTranslate(prefs, "fr", url));
  SetPrefObserverExpectation(TranslatePrefs::kPrefTranslateLanguageBlacklist);
  translate_prefs.BlacklistLanguage("fr");
  EXPECT_TRUE(translate_prefs.IsLanguageBlacklisted("fr"));
  EXPECT_FALSE(translate_prefs.CanTranslate(prefs, "fr", url));

  // Close the infobar.
  EXPECT_TRUE(CloseTranslateInfoBar());

  // Navigate to a new page also in French.
  SimulateNavigation(GURL("http://wwww.youtube.fr"), 1, L"Le YouTube", "fr");

  // There should not be a translate infobar.
  EXPECT_TRUE(GetTranslateInfoBar() == NULL);

  // Remove the language from the blacklist.
  SetPrefObserverExpectation(TranslatePrefs::kPrefTranslateLanguageBlacklist);
  translate_prefs.RemoveLanguageFromBlacklist("fr");
  EXPECT_FALSE(translate_prefs.IsLanguageBlacklisted("fr"));
  EXPECT_TRUE(translate_prefs.CanTranslate(prefs, "fr", url));

  // Navigate to a page in French.
  SimulateNavigation(url, 2, L"Le Google", "fr");

  // There should be a translate infobar.
  EXPECT_TRUE(GetTranslateInfoBar() != NULL);
  prefs->RemovePrefObserver(TranslatePrefs::kPrefTranslateLanguageBlacklist,
                            &pref_observer_);
}

// Tests the "Never translate this site" pref.
TEST_F(TranslateManager2Test, NeverTranslateSitePref) {
  // Simulate navigating to a page and getting its language.
  GURL url("http://www.google.fr");
  std::string host(url.host());
  SimulateNavigation(url, 0, L"Le Google", "fr");

  // An infobar should be shown.
  EXPECT_TRUE(GetTranslateInfoBar() != NULL);

  // Select never translate this site.
  PrefService* prefs = contents()->profile()->GetPrefs();
  prefs->AddPrefObserver(TranslatePrefs::kPrefTranslateSiteBlacklist,
                         &pref_observer_);
  TranslatePrefs translate_prefs(prefs);
  EXPECT_FALSE(translate_prefs.IsSiteBlacklisted(host));
  EXPECT_TRUE(translate_prefs.CanTranslate(prefs, "fr", url));
  SetPrefObserverExpectation(TranslatePrefs::kPrefTranslateSiteBlacklist);
  translate_prefs.BlacklistSite(host);
  EXPECT_TRUE(translate_prefs.IsSiteBlacklisted(host));
  EXPECT_FALSE(translate_prefs.CanTranslate(prefs, "fr", url));

  // Close the infobar.
  EXPECT_TRUE(CloseTranslateInfoBar());

  // Navigate to a new page also on the same site.
  SimulateNavigation(GURL("http://www.google.fr/hello"), 1, L"Bonjour", "fr");

  // There should not be a translate infobar.
  EXPECT_TRUE(GetTranslateInfoBar() == NULL);

  // Remove the site from the blacklist.
  SetPrefObserverExpectation(TranslatePrefs::kPrefTranslateSiteBlacklist);
  translate_prefs.RemoveSiteFromBlacklist(host);
  EXPECT_FALSE(translate_prefs.IsSiteBlacklisted(host));
  EXPECT_TRUE(translate_prefs.CanTranslate(prefs, "fr", url));

  // Navigate to a page in French.
  SimulateNavigation(url, 0, L"Le Google", "fr");

  // There should be a translate infobar.
  EXPECT_TRUE(GetTranslateInfoBar() != NULL);
  prefs->RemovePrefObserver(TranslatePrefs::kPrefTranslateSiteBlacklist,
                            &pref_observer_);
}

// Tests the "Always translate this language" pref.
TEST_F(TranslateManager2Test, AlwaysTranslateLanguagePref) {
  // Select always translate French to English.
  PrefService* prefs = contents()->profile()->GetPrefs();
  prefs->AddPrefObserver(TranslatePrefs::kPrefTranslateWhitelists,
                         &pref_observer_);
  TranslatePrefs translate_prefs(prefs);
  SetPrefObserverExpectation(TranslatePrefs::kPrefTranslateWhitelists);
  translate_prefs.WhitelistLanguagePair("fr", "en");

  // Load a page in French.
  SimulateNavigation(GURL("http://www.google.fr"), 0, L"Le Google", "fr");

  // It should have triggered an automatic translation to English.
  SimulateURLFetch(true);  // Simulate the translate script being retrieved.
  int page_id = 0;
  std::string original_lang, target_lang;
  EXPECT_TRUE(GetTranslateMessage(&page_id, &original_lang, &target_lang));
  EXPECT_EQ(0, page_id);
  EXPECT_EQ("fr", original_lang);
  EXPECT_EQ("en", target_lang);
  process()->sink().ClearMessages();
  // And we should have no infobar (since we don't send the page translated
  // notification, the after translate infobar is not shown).
  EXPECT_TRUE(GetTranslateInfoBar() == NULL);

  // Try another language, it should not be autotranslated.
  SimulateNavigation(GURL("http://www.google.es"), 1, L"El Google", "es");
  EXPECT_FALSE(GetTranslateMessage(&page_id, &original_lang, &target_lang));
  EXPECT_TRUE(GetTranslateInfoBar() != NULL);
  EXPECT_TRUE(CloseTranslateInfoBar());

  // Let's switch to incognito mode, it should not be autotranslated in that
  // case either.
  TestingProfile* test_profile =
      static_cast<TestingProfile*>(contents()->profile());
  test_profile->set_off_the_record(true);
  SimulateNavigation(GURL("http://www.youtube.fr"), 2, L"Le YouTube", "fr");
  EXPECT_FALSE(GetTranslateMessage(&page_id, &original_lang, &target_lang));
  EXPECT_TRUE(GetTranslateInfoBar() != NULL);
  EXPECT_TRUE(CloseTranslateInfoBar());
  test_profile->set_off_the_record(false);  // Get back to non incognito.

  // Now revert the always translate pref and make sure we go back to expected
  // behavior, which is show an infobar.
  SetPrefObserverExpectation(TranslatePrefs::kPrefTranslateWhitelists);
  translate_prefs.RemoveLanguagePairFromWhitelist("fr", "en");
  SimulateNavigation(GURL("http://www.google.fr"), 3, L"Le Google", "fr");
  EXPECT_FALSE(GetTranslateMessage(&page_id, &original_lang, &target_lang));
  EXPECT_TRUE(GetTranslateInfoBar() != NULL);
  prefs->RemovePrefObserver(TranslatePrefs::kPrefTranslateWhitelists,
                            &pref_observer_);
}
/*

// Context menu.
TEST_F(TranslateManager2Test, ContextMenu) {
  // Blacklist www.google.fr and French for translation.
  GURL url("http://www.google.fr");
  TranslatePrefs translate_prefs(contents()->profile()->GetPrefs());
  translate_prefs.BlacklistLanguage("fr");
  translate_prefs.BlacklistSite(url.host());
  EXPECT_TRUE(translate_prefs.IsLanguageBlacklisted("fr"));
  EXPECT_TRUE(translate_prefs.IsSiteBlacklisted(url.host()));

  // Simulate navigating to a page in French. The translate menu should show.
  SimulateNavigation(url, 0, L"Le Google", "fr");
  scoped_ptr<TestRenderViewContextMenu> menu(
      TestRenderViewContextMenu::CreateContextMenu(contents()));
  menu->Init();
  EXPECT_TRUE(menu->IsItemPresent(IDS_CONTENT_CONTEXT_TRANSLATE));
  EXPECT_TRUE(menu->TestIsCommandIdEnabled(IDS_CONTENT_CONTEXT_TRANSLATE));

  // Use the menu to translate the page.
  menu->TestExecuteItemCommand(IDS_CONTENT_CONTEXT_TRANSLATE);

  // That should have triggered a translation.
  SimulateURLFetch(true);  // Simulate the translate script being retrieved.
  int page_id = 0;
  std::string original_lang, target_lang;
  EXPECT_TRUE(GetTranslateMessage(&page_id, &original_lang, &target_lang));
  EXPECT_EQ(0, page_id);
  EXPECT_EQ("fr", original_lang);
  EXPECT_EQ("en", target_lang);
  process()->sink().ClearMessages();

  // This should also have reverted the blacklisting of this site and language.
  EXPECT_FALSE(translate_prefs.IsLanguageBlacklisted("fr"));
  EXPECT_FALSE(translate_prefs.IsSiteBlacklisted(url.host()));

  // Let's simulate the page being translated.
  rvh()->TestOnMessageReceived(ViewHostMsg_PageTranslated(0, 0, "fr", "en",
      TranslateErrors::NONE));

  // The translate menu should now be disabled.
  menu.reset(TestRenderViewContextMenu::CreateContextMenu(contents()));
  menu->Init();
  EXPECT_TRUE(menu->IsItemPresent(IDS_CONTENT_CONTEXT_TRANSLATE));
  EXPECT_FALSE(menu->TestIsCommandIdEnabled(IDS_CONTENT_CONTEXT_TRANSLATE));

  // Test that selecting translate in the context menu WHILE the page is being
  // translated does nothing (this could happen if autotranslate kicks-in and
  // the user selects the menu while the translation is being performed).
  SimulateNavigation(GURL("http://www.google.es"), 1, L"El Google", "es");
  TranslateInfoBarDelegate2* infobar = GetTranslateInfoBar();
  ASSERT_TRUE(infobar != NULL);
  infobar->Translate();
  EXPECT_TRUE(GetTranslateMessage(&page_id, &original_lang, &target_lang));
  EXPECT_EQ(1, page_id);
  process()->sink().ClearMessages();
  menu.reset(TestRenderViewContextMenu::CreateContextMenu(contents()));
  menu->Init();
  EXPECT_TRUE(menu->TestIsCommandIdEnabled(IDS_CONTENT_CONTEXT_TRANSLATE));
  menu->TestExecuteItemCommand(IDS_CONTENT_CONTEXT_TRANSLATE);
  // No message expected since the translation should have been ignored.
  EXPECT_FALSE(GetTranslateMessage(&page_id, &original_lang, &target_lang));

  // Now test that selecting translate in the context menu AFTER the page has
  // been translated does nothing.
  SimulateNavigation(GURL("http://www.google.de"), 2, L"Das Google", "de");
  infobar = GetTranslateInfoBar();
  ASSERT_TRUE(infobar != NULL);
  infobar->Translate();
  EXPECT_TRUE(GetTranslateMessage(&page_id, &original_lang, &target_lang));
  EXPECT_EQ(2, page_id);
  process()->sink().ClearMessages();
  menu.reset(TestRenderViewContextMenu::CreateContextMenu(contents()));
  menu->Init();
  EXPECT_TRUE(menu->TestIsCommandIdEnabled(IDS_CONTENT_CONTEXT_TRANSLATE));
  rvh()->TestOnMessageReceived(ViewHostMsg_PageTranslated(0, 0, "de", "en",
      TranslateErrors::NONE));
  menu->TestExecuteItemCommand(IDS_CONTENT_CONTEXT_TRANSLATE);
  // No message expected since the translation should have been ignored.
  EXPECT_FALSE(GetTranslateMessage(&page_id, &original_lang, &target_lang));

  // Test that the translate context menu is disabled when the page is in the
  // same language as the UI.
  SimulateNavigation(url, 0, L"Google", "en");
  menu.reset(TestRenderViewContextMenu::CreateContextMenu(contents()));
  menu->Init();
  EXPECT_TRUE(menu->IsItemPresent(IDS_CONTENT_CONTEXT_TRANSLATE));
  EXPECT_FALSE(menu->TestIsCommandIdEnabled(IDS_CONTENT_CONTEXT_TRANSLATE));
}
*/

// Context menu.
TEST_F(TranslateManager2Test, ContextMenu) {
  // Blacklist www.google.fr and French for translation.
  GURL url("http://www.google.fr");
  TranslatePrefs translate_prefs(contents()->profile()->GetPrefs());
  translate_prefs.BlacklistLanguage("fr");
  translate_prefs.BlacklistSite(url.host());
  EXPECT_TRUE(translate_prefs.IsLanguageBlacklisted("fr"));
  EXPECT_TRUE(translate_prefs.IsSiteBlacklisted(url.host()));

  // Simulate navigating to a page in French. The translate menu should show.
  SimulateNavigation(url, 0, L"Le Google", "fr");
  scoped_ptr<TestRenderViewContextMenu> menu(
      TestRenderViewContextMenu::CreateContextMenu(contents()));
  menu->Init();
  EXPECT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_TRANSLATE));
  EXPECT_TRUE(menu->IsCommandIdEnabled(IDC_CONTENT_CONTEXT_TRANSLATE));

  // Use the menu to translate the page.
  menu->ExecuteCommand(IDC_CONTENT_CONTEXT_TRANSLATE);

  // That should have triggered a translation.
  SimulateURLFetch(true);  // Simulate the translate script being retrieved.
  int page_id = 0;
  std::string original_lang, target_lang;
  EXPECT_TRUE(GetTranslateMessage(&page_id, &original_lang, &target_lang));
  EXPECT_EQ(0, page_id);
  EXPECT_EQ("fr", original_lang);
  EXPECT_EQ("en", target_lang);
  process()->sink().ClearMessages();

  // This should also have reverted the blacklisting of this site and language.
  EXPECT_FALSE(translate_prefs.IsLanguageBlacklisted("fr"));
  EXPECT_FALSE(translate_prefs.IsSiteBlacklisted(url.host()));

  // Let's simulate the page being translated.
  rvh()->TestOnMessageReceived(ViewHostMsg_PageTranslated(0, 0, "fr", "en",
      TranslateErrors::NONE));

  // The translate menu should now be disabled.
  menu.reset(TestRenderViewContextMenu::CreateContextMenu(contents()));
  menu->Init();
  EXPECT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_TRANSLATE));
  EXPECT_FALSE(menu->IsCommandIdEnabled(IDC_CONTENT_CONTEXT_TRANSLATE));

  // Test that selecting translate in the context menu WHILE the page is being
  // translated does nothing (this could happen if autotranslate kicks-in and
  // the user selects the menu while the translation is being performed).
  SimulateNavigation(GURL("http://www.google.es"), 1, L"El Google", "es");
  TranslateInfoBarDelegate2* infobar = GetTranslateInfoBar();
  ASSERT_TRUE(infobar != NULL);
  infobar->Translate();
  EXPECT_TRUE(GetTranslateMessage(&page_id, &original_lang, &target_lang));
  EXPECT_EQ(1, page_id);
  process()->sink().ClearMessages();
  menu.reset(TestRenderViewContextMenu::CreateContextMenu(contents()));
  menu->Init();
  EXPECT_TRUE(menu->IsCommandIdEnabled(IDC_CONTENT_CONTEXT_TRANSLATE));
  menu->ExecuteCommand(IDC_CONTENT_CONTEXT_TRANSLATE);
  // No message expected since the translation should have been ignored.
  EXPECT_FALSE(GetTranslateMessage(&page_id, &original_lang, &target_lang));

  // Now test that selecting translate in the context menu AFTER the page has
  // been translated does nothing.
  SimulateNavigation(GURL("http://www.google.de"), 2, L"Das Google", "de");
  infobar = GetTranslateInfoBar();
  ASSERT_TRUE(infobar != NULL);
  infobar->Translate();
  EXPECT_TRUE(GetTranslateMessage(&page_id, &original_lang, &target_lang));
  EXPECT_EQ(2, page_id);
  process()->sink().ClearMessages();
  menu.reset(TestRenderViewContextMenu::CreateContextMenu(contents()));
  menu->Init();
  EXPECT_TRUE(menu->IsCommandIdEnabled(IDC_CONTENT_CONTEXT_TRANSLATE));
  rvh()->TestOnMessageReceived(ViewHostMsg_PageTranslated(0, 0, "de", "en",
      TranslateErrors::NONE));
  menu->ExecuteCommand(IDC_CONTENT_CONTEXT_TRANSLATE);
  // No message expected since the translation should have been ignored.
  EXPECT_FALSE(GetTranslateMessage(&page_id, &original_lang, &target_lang));

  // Test that the translate context menu is disabled when the page is in the
  // same language as the UI.
  SimulateNavigation(url, 0, L"Google", "en");
  menu.reset(TestRenderViewContextMenu::CreateContextMenu(contents()));
  menu->Init();
  EXPECT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_TRANSLATE));
  EXPECT_FALSE(menu->IsCommandIdEnabled(IDC_CONTENT_CONTEXT_TRANSLATE));
}