// Copyright (c) 2011 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. #ifndef COMPONENTS_TRANSLATE_CONTENT_RENDERER_TRANSLATE_HELPER_H_ #define COMPONENTS_TRANSLATE_CONTENT_RENDERER_TRANSLATE_HELPER_H_ #include <string> #include "base/gtest_prod_util.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/strings/string16.h" #include "base/time/time.h" #include "components/translate/content/renderer/renderer_cld_data_provider.h" #include "components/translate/core/common/translate_errors.h" #include "content/public/renderer/render_view_observer.h" #include "url/gurl.h" namespace blink { class WebDocument; class WebFrame; } namespace content { class RendererCldDataProvider; } namespace translate { // This class deals with page translation. // There is one TranslateHelper per RenderView. // // This class provides metrics that allow tracking the user experience impact // of non-static CldDataProvider implementations. For background on the data // providers, please refer to the following documentation: // http://www.chromium.org/developers/how-tos/compact-language-detector-cld-data-source-configuration // // Available metrics (from the LanguageDetectionTiming enum): // 1. ON_TIME // Recorded if PageCaptured(...) is invoked after CLD is available. This is // the ideal case, indicating that CLD is available before it is needed. // 2. DEFERRED // Recorded if PageCaptured(...) is invoked before CLD is available. // Sub-optimal case indicating that CLD wasn't available when it was needed, // so the request for detection has been deferred until CLD is available or // until the user navigates to a different page. // 3. RESUMED // Recorded if CLD becomes available after a language detection request was // deferred, but before the user navigated to a different page. Language // detection is ultimately completed, it just didn't happen on time. // // Note that there is NOT a metric that records the number of times that // language detection had to be aborted because CLD never became available in // time. This is because there is no reasonable way to cover all the cases // under which this could occur, particularly the destruction of the renderer // for which this object was created. However, this value can be synthetically // derived, using the logic below. // // Every page load that triggers language detection will result in the // recording of exactly one of the first two events: ON_TIME or DEFERRED. If // CLD is available in time to satisfy the request, the third event (RESUMED) // will be recorded; thus, the number of times when language detection // ultimately fails because CLD isn't ever available is implied as the number of // times that detection is deferred minus the number of times that language // detection is late: // // count(FAILED) ~= count(DEFERRED) - count(RESUMED) // // Note that this is not 100% accurate: some renderer process are so short-lived // that language detection wouldn't have been relevant anyway, and so a failure // to detect the language in a timely manner might be completely innocuous. The // overall problem with language detection is that it isn't possible to know // whether it was required or not until after it has been performed! // // We use histograms for recording these metrics. On Android, the renderer can // be killed without the chance to clean up or transmit these histograms, // leading to dropped metrics. To work around this, this method forces an IPC // message to be sent to the browser process immediately. class TranslateHelper : public content::RenderViewObserver { public: explicit TranslateHelper(content::RenderView* render_view, int world_id, int extension_group, const std::string& extension_scheme); virtual ~TranslateHelper(); // Informs us that the page's text has been extracted. void PageCaptured(const base::string16& contents); // Lets the translation system know that we are preparing to navigate to // the specified URL. If there is anything that can or should be done before // this URL loads, this is the time to prepare for it. void PrepareForUrl(const GURL& url); protected: // The following methods are protected so they can be overridden in // unit-tests. void OnTranslatePage(int page_seq_no, const std::string& translate_script, const std::string& source_lang, const std::string& target_lang); void OnRevertTranslation(int page_seq_no); // Returns true if the translate library is available, meaning the JavaScript // has already been injected in that page. virtual bool IsTranslateLibAvailable(); // Returns true if the translate library has been initialized successfully. virtual bool IsTranslateLibReady(); // Returns true if the translation script has finished translating the page. virtual bool HasTranslationFinished(); // Returns true if the translation script has reported an error performing the // translation. virtual bool HasTranslationFailed(); // Starts the translation by calling the translate library. This method // should only be called when the translate script has been injected in the // page. Returns false if the call failed immediately. virtual bool StartTranslation(); // Asks the Translate element in the page what the language of the page is. // Can only be called if a translation has happened and was successful. // Returns the language code on success, an empty string on failure. virtual std::string GetOriginalPageLanguage(); // Adjusts a delay time for a posted task. This is used in tests to do tasks // immediately by returning 0. virtual base::TimeDelta AdjustDelay(int delayInMs); // Executes the JavaScript code in |script| in the main frame of RenderView. virtual void ExecuteScript(const std::string& script); // Executes the JavaScript code in |script| in the main frame of RenderView, // and returns the boolean returned by the script evaluation if the script was // run successfully. Otherwise, returns |fallback| value. virtual bool ExecuteScriptAndGetBoolResult(const std::string& script, bool fallback); // Executes the JavaScript code in |script| in the main frame of RenderView, // and returns the string returned by the script evaluation if the script was // run successfully. Otherwise, returns empty string. virtual std::string ExecuteScriptAndGetStringResult( const std::string& script); // Executes the JavaScript code in |script| in the main frame of RenderView. // and returns the number returned by the script evaluation if the script was // run successfully. Otherwise, returns 0.0. virtual double ExecuteScriptAndGetDoubleResult(const std::string& script); private: FRIEND_TEST_ALL_PREFIXES(TranslateHelperTest, AdoptHtmlLang); FRIEND_TEST_ALL_PREFIXES(TranslateHelperTest, CLDAgreeWithLanguageCodeHavingCountryCode); FRIEND_TEST_ALL_PREFIXES(TranslateHelperTest, CLDDisagreeWithWrongLanguageCode); FRIEND_TEST_ALL_PREFIXES(TranslateHelperTest, InvalidLanguageMetaTagProviding); FRIEND_TEST_ALL_PREFIXES(TranslateHelperTest, LanguageCodeTypoCorrection); FRIEND_TEST_ALL_PREFIXES(TranslateHelperTest, LanguageCodeSynonyms); FRIEND_TEST_ALL_PREFIXES(TranslateHelperTest, ResetInvalidLanguageCode); FRIEND_TEST_ALL_PREFIXES(TranslateHelperTest, SimilarLanguageCode); FRIEND_TEST_ALL_PREFIXES(TranslateHelperTest, WellKnownWrongConfiguration); enum LanguageDetectionTiming { ON_TIME, // Language detection was performed as soon as it was requested DEFERRED, // Language detection couldn't be performed when it was requested RESUMED, // A deferred language detection attempt was completed later LANGUAGE_DETECTION_TIMING_MAX_VALUE // The bounding value for this enum }; // Converts language code to the one used in server supporting list. static void ConvertLanguageCodeSynonym(std::string* code); // Returns whether the page associated with |document| is a candidate for // translation. Some pages can explictly specify (via a meta-tag) that they // should not be translated. static bool IsTranslationAllowed(blink::WebDocument* document); // RenderViewObserver implementation. virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; // Informs us that the page's text has been extracted. void PageCapturedImpl(int page_seq_no, const base::string16& contents); // Cancels any translation that is currently being performed. This does not // revert existing translations. void CancelPendingTranslation(); // Checks if the current running page translation is finished or errored and // notifies the browser accordingly. If the translation has not terminated, // posts a task to check again later. void CheckTranslateStatus(int page_seq_no); // Called by TranslatePage to do the actual translation. |count| is used to // limit the number of retries. void TranslatePageImpl(int page_seq_no, int count); // Sends a message to the browser to notify it that the translation failed // with |error|. void NotifyBrowserTranslationFailed(TranslateErrors::Type error); // Convenience method to access the main frame. Can return NULL, typically // if the page is being closed. blink::WebFrame* GetMainFrame(); // Do not ask for CLD data any more. void CancelCldDataPolling(); // Invoked when PageCaptured is called prior to obtaining CLD data. This // method stores the page ID into deferred_page_id_ and COPIES the contents // of the page, then sets deferred_page_capture_ to true. When CLD data is // eventually received (in OnCldDataAvailable), any deferred request will be // "resurrected" and allowed to proceed automatically, assuming that the // page ID has not changed. void DeferPageCaptured(const int page_id, const base::string16& contents); // Start polling for CLD data. // Polling will automatically halt as soon as the renderer obtains a // reference to the data file. void SendCldDataRequest(const int delay_millis, const int next_delay_millis); // Callback triggered when CLD data becomes available. void OnCldDataAvailable(); // Record the timing of language detection, immediately sending an IPC-based // histogram delta update to the browser process in case the hosting renderer // process terminates before the metrics would otherwise be transferred. void RecordLanguageDetectionTiming(LanguageDetectionTiming timing); // An ever-increasing sequence number of the current page, used to match up // translation requests with responses. int page_seq_no_; // The states associated with the current translation. bool translation_pending_; std::string source_lang_; std::string target_lang_; // Time when a page langauge is determined. This is used to know a duration // time from showing infobar to requesting translation. base::TimeTicks language_determined_time_; // Provides CLD data for this process. scoped_ptr<RendererCldDataProvider> cld_data_provider_; // Whether or not polling for CLD2 data has started. bool cld_data_polling_started_; // Whether or not CancelCldDataPolling has been called. bool cld_data_polling_canceled_; // Whether or not a PageCaptured event arrived prior to CLD data becoming // available. If true, deferred_contents_ contains the most recent contents. bool deferred_page_capture_; // The ID of the page most recently reported to PageCaptured if // deferred_page_capture_ is true. int deferred_page_seq_no_; // The world ID to use for script execution. int world_id_; // The extension group. int extension_group_; // The URL scheme for translate extensions. std::string extension_scheme_; // The contents of the page most recently reported to PageCaptured if // deferred_page_capture_ is true. base::string16 deferred_contents_; // Method factory used to make calls to TranslatePageImpl. base::WeakPtrFactory<TranslateHelper> weak_method_factory_; DISALLOW_COPY_AND_ASSIGN(TranslateHelper); }; } // namespace translate #endif // COMPONENTS_TRANSLATE_CONTENT_RENDERER_TRANSLATE_HELPER_H_