Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / components / translate / ios / browser / ios_translate_driver.mm
blob7072616e0c2cc125d7d9aec46ee19cb3e40eed91
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.
5 #include "components/translate/ios/browser/ios_translate_driver.h"
7 #include "base/bind.h"
8 #include "base/location.h"
9 #include "base/logging.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/strings/sys_string_conversions.h"
12 #include "base/time/time.h"
13 #include "components/translate/core/browser/translate_client.h"
14 #include "components/translate/core/browser/translate_manager.h"
15 #include "components/translate/core/common/translate_constants.h"
16 #include "components/translate/core/common/translate_errors.h"
17 #include "components/translate/core/common/translate_metrics.h"
18 #import "components/translate/ios/browser/js_language_detection_manager.h"
19 #import "components/translate/ios/browser/js_translate_manager.h"
20 #import "components/translate/ios/browser/language_detection_controller.h"
21 #import "components/translate/ios/browser/translate_controller.h"
22 #include "ios/web/public/browser_state.h"
23 #include "ios/web/public/load_committed_details.h"
24 #include "ios/web/public/navigation_item.h"
25 #include "ios/web/public/navigation_manager.h"
26 #include "ios/web/public/referrer.h"
27 #include "ios/web/public/web_state/js/crw_js_injection_receiver.h"
28 #include "ios/web/public/web_state/web_state.h"
29 #include "ui/base/page_transition_types.h"
30 #include "ui/base/window_open_disposition.h"
31 #include "url/gurl.h"
33 namespace translate {
35 namespace {
36 // The delay we wait in milliseconds before checking whether the translation has
37 // finished.
38 // Note: This should be kept in sync with the constant of the same name in
39 // translate_ios.js.
40 const int kTranslateStatusCheckDelayMs = 400;
41 // Language name passed to the Translate element for it to detect the language.
42 const char kAutoDetectionLanguage[] = "auto";
44 }  // namespace
46 IOSTranslateDriver::IOSTranslateDriver(
47     web::WebState* web_state,
48     web::NavigationManager* navigation_manager,
49     TranslateManager* translate_manager)
50     : web::WebStateObserver(web_state),
51       navigation_manager_(navigation_manager),
52       translate_manager_(translate_manager->GetWeakPtr()),
53       page_seq_no_(0),
54       pending_page_seq_no_(0),
55       weak_method_factory_(this) {
56   DCHECK(navigation_manager_);
57   DCHECK(translate_manager_);
58   DCHECK(web::WebStateObserver::web_state());
60   CRWJSInjectionReceiver* receiver = web_state->GetJSInjectionReceiver();
61   DCHECK(receiver);
63   // Create the language detection controller.
64   JsLanguageDetectionManager* language_detection_manager =
65       static_cast<JsLanguageDetectionManager*>(
66           [receiver instanceOfClass:[JsLanguageDetectionManager class]]);
67   language_detection_controller_.reset(new LanguageDetectionController(
68       web_state, language_detection_manager,
69       translate_manager_->translate_client()->GetPrefs()));
70   language_detection_callback_subscription_ =
71       language_detection_controller_->RegisterLanguageDetectionCallback(
72           base::Bind(&IOSTranslateDriver::OnLanguageDetermined,
73                      base::Unretained(this)));
74   // Create the translate controller.
75   JsTranslateManager* js_translate_manager = static_cast<JsTranslateManager*>(
76       [receiver instanceOfClass:[JsTranslateManager class]]);
77   translate_controller_.reset(
78       new TranslateController(web_state, js_translate_manager));
79   translate_controller_->set_observer(this);
82 IOSTranslateDriver::~IOSTranslateDriver() {
85 void IOSTranslateDriver::OnLanguageDetermined(
86     const LanguageDetectionController::DetectionDetails& details) {
87   if (!translate_manager_)
88     return;
89   translate_manager_->GetLanguageState().LanguageDetermined(
90       details.adopted_language, true);
92   if (web_state())
93     translate_manager_->InitiateTranslation(details.adopted_language);
96 // web::WebStateObserver methods
98 void IOSTranslateDriver::NavigationItemCommitted(
99     const web::LoadCommittedDetails& load_details) {
100   // Interrupt pending translations and reset various data when a navigation
101   // happens. Desktop does it by tracking changes in the page ID, and
102   // through WebContentObserver, but these concepts do not exist on iOS.
103   if (!load_details.is_in_page) {
104     ++page_seq_no_;
105     translate_manager_->set_current_seq_no(page_seq_no_);
106   }
108   // TODO(droger): support navigation types, like content/ does.
109   const bool reload = ui::PageTransitionCoreTypeIs(
110       load_details.item->GetTransitionType(), ui::PAGE_TRANSITION_RELOAD);
111   translate_manager_->GetLanguageState().DidNavigate(load_details.is_in_page,
112                                                      true, reload);
115 // TranslateDriver methods
117 bool IOSTranslateDriver::IsLinkNavigation() {
118   return navigation_manager_->GetVisibleItem() &&
119          ui::PageTransitionCoreTypeIs(
120              navigation_manager_->GetVisibleItem()->GetTransitionType(),
121              ui::PAGE_TRANSITION_LINK);
124 void IOSTranslateDriver::OnTranslateEnabledChanged() {
127 void IOSTranslateDriver::OnIsPageTranslatedChanged() {
130 void IOSTranslateDriver::TranslatePage(int page_seq_no,
131                                        const std::string& translate_script,
132                                        const std::string& source_lang,
133                                        const std::string& target_lang) {
134   if (page_seq_no != page_seq_no_)
135     return;  // The user navigated away.
136   source_language_ = source_lang;
137   target_language_ = target_lang;
138   pending_page_seq_no_ = page_seq_no;
139   translate_controller_->InjectTranslateScript(translate_script);
142 void IOSTranslateDriver::RevertTranslation(int page_seq_no) {
143   if (page_seq_no != page_seq_no_)
144     return;  // The user navigated away.
145   translate_controller_->RevertTranslation();
148 bool IOSTranslateDriver::IsOffTheRecord() {
149   return navigation_manager_->GetBrowserState()->IsOffTheRecord();
152 const std::string& IOSTranslateDriver::GetContentsMimeType() {
153   return web_state()->GetContentsMimeType();
156 const GURL& IOSTranslateDriver::GetLastCommittedURL() {
157   return web_state()->GetLastCommittedURL();
160 const GURL& IOSTranslateDriver::GetVisibleURL() {
161   return web_state()->GetVisibleURL();
164 bool IOSTranslateDriver::HasCurrentPage() {
165   return (navigation_manager_->GetVisibleItem() != nullptr);
168 void IOSTranslateDriver::OpenUrlInNewTab(const GURL& url) {
169   web::WebState::OpenURLParams params(url, web::Referrer(), NEW_FOREGROUND_TAB,
170                                       ui::PAGE_TRANSITION_LINK, false);
171   web_state()->OpenURL(params);
174 void IOSTranslateDriver::TranslationDidSucceed(
175     const std::string& source_lang,
176     const std::string& target_lang,
177     int page_seq_no,
178     const std::string& original_page_language,
179     double translation_time) {
180   if (!IsPageValid(page_seq_no))
181     return;
182   std::string actual_source_lang;
183   translate::TranslateErrors::Type translate_errors = TranslateErrors::NONE;
184   // Translation was successfull; if it was auto, retrieve the source
185   // language the Translate Element detected.
186   if (source_lang == kAutoDetectionLanguage) {
187     actual_source_lang = original_page_language;
188     if (actual_source_lang.empty()) {
189       translate_errors = TranslateErrors::UNKNOWN_LANGUAGE;
190     } else if (actual_source_lang == target_lang) {
191       translate_errors = TranslateErrors::IDENTICAL_LANGUAGES;
192     }
193   } else {
194     actual_source_lang = source_lang;
195   }
196   if (translate_errors == TranslateErrors::NONE)
197     translate::ReportTimeToTranslate(translation_time);
198   // Notify the manage of completion.
199   translate_manager_->PageTranslated(actual_source_lang, target_lang,
200                                      translate_errors);
203 void IOSTranslateDriver::CheckTranslateStatus(
204     const std::string& source_language,
205     const std::string& target_language,
206     int page_seq_no) {
207   if (!IsPageValid(page_seq_no))
208     return;
209   translate_controller_->CheckTranslateStatus();
212 bool IOSTranslateDriver::IsPageValid(int page_seq_no) const {
213   bool user_navigated_away = page_seq_no != page_seq_no_;
214   return !user_navigated_away && web_state();
217 // TranslateController::Observer implementation.
219 void IOSTranslateDriver::OnTranslateScriptReady(bool success,
220                                                 double load_time,
221                                                 double ready_time) {
222   if (!IsPageValid(pending_page_seq_no_))
223     return;
225   if (!success) {
226     translate_manager_->PageTranslated(source_language_, target_language_,
227                                        TranslateErrors::INITIALIZATION_ERROR);
228     return;
229   }
231   translate::ReportTimeToLoad(load_time);
232   translate::ReportTimeToBeReady(ready_time);
233   const char kAutoDetectionLanguage[] = "auto";
234   std::string source = (source_language_ != translate::kUnknownLanguageCode)
235                            ? source_language_
236                            : kAutoDetectionLanguage;
237   translate_controller_->StartTranslation(source_language_, target_language_);
238   // Check the status of the translation -- after a delay.
239   base::MessageLoop::current()->PostDelayedTask(
240       FROM_HERE, base::Bind(&IOSTranslateDriver::CheckTranslateStatus,
241                             weak_method_factory_.GetWeakPtr(), source_language_,
242                             target_language_, pending_page_seq_no_),
243       base::TimeDelta::FromMilliseconds(kTranslateStatusCheckDelayMs));
246 void IOSTranslateDriver::OnTranslateComplete(
247     bool success,
248     const std::string& original_language,
249     double translation_time) {
250   if (!IsPageValid(pending_page_seq_no_))
251     return;
253   if (!success) {
254     // TODO(toyoshim): Check |errorCode| of translate.js and notify it here.
255     translate_manager_->PageTranslated(source_language_, target_language_,
256                                        TranslateErrors::TRANSLATION_ERROR);
257   }
259   TranslationDidSucceed(source_language_, target_language_,
260                         pending_page_seq_no_, original_language,
261                         translation_time);
264 }  // namespace translate