• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ContentTranslateDriver(content::NavigationController * nav_controller)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 
~ContentTranslateDriver()43 ContentTranslateDriver::~ContentTranslateDriver() {}
44 
AddObserver(Observer * observer)45 void ContentTranslateDriver::AddObserver(Observer* observer) {
46   observer_list_.AddObserver(observer);
47 }
48 
RemoveObserver(Observer * observer)49 void ContentTranslateDriver::RemoveObserver(Observer* observer) {
50   observer_list_.RemoveObserver(observer);
51 }
52 
InitiateTranslation(const std::string & page_lang,int attempt)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 
IsLinkNavigation()80 bool ContentTranslateDriver::IsLinkNavigation() {
81   return navigation_controller_ && navigation_controller_->GetActiveEntry() &&
82          navigation_controller_->GetActiveEntry()->GetTransitionType() ==
83              ui::PAGE_TRANSITION_LINK;
84 }
85 
OnTranslateEnabledChanged()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 
OnIsPageTranslatedChanged()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 
TranslatePage(int page_seq_no,const std::string & translate_script,const std::string & source_lang,const std::string & target_lang)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 
RevertTranslation(int page_seq_no)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 
IsOffTheRecord()121 bool ContentTranslateDriver::IsOffTheRecord() {
122   return navigation_controller_->GetBrowserContext()->IsOffTheRecord();
123 }
124 
GetContentsMimeType()125 const std::string& ContentTranslateDriver::GetContentsMimeType() {
126   return navigation_controller_->GetWebContents()->GetContentsMimeType();
127 }
128 
GetLastCommittedURL()129 const GURL& ContentTranslateDriver::GetLastCommittedURL() {
130   return navigation_controller_->GetWebContents()->GetLastCommittedURL();
131 }
132 
GetActiveURL()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 
GetVisibleURL()140 const GURL& ContentTranslateDriver::GetVisibleURL() {
141   return navigation_controller_->GetWebContents()->GetVisibleURL();
142 }
143 
HasCurrentPage()144 bool ContentTranslateDriver::HasCurrentPage() {
145   return (navigation_controller_->GetActiveEntry() != NULL);
146 }
147 
OpenUrlInNewTab(const GURL & url)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 
NavigationEntryCommitted(const content::LoadCommittedDetails & load_details)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 
DidNavigateAnyFrame(const content::LoadCommittedDetails & details,const content::FrameNavigateParams & params)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 
OnMessageReceived(const IPC::Message & message)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 
OnTranslateAssignedSequenceNumber(int page_seq_no)232 void ContentTranslateDriver::OnTranslateAssignedSequenceNumber(
233     int page_seq_no) {
234   translate_manager_->set_current_seq_no(page_seq_no);
235 }
236 
OnLanguageDetermined(const LanguageDetectionDetails & details,bool page_needs_translation)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 
OnPageTranslated(const std::string & original_lang,const std::string & translated_lang,TranslateErrors::Type error_type)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