Roll src/third_party/WebKit f36d5e0:68b67cd (svn 193299:193303)
[chromium-blink-merge.git] / components / translate / ios / browser / ios_translate_driver.mm
blobd6d21172db248b3fe2398c9cde564846ebf779df
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::GetActiveURL() {
161   web::NavigationItem* item = navigation_manager_->GetVisibleItem();
162   if (!item)
163     return GURL::EmptyGURL();
164   return item->GetURL();
167 const GURL& IOSTranslateDriver::GetVisibleURL() {
168   return web_state()->GetVisibleURL();
171 bool IOSTranslateDriver::HasCurrentPage() {
172   return (navigation_manager_->GetVisibleItem() != nullptr);
175 void IOSTranslateDriver::OpenUrlInNewTab(const GURL& url) {
176   web::WebState::OpenURLParams params(url, web::Referrer(), NEW_FOREGROUND_TAB,
177                                       ui::PAGE_TRANSITION_LINK, false);
178   web_state()->OpenURL(params);
181 void IOSTranslateDriver::TranslationDidSucceed(
182     const std::string& source_lang,
183     const std::string& target_lang,
184     int page_seq_no,
185     const std::string& original_page_language,
186     double translation_time) {
187   if (!IsPageValid(page_seq_no))
188     return;
189   std::string actual_source_lang;
190   translate::TranslateErrors::Type translate_errors = TranslateErrors::NONE;
191   // Translation was successfull; if it was auto, retrieve the source
192   // language the Translate Element detected.
193   if (source_lang == kAutoDetectionLanguage) {
194     actual_source_lang = original_page_language;
195     if (actual_source_lang.empty()) {
196       translate_errors = TranslateErrors::UNKNOWN_LANGUAGE;
197     } else if (actual_source_lang == target_lang) {
198       translate_errors = TranslateErrors::IDENTICAL_LANGUAGES;
199     }
200   } else {
201     actual_source_lang = source_lang;
202   }
203   if (translate_errors == TranslateErrors::NONE)
204     translate::ReportTimeToTranslate(translation_time);
205   // Notify the manage of completion.
206   translate_manager_->PageTranslated(actual_source_lang, target_lang,
207                                      translate_errors);
210 void IOSTranslateDriver::CheckTranslateStatus(
211     const std::string& source_language,
212     const std::string& target_language,
213     int page_seq_no) {
214   if (!IsPageValid(page_seq_no))
215     return;
216   translate_controller_->CheckTranslateStatus();
219 bool IOSTranslateDriver::IsPageValid(int page_seq_no) const {
220   bool user_navigated_away = page_seq_no != page_seq_no_;
221   return !user_navigated_away && web_state();
224 // TranslateController::Observer implementation.
226 void IOSTranslateDriver::OnTranslateScriptReady(bool success,
227                                                 double load_time,
228                                                 double ready_time) {
229   if (!IsPageValid(pending_page_seq_no_))
230     return;
232   if (!success) {
233     translate_manager_->PageTranslated(source_language_, target_language_,
234                                        TranslateErrors::INITIALIZATION_ERROR);
235     return;
236   }
238   translate::ReportTimeToLoad(load_time);
239   translate::ReportTimeToBeReady(ready_time);
240   const char kAutoDetectionLanguage[] = "auto";
241   std::string source = (source_language_ != translate::kUnknownLanguageCode)
242                            ? source_language_
243                            : kAutoDetectionLanguage;
244   translate_controller_->StartTranslation(source_language_, target_language_);
245   // Check the status of the translation -- after a delay.
246   base::MessageLoop::current()->PostDelayedTask(
247       FROM_HERE, base::Bind(&IOSTranslateDriver::CheckTranslateStatus,
248                             weak_method_factory_.GetWeakPtr(), source_language_,
249                             target_language_, pending_page_seq_no_),
250       base::TimeDelta::FromMilliseconds(kTranslateStatusCheckDelayMs));
253 void IOSTranslateDriver::OnTranslateComplete(
254     bool success,
255     const std::string& original_language,
256     double translation_time) {
257   if (!IsPageValid(pending_page_seq_no_))
258     return;
260   if (!success) {
261     // TODO(toyoshim): Check |errorCode| of translate.js and notify it here.
262     translate_manager_->PageTranslated(source_language_, target_language_,
263                                        TranslateErrors::TRANSLATION_ERROR);
264   }
266   TranslationDidSucceed(source_language_, target_language_,
267                         pending_page_seq_no_, original_language,
268                         translation_time);
271 }  // namespace translate