Home | History | Annotate | Download | only in browser
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "components/translate/content/browser/content_translate_driver.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/logging.h"
      9 #include "components/translate/content/common/translate_messages.h"
     10 #include "components/translate/core/browser/translate_download_manager.h"
     11 #include "components/translate/core/browser/translate_manager.h"
     12 #include "content/public/browser/browser_context.h"
     13 #include "content/public/browser/navigation_controller.h"
     14 #include "content/public/browser/navigation_details.h"
     15 #include "content/public/browser/navigation_entry.h"
     16 #include "content/public/browser/page_navigator.h"
     17 #include "content/public/browser/render_view_host.h"
     18 #include "content/public/browser/web_contents.h"
     19 #include "content/public/common/referrer.h"
     20 #include "net/http/http_status_code.h"
     21 #include "url/gurl.h"
     22 
     23 namespace {
     24 
     25 // The maximum number of attempts we'll do to see if the page has finshed
     26 // loading before giving up the translation
     27 const int kMaxTranslateLoadCheckAttempts = 20;
     28 
     29 }  // namespace
     30 
     31 namespace translate {
     32 
     33 ContentTranslateDriver::ContentTranslateDriver(
     34     content::NavigationController* nav_controller)
     35     : content::WebContentsObserver(nav_controller->GetWebContents()),
     36       navigation_controller_(nav_controller),
     37       translate_manager_(NULL),
     38       max_reload_check_attempts_(kMaxTranslateLoadCheckAttempts),
     39       weak_pointer_factory_(this) {
     40   DCHECK(navigation_controller_);
     41 }
     42 
     43 ContentTranslateDriver::~ContentTranslateDriver() {}
     44 
     45 void ContentTranslateDriver::AddObserver(Observer* observer) {
     46   observer_list_.AddObserver(observer);
     47 }
     48 
     49 void ContentTranslateDriver::RemoveObserver(Observer* observer) {
     50   observer_list_.RemoveObserver(observer);
     51 }
     52 
     53 void ContentTranslateDriver::InitiateTranslation(const std::string& page_lang,
     54                                                  int attempt) {
     55   if (translate_manager_->GetLanguageState().translation_pending())
     56     return;
     57 
     58   // During a reload we need web content to be available before the
     59   // translate script is executed. Otherwise we will run the translate script on
     60   // an empty DOM which will fail. Therefore we wait a bit to see if the page
     61   // has finished.
     62   if (web_contents()->IsLoading() && attempt < max_reload_check_attempts_) {
     63     int backoff = attempt * kMaxTranslateLoadCheckAttempts;
     64     base::MessageLoop::current()->PostDelayedTask(
     65         FROM_HERE,
     66         base::Bind(&ContentTranslateDriver::InitiateTranslation,
     67                    weak_pointer_factory_.GetWeakPtr(),
     68                    page_lang,
     69                    attempt + 1),
     70         base::TimeDelta::FromMilliseconds(backoff));
     71     return;
     72   }
     73 
     74   translate_manager_->InitiateTranslation(
     75       translate::TranslateDownloadManager::GetLanguageCode(page_lang));
     76 }
     77 
     78 // TranslateDriver methods
     79 
     80 bool ContentTranslateDriver::IsLinkNavigation() {
     81   return navigation_controller_ && navigation_controller_->GetActiveEntry() &&
     82          navigation_controller_->GetActiveEntry()->GetTransitionType() ==
     83              ui::PAGE_TRANSITION_LINK;
     84 }
     85 
     86 void ContentTranslateDriver::OnTranslateEnabledChanged() {
     87   content::WebContents* web_contents = navigation_controller_->GetWebContents();
     88   FOR_EACH_OBSERVER(
     89       Observer, observer_list_, OnTranslateEnabledChanged(web_contents));
     90 }
     91 
     92 void ContentTranslateDriver::OnIsPageTranslatedChanged() {
     93     content::WebContents* web_contents =
     94         navigation_controller_->GetWebContents();
     95     FOR_EACH_OBSERVER(
     96         Observer, observer_list_, OnIsPageTranslatedChanged(web_contents));
     97 }
     98 
     99 void ContentTranslateDriver::TranslatePage(int page_seq_no,
    100                                            const std::string& translate_script,
    101                                            const std::string& source_lang,
    102                                            const std::string& target_lang) {
    103   content::WebContents* web_contents = navigation_controller_->GetWebContents();
    104   web_contents->GetRenderViewHost()->Send(
    105       new ChromeViewMsg_TranslatePage(
    106           web_contents->GetRenderViewHost()->GetRoutingID(),
    107           page_seq_no,
    108           translate_script,
    109           source_lang,
    110           target_lang));
    111 }
    112 
    113 void ContentTranslateDriver::RevertTranslation(int page_seq_no) {
    114   content::WebContents* web_contents = navigation_controller_->GetWebContents();
    115   web_contents->GetRenderViewHost()->Send(
    116       new ChromeViewMsg_RevertTranslation(
    117           web_contents->GetRenderViewHost()->GetRoutingID(),
    118           page_seq_no));
    119 }
    120 
    121 bool ContentTranslateDriver::IsOffTheRecord() {
    122   return navigation_controller_->GetBrowserContext()->IsOffTheRecord();
    123 }
    124 
    125 const std::string& ContentTranslateDriver::GetContentsMimeType() {
    126   return navigation_controller_->GetWebContents()->GetContentsMimeType();
    127 }
    128 
    129 const GURL& ContentTranslateDriver::GetLastCommittedURL() {
    130   return navigation_controller_->GetWebContents()->GetLastCommittedURL();
    131 }
    132 
    133 const GURL& ContentTranslateDriver::GetActiveURL() {
    134   content::NavigationEntry* entry = navigation_controller_->GetActiveEntry();
    135   if (!entry)
    136     return GURL::EmptyGURL();
    137   return entry->GetURL();
    138 }
    139 
    140 const GURL& ContentTranslateDriver::GetVisibleURL() {
    141   return navigation_controller_->GetWebContents()->GetVisibleURL();
    142 }
    143 
    144 bool ContentTranslateDriver::HasCurrentPage() {
    145   return (navigation_controller_->GetActiveEntry() != NULL);
    146 }
    147 
    148 void ContentTranslateDriver::OpenUrlInNewTab(const GURL& url) {
    149   content::OpenURLParams params(url,
    150                                 content::Referrer(),
    151                                 NEW_FOREGROUND_TAB,
    152                                 ui::PAGE_TRANSITION_LINK,
    153                                 false);
    154   navigation_controller_->GetWebContents()->OpenURL(params);
    155 }
    156 
    157 // content::WebContentsObserver methods
    158 
    159 void ContentTranslateDriver::NavigationEntryCommitted(
    160     const content::LoadCommittedDetails& load_details) {
    161   // Check whether this is a reload: When doing a page reload, the
    162   // TranslateLanguageDetermined IPC is not sent so the translation needs to be
    163   // explicitly initiated.
    164 
    165   content::NavigationEntry* entry =
    166       web_contents()->GetController().GetActiveEntry();
    167   if (!entry) {
    168     NOTREACHED();
    169     return;
    170   }
    171 
    172   // If the navigation happened while offline don't show the translate
    173   // bar since there will be nothing to translate.
    174   if (load_details.http_status_code == 0 ||
    175       load_details.http_status_code == net::HTTP_INTERNAL_SERVER_ERROR) {
    176     return;
    177   }
    178 
    179   if (!load_details.is_main_frame &&
    180       translate_manager_->GetLanguageState().translation_declined()) {
    181     // Some sites (such as Google map) may trigger sub-frame navigations
    182     // when the user interacts with the page.  We don't want to show a new
    183     // infobar if the user already dismissed one in that case.
    184     return;
    185   }
    186 
    187   // If not a reload, return.
    188   if (entry->GetTransitionType() != ui::PAGE_TRANSITION_RELOAD &&
    189       load_details.type != content::NAVIGATION_TYPE_SAME_PAGE) {
    190     return;
    191   }
    192 
    193   if (!translate_manager_->GetLanguageState().page_needs_translation())
    194     return;
    195 
    196   // Note that we delay it as the ordering of the processing of this callback
    197   // by WebContentsObservers is undefined and might result in the current
    198   // infobars being removed. Since the translation initiation process might add
    199   // an infobar, it must be done after that.
    200   base::MessageLoop::current()->PostTask(
    201       FROM_HERE,
    202       base::Bind(&ContentTranslateDriver::InitiateTranslation,
    203                  weak_pointer_factory_.GetWeakPtr(),
    204                  translate_manager_->GetLanguageState().original_language(),
    205                  0));
    206 }
    207 
    208 void ContentTranslateDriver::DidNavigateAnyFrame(
    209     const content::LoadCommittedDetails& details,
    210     const content::FrameNavigateParams& params) {
    211   // Let the LanguageState clear its state.
    212   const bool reload =
    213       details.entry->GetTransitionType() == ui::PAGE_TRANSITION_RELOAD ||
    214       details.type == content::NAVIGATION_TYPE_SAME_PAGE;
    215   translate_manager_->GetLanguageState().DidNavigate(
    216       details.is_in_page, details.is_main_frame, reload);
    217 }
    218 
    219 bool ContentTranslateDriver::OnMessageReceived(const IPC::Message& message) {
    220   bool handled = true;
    221   IPC_BEGIN_MESSAGE_MAP(ContentTranslateDriver, message)
    222   IPC_MESSAGE_HANDLER(ChromeViewHostMsg_TranslateAssignedSequenceNumber,
    223                       OnTranslateAssignedSequenceNumber)
    224   IPC_MESSAGE_HANDLER(ChromeViewHostMsg_TranslateLanguageDetermined,
    225                       OnLanguageDetermined)
    226   IPC_MESSAGE_HANDLER(ChromeViewHostMsg_PageTranslated, OnPageTranslated)
    227   IPC_MESSAGE_UNHANDLED(handled = false)
    228   IPC_END_MESSAGE_MAP()
    229   return handled;
    230 }
    231 
    232 void ContentTranslateDriver::OnTranslateAssignedSequenceNumber(
    233     int page_seq_no) {
    234   translate_manager_->set_current_seq_no(page_seq_no);
    235 }
    236 
    237 void ContentTranslateDriver::OnLanguageDetermined(
    238     const LanguageDetectionDetails& details,
    239     bool page_needs_translation) {
    240   translate_manager_->GetLanguageState().LanguageDetermined(
    241       details.adopted_language, page_needs_translation);
    242 
    243   if (web_contents())
    244     translate_manager_->InitiateTranslation(details.adopted_language);
    245 
    246   FOR_EACH_OBSERVER(Observer, observer_list_, OnLanguageDetermined(details));
    247 }
    248 
    249 void ContentTranslateDriver::OnPageTranslated(
    250     const std::string& original_lang,
    251     const std::string& translated_lang,
    252     TranslateErrors::Type error_type) {
    253   translate_manager_->PageTranslated(
    254       original_lang, translated_lang, error_type);
    255   FOR_EACH_OBSERVER(
    256       Observer,
    257       observer_list_,
    258       OnPageTranslated(original_lang, translated_lang, error_type));
    259 }
    260 
    261 }  // namespace translate
    262