blob: 04f387c7da400692fbdec796a96187227726facf [file] [log] [blame]
// 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/ios/browser/language_detection_controller.h"
#include <string>
#include "base/bind.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/time/time.h"
#include "components/prefs/pref_member.h"
#include "components/translate/core/common/translate_pref_names.h"
#include "components/translate/core/language_detection/language_detection_util.h"
#import "components/translate/ios/browser/js_language_detection_manager.h"
#include "components/translate/ios/browser/string_clipping_util.h"
#import "ios/web/public/url_scheme_util.h"
#include "ios/web/public/web_state/web_state.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace translate {
namespace {
// Name for the UMA metric used to track text extraction time.
const char kTranslateCaptureText[] = "Translate.CaptureText";
// Prefix for the language detection javascript commands. Must be kept in sync
// with language_detection.js.
const char kCommandPrefix[] = "languageDetection";
}
LanguageDetectionController::LanguageDetectionController(
web::WebState* web_state,
JsLanguageDetectionManager* manager,
PrefService* prefs)
: web::WebStateObserver(web_state),
js_manager_(manager),
weak_method_factory_(this) {
DCHECK(web::WebStateObserver::web_state());
DCHECK(js_manager_);
translate_enabled_.Init(prefs::kEnableTranslate, prefs);
web_state->AddScriptCommandCallback(
base::Bind(&LanguageDetectionController::OnTextCaptured,
base::Unretained(this)),
kCommandPrefix);
}
LanguageDetectionController::~LanguageDetectionController() {
}
std::unique_ptr<LanguageDetectionController::CallbackList::Subscription>
LanguageDetectionController::RegisterLanguageDetectionCallback(
const Callback& callback) {
return language_detection_callbacks_.Add(callback);
}
void LanguageDetectionController::StartLanguageDetection() {
if (!translate_enabled_.GetValue())
return; // Translate disabled in preferences.
DCHECK(web_state());
const GURL& url = web_state()->GetVisibleURL();
if (!web::UrlHasWebScheme(url) || !web_state()->ContentIsHTML())
return;
[js_manager_ inject];
[js_manager_ startLanguageDetection];
}
bool LanguageDetectionController::OnTextCaptured(
const base::DictionaryValue& command,
const GURL& url,
bool interacting) {
std::string textCapturedCommand;
if (!command.GetString("command", &textCapturedCommand) ||
textCapturedCommand != "languageDetection.textCaptured" ||
!command.HasKey("translationAllowed")) {
NOTREACHED();
return false;
}
bool translation_allowed = false;
command.GetBoolean("translationAllowed", &translation_allowed);
if (!translation_allowed) {
// Translation not allowed by the page. Done processing.
return true;
}
if (!command.HasKey("captureTextTime") || !command.HasKey("htmlLang") ||
!command.HasKey("httpContentLanguage")) {
NOTREACHED();
return false;
}
double capture_text_time = 0;
command.GetDouble("captureTextTime", &capture_text_time);
UMA_HISTOGRAM_TIMES(kTranslateCaptureText,
base::TimeDelta::FromMillisecondsD(capture_text_time));
std::string html_lang;
command.GetString("htmlLang", &html_lang);
std::string http_content_language;
command.GetString("httpContentLanguage", &http_content_language);
// If there is no language defined in httpEquiv, use the HTTP header.
if (http_content_language.empty())
http_content_language = web_state()->GetContentLanguageHeader();
[js_manager_ retrieveBufferedTextContent:
base::Bind(&LanguageDetectionController::OnTextRetrieved,
weak_method_factory_.GetWeakPtr(),
http_content_language, html_lang)];
return true;
}
void LanguageDetectionController::OnTextRetrieved(
const std::string& http_content_language,
const std::string& html_lang,
const base::string16& text_content) {
std::string language = translate::DeterminePageLanguage(
http_content_language, html_lang,
GetStringByClippingLastWord(text_content,
language_detection::kMaxIndexChars),
nullptr /* cld_language */, nullptr /* is_cld_reliable */);
if (language.empty())
return; // No language detected.
DetectionDetails details;
details.content_language = http_content_language;
details.html_root_language = html_lang;
details.adopted_language = language;
language_detection_callbacks_.Notify(details);
}
// web::WebStateObserver implementation:
void LanguageDetectionController::PageLoaded(
web::PageLoadCompletionStatus load_completion_status) {
if (load_completion_status == web::PageLoadCompletionStatus::SUCCESS)
StartLanguageDetection();
}
void LanguageDetectionController::UrlHashChanged() {
StartLanguageDetection();
}
void LanguageDetectionController::HistoryStateChanged() {
StartLanguageDetection();
}
void LanguageDetectionController::WebStateDestroyed() {
web_state()->RemoveScriptCommandCallback(kCommandPrefix);
}
} // namespace translate