Commit d51b7db0 authored by Mike Dougherty's avatar Mike Dougherty Committed by Chromium LUCI CQ

Remove JSTranslateManager dependency on CRWJSInjectionManager

CRWJSInjectionManager is deprecated and translate is one of
the few remaining features using it. Migrating it off of this dependency
will help to cleanup existing JavaScript feature code in preparation for
new JavaScript features.

Bug: 949178
Change-Id: I4931c97db89859703ff021a17049de5593be1d2b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2587393
Commit-Queue: Mike Dougherty <michaeldo@chromium.org>
Reviewed-by: default avatarJohn Wu <jzw@chromium.org>
Cr-Commit-Position: refs/heads/master@{#837339}
parent c7ebb01e
......@@ -69,7 +69,6 @@ source_set("unit_tests") {
"//ios/web/public/deprecated:test_doubles",
"//ios/web/public/test",
"//testing/gtest",
"//third_party/ocmock",
"//ui/base",
"//url",
]
......
......@@ -69,8 +69,8 @@ IOSTranslateDriver::IOSTranslateDriver(
web_state, translate_manager_->translate_client()->GetPrefs());
// Create the translate controller.
JsTranslateManager* js_translate_manager = static_cast<JsTranslateManager*>(
[receiver instanceOfClass:[JsTranslateManager class]]);
JsTranslateManager* js_translate_manager =
[[JsTranslateManager alloc] initWithWebState:web_state];
translate_controller_ =
std::make_unique<TranslateController>(web_state, js_translate_manager);
......
......@@ -5,24 +5,28 @@
#ifndef COMPONENTS_TRANSLATE_IOS_BROWSER_JS_TRANSLATE_MANAGER_H_
#define COMPONENTS_TRANSLATE_IOS_BROWSER_JS_TRANSLATE_MANAGER_H_
#import "ios/web/public/deprecated/crw_js_injection_manager.h"
#import <Foundation/Foundation.h>
#include <string>
#include "base/time/time.h"
@class NSString;
namespace web {
class WebState;
} // namespace web
// Manager for the injection of the Translate JavaScript.
// Replicates functionality from TranslateAgent in
// chrome/renderer/translate/translate_agent.cc.
// JsTranslateManager injects the script in the page and calls it, but is not
// responsible for loading it or caching it.
@interface JsTranslateManager : CRWJSInjectionManager
@interface JsTranslateManager : NSObject
- (instancetype)initWithWebState:(web::WebState*)web_state
NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
// The translation script. Must be set before |-inject| is called, and is reset
// after the injection.
@property(nonatomic, copy) NSString* script;
// Injects the |translate_script| into the |web_state| passed in at
// initialization.
- (void)injectWithTranslateScript:(const std::string&)translate_script;
// Starts translation of the page from |source| language to |target| language.
// Equivalent to TranslateAgent::StartTranslation().
......@@ -41,12 +45,12 @@
// |statusText| The status text associated with the response code, may be empty.
// |responseURL| The final URL from which the response originates.
// |responseText| The contents of the response.
- (void)handleTranslateResponseWithURL:(NSString*)URL
- (void)handleTranslateResponseWithURL:(const std::string&)URL
requestID:(int)requestID
responseCode:(int)responseCode
statusText:(NSString*)statusText
responseURL:(NSString*)responseURL
responseText:(NSString*)responseText;
statusText:(const std::string&)statusText
responseURL:(const std::string&)responseURL
responseText:(const std::string&)responseText;
@end
......
......@@ -10,94 +10,105 @@
#include "base/check.h"
#include "base/mac/bundle_locations.h"
#include "base/strings/stringprintf.h"
#import "base/strings/sys_string_conversions.h"
#import "base/strings/utf_string_conversions.h"
#import "ios/web/public/deprecated/crw_js_injection_receiver.h"
#import "ios/web/public/web_state.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@implementation JsTranslateManager {
NSString* _translationScript;
}
- (NSString*)script {
return _translationScript;
}
namespace {
- (void)setScript:(NSString*)script {
// Returns an autoreleased string containing the JavaScript loaded from a
// bundled resource file with the given name (excluding extension).
NSString* GetPageScript(NSString* script_file_name) {
DCHECK(script_file_name);
NSString* path =
[base::mac::FrameworkBundle() pathForResource:@"translate_ios"
[base::mac::FrameworkBundle() pathForResource:script_file_name
ofType:@"js"];
DCHECK(path);
DCHECK(path) << "Script file not found: "
<< base::SysNSStringToUTF8(script_file_name) << ".js";
NSError* error = nil;
NSString* content = [NSString stringWithContentsOfFile:path
encoding:NSUTF8StringEncoding
error:&error];
DCHECK(!error && [content length]);
// Prepend so callbacks defined in translate_ios.js can be installed.
script = [content stringByAppendingString:script];
DCHECK(!error) << "Error fetching script: "
<< base::SysNSStringToUTF8(error.description);
DCHECK(content);
return content;
}
} // namespace
@interface JsTranslateManager ()
@property(nonatomic) web::WebState* web_state;
@property(nonatomic) bool injected;
@end
@implementation JsTranslateManager
- (instancetype)initWithWebState:(web::WebState*)web_state {
self = [super init];
if (self) {
_web_state = web_state;
_injected = false;
}
return self;
}
- (void)injectWithTranslateScript:(const std::string&)translate_script {
// Prepend translate_ios.js
NSString* translate_ios = GetPageScript(@"translate_ios");
NSString* script = [translate_ios
stringByAppendingString:base::SysUTF8ToNSString(translate_script)];
// Reset translate state if previously injected.
if (_injected) {
NSString* resetScript = @"try {"
" cr.googleTranslate.revert();"
"} catch (e) {"
"}";
script = [resetScript stringByAppendingString:script];
}
_injected = true;
_translationScript = [script copy];
_web_state->ExecuteJavaScript(base::SysNSStringToUTF16(script));
}
- (void)startTranslationFrom:(const std::string&)source
to:(const std::string&)target {
NSString* script =
[NSString stringWithFormat:@"cr.googleTranslate.translate('%s','%s')",
source.c_str(), target.c_str()];
[self.receiver executeJavaScript:script completionHandler:nil];
std::string script =
base::StringPrintf("cr.googleTranslate.translate('%s','%s')",
source.c_str(), target.c_str());
_web_state->ExecuteJavaScript(base::UTF8ToUTF16(script));
}
- (void)revertTranslation {
if (![self hasBeenInjected])
if (!_injected)
return;
[self.receiver executeJavaScript:@"cr.googleTranslate.revert()"
completionHandler:nil];
_web_state->ExecuteJavaScript(
base::UTF8ToUTF16("cr.googleTranslate.revert()"));
}
- (void)handleTranslateResponseWithURL:(NSString*)URL
- (void)handleTranslateResponseWithURL:(const std::string&)URL
requestID:(int)requestID
responseCode:(int)responseCode
statusText:(NSString*)statusText
responseURL:(NSString*)responseURL
responseText:(NSString*)responseText {
DCHECK([self hasBeenInjected]);
statusText:(const std::string&)statusText
responseURL:(const std::string&)responseURL
responseText:(const std::string&)responseText {
DCHECK(_injected);
// Return the response details to function defined in translate_ios.js.
NSString* script = [NSString
stringWithFormat:
@"__gCrWeb.translate.handleResponse('%@', %d, %d, '%@', '%@', '%@')",
URL, requestID, responseCode, statusText, responseURL, responseText];
[self.receiver executeJavaScript:script completionHandler:nil];
}
#pragma mark -
#pragma mark CRWJSInjectionManager methods
- (void)inject {
NSString* script = [self injectionContent];
// Reset any state if previously injected.
if ([self hasBeenInjected]) {
NSString* resetScript =
@"try {"
" cr.googleTranslate.revert();"
"} catch (e) {"
"}";
script = [resetScript stringByAppendingString:script];
}
// The scripts need to be re-injected to ensure that the logic that
// initializes translate can be restarted properly.
[[self receiver] injectScript:script forClass:[self class]];
}
- (NSString*)injectionContent {
DCHECK(_translationScript);
NSString* translationScript = _translationScript;
_translationScript = nil;
return translationScript;
std::string script = base::StringPrintf(
"__gCrWeb.translate.handleResponse('%s', %d, %d, '%s', '%s', '%s')",
URL.c_str(), requestID, responseCode, statusText.c_str(),
responseURL.c_str(), responseText.c_str());
_web_state->ExecuteJavaScript(base::UTF8ToUTF16(script));
}
@end
......@@ -4,46 +4,31 @@
#import "components/translate/ios/browser/js_translate_manager.h"
#include "base/strings/sys_string_conversions.h"
#include "components/grit/components_resources.h"
#import "ios/web/public/deprecated/crw_test_js_injection_receiver.h"
#import "ios/web/public/test/js_test_util.h"
#import "ios/web/public/test/web_test_with_web_state.h"
#import "testing/gtest_mac.h"
#include "testing/platform_test.h"
#include "ui/base/resource/resource_bundle.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
class JsTranslateManagerTest : public PlatformTest {
protected:
JsTranslateManagerTest() {
receiver_ = [[CRWTestJSInjectionReceiver alloc] init];
manager_ = [[JsTranslateManager alloc] initWithReceiver:receiver_];
std::string script =
ui::ResourceBundle::GetSharedInstance().LoadDataResourceString(
IDR_TRANSLATE_JS);
[manager_ setScript:base::SysUTF8ToNSString(script + "('DummyKey');")];
}
bool IsDefined(NSString* name) {
NSString* script =
[NSString stringWithFormat:@"typeof %@ != 'undefined'", name];
return [web::test::ExecuteJavaScript(receiver_, script) boolValue];
}
CRWTestJSInjectionReceiver* receiver_;
JsTranslateManager* manager_;
};
typedef web::WebTestWithWebState JsTranslateManagerTest;
// Checks that cr.googleTranslate.libReady is available after the code has
// been injected in the page.
TEST_F(JsTranslateManagerTest, Inject) {
[manager_ inject];
EXPECT_TRUE([manager_ hasBeenInjected]);
EXPECT_EQ(nil, [manager_ script]);
EXPECT_TRUE(IsDefined(@"cr.googleTranslate"));
EXPECT_NSEQ(@NO, web::test::ExecuteJavaScript(
manager_, @"cr.googleTranslate.libReady"));
LoadHtml(@"<html></html>");
JsTranslateManager* manager =
[[JsTranslateManager alloc] initWithWebState:web_state()];
std::string script =
ui::ResourceBundle::GetSharedInstance().LoadDataResourceString(
IDR_TRANSLATE_JS);
[manager injectWithTranslateScript:script + "('DummyKey');"];
id result = ExecuteJavaScript(@"typeof cr.googleTranslate != 'undefined'");
EXPECT_NSEQ(@YES, result);
result = ExecuteJavaScript(@"cr.googleTranslate.libReady");
EXPECT_NSEQ(@NO, result);
}
......@@ -10,9 +10,6 @@
#include "base/callback_helpers.h"
#include "base/check_op.h"
#include "base/json/string_escape.h"
#include "base/strings/string16.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "components/translate/core/common/translate_util.h"
......@@ -70,8 +67,7 @@ TranslateController::~TranslateController() {
void TranslateController::InjectTranslateScript(
const std::string& translate_script) {
[js_manager_ setScript:base::SysUTF8ToNSString(translate_script)];
[js_manager_ inject];
[js_manager_ injectWithTranslateScript:translate_script];
}
void TranslateController::RevertTranslation() {
......@@ -279,14 +275,12 @@ void TranslateController::OnRequestFetchComplete(
&escaped_response_text);
std::string final_url = url_loader->GetFinalURL().spec();
[js_manager_
handleTranslateResponseWithURL:base::SysUTF8ToNSString(url)
requestID:request_id
responseCode:response_code
statusText:base::SysUTF8ToNSString(status_text)
responseURL:base::SysUTF8ToNSString(final_url)
responseText:base::SysUTF8ToNSString(
escaped_response_text)];
[js_manager_ handleTranslateResponseWithURL:url
requestID:request_id
responseCode:response_code
statusText:status_text
responseURL:final_url
responseText:escaped_response_text];
request_fetchers_.erase(it);
}
......
......@@ -14,13 +14,68 @@
#include "ios/web/public/test/web_task_environment.h"
#include "net/http/http_status_code.h"
#include "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#include "url/gurl.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
#pragma mark - FakeJSTranslateManager
namespace {
struct HandleTranslateResponseParams {
HandleTranslateResponseParams(const std::string& URL,
int request_ID,
int response_code,
const std::string& status_text,
const std::string& response_URL,
const std::string& response_text)
: URL(URL),
request_ID(request_ID),
response_code(response_code),
status_text(status_text),
response_URL(response_URL),
response_text(response_text) {}
~HandleTranslateResponseParams() {}
std::string URL;
int request_ID;
int response_code;
std::string status_text;
std::string response_URL;
std::string response_text;
};
} // namespace
// Fake translate manager to store parameters passed to
// |handleTranslateResponseWithURL:...|
@interface FakeJSTranslateManager : JsTranslateManager
@property(readonly) HandleTranslateResponseParams* lastHandleResponseParams;
@end
@implementation FakeJSTranslateManager {
std::unique_ptr<HandleTranslateResponseParams> _lastHandleResponseParams;
}
- (HandleTranslateResponseParams*)lastHandleResponseParams {
return _lastHandleResponseParams.get();
}
- (void)handleTranslateResponseWithURL:(const std::string&)URL
requestID:(int)requestID
responseCode:(int)responseCode
statusText:(const std::string&)statusText
responseURL:(const std::string&)responseURL
responseText:(const std::string&)responseText {
_lastHandleResponseParams = std::make_unique<HandleTranslateResponseParams>(
URL, requestID, responseCode, statusText, responseURL, responseText);
}
@end
namespace translate {
class TranslateControllerTest : public PlatformTest,
......@@ -38,10 +93,10 @@ class TranslateControllerTest : public PlatformTest,
on_script_ready_called_(false),
on_translate_complete_called_(false) {
fake_web_state_->SetBrowserState(fake_browser_state_.get());
mock_js_translate_manager_ =
[OCMockObject niceMockForClass:[JsTranslateManager class]];
fake_js_translate_manager_ =
[[FakeJSTranslateManager alloc] initWithWebState:fake_web_state_.get()];
translate_controller_ = std::make_unique<TranslateController>(
fake_web_state_.get(), mock_js_translate_manager_);
fake_web_state_.get(), fake_js_translate_manager_);
translate_controller_->set_observer(this);
}
......@@ -69,7 +124,7 @@ class TranslateControllerTest : public PlatformTest,
std::unique_ptr<web::FakeBrowserState> fake_browser_state_;
web::FakeWebFrame fake_main_frame_;
web::FakeWebFrame fake_iframe_;
id mock_js_translate_manager_;
FakeJSTranslateManager* fake_js_translate_manager_;
std::unique_ptr<TranslateController> translate_controller_;
TranslateErrors::Type error_type_;
double ready_time_;
......@@ -225,16 +280,6 @@ TEST_F(TranslateControllerTest, OnTranslateSendRequestWithBadMethod) {
command.SetString("body", "helloworld");
command.SetDouble("requestID", 0);
[[mock_js_translate_manager_ expect]
handleTranslateResponseWithURL:
@"https://translate.googleapis.com/translate?key=abcd"
requestID:0
responseCode:net::HttpStatusCode::HTTP_BAD_REQUEST
statusText:@""
responseURL:@"https://translate.googleapis.com/"
@"translate?key=abcd"
responseText:@""];
// The command will be accepted, but a bad method should cause the request to
// fail shortly thereafter.
EXPECT_TRUE(translate_controller_->OnJavascriptCommandReceived(
......@@ -242,7 +287,17 @@ TEST_F(TranslateControllerTest, OnTranslateSendRequestWithBadMethod) {
&fake_main_frame_));
task_environment_.RunUntilIdle();
[mock_js_translate_manager_ verify];
HandleTranslateResponseParams* last_params =
fake_js_translate_manager_.lastHandleResponseParams;
ASSERT_TRUE(last_params);
EXPECT_EQ("https://translate.googleapis.com/translate?key=abcd",
last_params->URL);
EXPECT_EQ(0, last_params->request_ID);
EXPECT_EQ(net::HttpStatusCode::HTTP_BAD_REQUEST, last_params->response_code);
EXPECT_EQ("", last_params->status_text);
EXPECT_EQ("https://translate.googleapis.com/translate?key=abcd",
last_params->response_URL);
EXPECT_EQ("", last_params->response_text);
}
} // namespace translate
......@@ -110,17 +110,12 @@ class TranslateAppInterfaceHelper {
@implementation FakeJSTranslateManager
- (instancetype)initWithWebState:(web::WebState*)webState {
if ((self = [super init])) {
if ((self = [super initWithWebState:webState])) {
_webState = webState;
}
return self;
}
- (void)setScript:(NSString*)script {
// No need to set the JavaScript since it will never be used by this fake
// object.
}
- (void)startTranslationFrom:(const std::string&)source
to:(const std::string&)target {
// Add a button with the 'Translated' label to the web page.
......@@ -139,9 +134,10 @@ class TranslateAppInterfaceHelper {
"myButton.remove();"));
}
- (void)inject {
// Prevent the actual script from being injected and instead just invoke host
// with 'translate.ready' followed by 'translate.status'.
- (void)injectWithTranslateScript:(const std::string&)translate_script {
// No need to set the |translate_script| JavaScript since it will never be
// used by this fake object. Instead just invoke host with 'translate.ready'
// followed by 'translate.status'.
_webState->ExecuteJavaScript(
base::UTF8ToUTF16("__gCrWeb.message.invokeOnHost({"
" 'command': 'translate.ready',"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment