blob: 24376c16d711385043017b4f44e6a4e94396375c [file] [log] [blame]
// Copyright 2014 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 "components/translate/content/browser/content_translate_driver.h"
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/translate/content/common/translate_messages.h"
#include "components/translate/core/browser/translate_download_manager.h"
#include "components/translate/core/browser/translate_manager.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/page_navigator.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/referrer.h"
#include "net/http/http_status_code.h"
#include "url/gurl.h"
namespace {
// The maximum number of attempts we'll do to see if the page has finshed
// loading before giving up the translation
const int kMaxTranslateLoadCheckAttempts = 20;
} // namespace
namespace translate {
ContentTranslateDriver::ContentTranslateDriver(
content::NavigationController* nav_controller)
: content::WebContentsObserver(nav_controller->GetWebContents()),
navigation_controller_(nav_controller),
translate_manager_(NULL),
max_reload_check_attempts_(kMaxTranslateLoadCheckAttempts),
weak_pointer_factory_(this) {
DCHECK(navigation_controller_);
}
ContentTranslateDriver::~ContentTranslateDriver() {}
void ContentTranslateDriver::AddObserver(Observer* observer) {
observer_list_.AddObserver(observer);
}
void ContentTranslateDriver::RemoveObserver(Observer* observer) {
observer_list_.RemoveObserver(observer);
}
void ContentTranslateDriver::InitiateTranslation(const std::string& page_lang,
int attempt) {
if (translate_manager_->GetLanguageState().translation_pending())
return;
// During a reload we need web content to be available before the
// translate script is executed. Otherwise we will run the translate script on
// an empty DOM which will fail. Therefore we wait a bit to see if the page
// has finished.
if (web_contents()->IsLoading() && attempt < max_reload_check_attempts_) {
int backoff = attempt * kMaxTranslateLoadCheckAttempts;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::Bind(&ContentTranslateDriver::InitiateTranslation,
weak_pointer_factory_.GetWeakPtr(), page_lang, attempt + 1),
base::TimeDelta::FromMilliseconds(backoff));
return;
}
translate_manager_->InitiateTranslation(
translate::TranslateDownloadManager::GetLanguageCode(page_lang));
}
// TranslateDriver methods
bool ContentTranslateDriver::IsLinkNavigation() {
return navigation_controller_ &&
navigation_controller_->GetLastCommittedEntry() &&
ui::PageTransitionCoreTypeIs(
navigation_controller_->GetLastCommittedEntry()
->GetTransitionType(),
ui::PAGE_TRANSITION_LINK);
}
void ContentTranslateDriver::OnTranslateEnabledChanged() {
content::WebContents* web_contents = navigation_controller_->GetWebContents();
FOR_EACH_OBSERVER(
Observer, observer_list_, OnTranslateEnabledChanged(web_contents));
}
void ContentTranslateDriver::OnIsPageTranslatedChanged() {
content::WebContents* web_contents =
navigation_controller_->GetWebContents();
FOR_EACH_OBSERVER(
Observer, observer_list_, OnIsPageTranslatedChanged(web_contents));
}
void ContentTranslateDriver::TranslatePage(int page_seq_no,
const std::string& translate_script,
const std::string& source_lang,
const std::string& target_lang) {
content::WebContents* web_contents = navigation_controller_->GetWebContents();
web_contents->GetMainFrame()->Send(new ChromeFrameMsg_TranslatePage(
web_contents->GetMainFrame()->GetRoutingID(), page_seq_no,
translate_script, source_lang, target_lang));
}
void ContentTranslateDriver::RevertTranslation(int page_seq_no) {
content::WebContents* web_contents = navigation_controller_->GetWebContents();
web_contents->GetMainFrame()->Send(new ChromeFrameMsg_RevertTranslation(
web_contents->GetMainFrame()->GetRoutingID(), page_seq_no));
}
bool ContentTranslateDriver::IsOffTheRecord() {
return navigation_controller_->GetBrowserContext()->IsOffTheRecord();
}
const std::string& ContentTranslateDriver::GetContentsMimeType() {
return navigation_controller_->GetWebContents()->GetContentsMimeType();
}
const GURL& ContentTranslateDriver::GetLastCommittedURL() {
return navigation_controller_->GetWebContents()->GetLastCommittedURL();
}
const GURL& ContentTranslateDriver::GetVisibleURL() {
return navigation_controller_->GetWebContents()->GetVisibleURL();
}
bool ContentTranslateDriver::HasCurrentPage() {
return (navigation_controller_->GetLastCommittedEntry() != NULL);
}
void ContentTranslateDriver::OpenUrlInNewTab(const GURL& url) {
content::OpenURLParams params(url,
content::Referrer(),
NEW_FOREGROUND_TAB,
ui::PAGE_TRANSITION_LINK,
false);
navigation_controller_->GetWebContents()->OpenURL(params);
}
// content::WebContentsObserver methods
void ContentTranslateDriver::NavigationEntryCommitted(
const content::LoadCommittedDetails& load_details) {
// Check whether this is a reload: When doing a page reload, the
// TranslateLanguageDetermined IPC is not sent so the translation needs to be
// explicitly initiated.
content::NavigationEntry* entry =
web_contents()->GetController().GetLastCommittedEntry();
if (!entry) {
NOTREACHED();
return;
}
// If the navigation happened while offline don't show the translate
// bar since there will be nothing to translate.
if (load_details.http_status_code == 0 ||
load_details.http_status_code == net::HTTP_INTERNAL_SERVER_ERROR) {
return;
}
if (!load_details.is_main_frame &&
translate_manager_->GetLanguageState().translation_declined()) {
// Some sites (such as Google map) may trigger sub-frame navigations
// when the user interacts with the page. We don't want to show a new
// infobar if the user already dismissed one in that case.
return;
}
// If not a reload, return.
if (!ui::PageTransitionCoreTypeIs(entry->GetTransitionType(),
ui::PAGE_TRANSITION_RELOAD) &&
load_details.type != content::NAVIGATION_TYPE_SAME_PAGE) {
return;
}
if (!translate_manager_->GetLanguageState().page_needs_translation())
return;
// Note that we delay it as the ordering of the processing of this callback
// by WebContentsObservers is undefined and might result in the current
// infobars being removed. Since the translation initiation process might add
// an infobar, it must be done after that.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(&ContentTranslateDriver::InitiateTranslation,
weak_pointer_factory_.GetWeakPtr(),
translate_manager_->GetLanguageState().original_language(),
0));
}
void ContentTranslateDriver::DidNavigateAnyFrame(
content::RenderFrameHost* render_frame_host,
const content::LoadCommittedDetails& details,
const content::FrameNavigateParams& params) {
// Let the LanguageState clear its state.
const bool reload =
ui::PageTransitionCoreTypeIs(details.entry->GetTransitionType(),
ui::PAGE_TRANSITION_RELOAD) ||
details.type == content::NAVIGATION_TYPE_SAME_PAGE;
translate_manager_->GetLanguageState().DidNavigate(
details.is_in_page, details.is_main_frame, reload);
}
bool ContentTranslateDriver::OnMessageReceived(
const IPC::Message& message,
content::RenderFrameHost* render_frame_host) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(ContentTranslateDriver, message)
IPC_MESSAGE_HANDLER(ChromeFrameHostMsg_TranslateAssignedSequenceNumber,
OnTranslateAssignedSequenceNumber)
IPC_MESSAGE_HANDLER(ChromeFrameHostMsg_TranslateLanguageDetermined,
OnLanguageDetermined)
IPC_MESSAGE_HANDLER(ChromeFrameHostMsg_PageTranslated, OnPageTranslated)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void ContentTranslateDriver::OnTranslateAssignedSequenceNumber(
int page_seq_no) {
translate_manager_->set_current_seq_no(page_seq_no);
}
void ContentTranslateDriver::OnLanguageDetermined(
const LanguageDetectionDetails& details,
bool page_needs_translation) {
translate_manager_->GetLanguageState().LanguageDetermined(
details.adopted_language, page_needs_translation);
if (web_contents())
translate_manager_->InitiateTranslation(details.adopted_language);
FOR_EACH_OBSERVER(Observer, observer_list_, OnLanguageDetermined(details));
}
void ContentTranslateDriver::OnPageTranslated(
const std::string& original_lang,
const std::string& translated_lang,
TranslateErrors::Type error_type) {
translate_manager_->PageTranslated(
original_lang, translated_lang, error_type);
FOR_EACH_OBSERVER(
Observer,
observer_list_,
OnPageTranslated(original_lang, translated_lang, error_type));
}
} // namespace translate