| // 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 |