Commit ffdb36cc authored by Javier Ernesto Flores Robles's avatar Javier Ernesto Flores Robles Committed by Commit Bot

JS injection

This CL adds JS injection to support interacting with forms. To be used
to get the content of the current field, and set values on the fields.

Bug: 845472
Cq-Include-Trybots: luci.chromium.try:ios-simulator-full-configs;master.tryserver.chromium.mac:ios-simulator-cronet
Change-Id: I860ebf4e2181f53658c0e519c5b3290ec86ac46f
Reviewed-on: https://chromium-review.googlesource.com/1073407
Commit-Queue: Javier Ernesto Flores Robles <javierrobles@chromium.org>
Reviewed-by: default avatarOlivier Robin <olivierrobin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#561891}
parent b7bfd936
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
import("//ios/web/js_compile.gni")
source_set("manual_fill") { source_set("manual_fill") {
sources = [ sources = [
"ipad_keyboard_proto_view_controller.h", "ipad_keyboard_proto_view_controller.h",
...@@ -18,7 +20,10 @@ source_set("manual_fill") { ...@@ -18,7 +20,10 @@ source_set("manual_fill") {
"password_picker_view_controller.mm", "password_picker_view_controller.mm",
] ]
deps = [ deps = [
":injected_js",
"//base", "//base",
"//ios/chrome/browser/web:chrome_bundle_all_frames",
"//ios/chrome/browser/web:chrome_bundle_main_frame",
"//ios/showcase/common", "//ios/showcase/common",
"//third_party/material_design_icons:ic_account_circle", "//third_party/material_design_icons:ic_account_circle",
"//third_party/material_design_icons:ic_credit_card", "//third_party/material_design_icons:ic_credit_card",
...@@ -32,3 +37,14 @@ source_set("manual_fill") { ...@@ -32,3 +37,14 @@ source_set("manual_fill") {
] ]
configs += [ "//build/config/compiler:enable_arc" ] configs += [ "//build/config/compiler:enable_arc" ]
} }
js_compile_checked("injected_js") {
visibility = [ ":manual_fill" ]
sources = [
"resources/manualfill_controller.js",
]
js_modules = [
"//components/autofill/ios/fill/resources/form.js",
"//components/autofill/ios/fill/resources/fill.js",
]
}
...@@ -53,7 +53,7 @@ UIView* GetFirstResponderSubview(UIView* view); ...@@ -53,7 +53,7 @@ UIView* GetFirstResponderSubview(UIView* view);
// Tries to inject the passed string into the web view last focused field. // Tries to inject the passed string into the web view last focused field.
// //
// @param string The content to be injected. // @param string The content to be injected. Must be JS encoded.
- (void)fillLastSelectedFieldWithString:(NSString*)string; - (void)fillLastSelectedFieldWithString:(NSString*)string;
// Calls JS `focus()` on the last active element in an attemp to highlight it. // Calls JS `focus()` on the last active element in an attemp to highlight it.
......
...@@ -42,7 +42,7 @@ UIView* GetFirstResponderSubview(UIView* view) { ...@@ -42,7 +42,7 @@ UIView* GetFirstResponderSubview(UIView* view) {
@interface KeyboardProtoViewController () @interface KeyboardProtoViewController ()
// The last recorded active field identifier, used to interact with the web // The last recorded active field identifier, used to interact with the web
// view (i.e. add CSS focus to the element). // view (i.e. overwrite the input of the field).
@property(nonatomic, strong) NSString* activeFieldID; @property(nonatomic, strong) NSString* activeFieldID;
@end @end
...@@ -56,9 +56,8 @@ UIView* GetFirstResponderSubview(UIView* view) { ...@@ -56,9 +56,8 @@ UIView* GetFirstResponderSubview(UIView* view) {
- (void)viewDidLoad { - (void)viewDidLoad {
[super viewDidLoad]; [super viewDidLoad];
WKWebViewConfiguration* configuration = [[WKWebViewConfiguration alloc] init];
_webView = [[WKWebView alloc] initWithFrame:self.view.bounds _webView = [[WKWebView alloc] initWithFrame:self.view.bounds
configuration:configuration]; configuration:[self webViewConfiguration]];
[self.view addSubview:self.webView]; [self.view addSubview:self.webView];
self.webView.translatesAutoresizingMaskIntoConstraints = NO; self.webView.translatesAutoresizingMaskIntoConstraints = NO;
manualfill::AddSameConstraints(self.webView, self.view); manualfill::AddSameConstraints(self.webView, self.view);
...@@ -67,8 +66,8 @@ UIView* GetFirstResponderSubview(UIView* view) { ...@@ -67,8 +66,8 @@ UIView* GetFirstResponderSubview(UIView* view) {
- (void)viewWillAppear:(BOOL)animated { - (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated]; [super viewWillAppear:animated];
NSURL* sigupURL = [NSURL URLWithString:@"https://appleid.apple.com/account"]; NSURL* signupURL = [NSURL URLWithString:@"https://appleid.apple.com/account"];
NSURLRequest* request = [NSURLRequest requestWithURL:sigupURL]; NSURLRequest* request = [NSURLRequest requestWithURL:signupURL];
[self.webView loadRequest:request]; [self.webView loadRequest:request];
} }
...@@ -82,7 +81,7 @@ UIView* GetFirstResponderSubview(UIView* view) { ...@@ -82,7 +81,7 @@ UIView* GetFirstResponderSubview(UIView* view) {
- (void)updateActiveFieldID { - (void)updateActiveFieldID {
__weak KeyboardProtoViewController* weakSelf = self; __weak KeyboardProtoViewController* weakSelf = self;
NSString* javaScriptQuery = @"document.activeElement.id"; NSString* javaScriptQuery = @"__gCrWeb.manualfill.activeElementId()";
[self.webView evaluateJavaScript:javaScriptQuery [self.webView evaluateJavaScript:javaScriptQuery
completionHandler:^(id result, NSError* error) { completionHandler:^(id result, NSError* error) {
NSLog(@"result: %@", [result description]); NSLog(@"result: %@", [result description]);
...@@ -92,9 +91,11 @@ UIView* GetFirstResponderSubview(UIView* view) { ...@@ -92,9 +91,11 @@ UIView* GetFirstResponderSubview(UIView* view) {
} }
- (void)fillLastSelectedFieldWithString:(NSString*)string { - (void)fillLastSelectedFieldWithString:(NSString*)string {
if ([self.lastFirstResponder conformsToProtocol:@protocol(UIKeyInput)]) { NSString* javaScriptQuery =
[(id<UIKeyInput>)self.lastFirstResponder insertText:string]; [NSString stringWithFormat:
} @"__gCrWeb.manualfill.setValueForElementId(\"%@\", \"%@\")",
string, self.activeFieldID];
[self.webView evaluateJavaScript:javaScriptQuery completionHandler:nil];
} }
- (void)callFocusOnLastActiveField { - (void)callFocusOnLastActiveField {
...@@ -104,4 +105,57 @@ UIView* GetFirstResponderSubview(UIView* view) { ...@@ -104,4 +105,57 @@ UIView* GetFirstResponderSubview(UIView* view) {
[self.webView evaluateJavaScript:javaScriptQuery completionHandler:nil]; [self.webView evaluateJavaScript:javaScriptQuery completionHandler:nil];
} }
#pragma mark JS Injection
// Returns an NSString with the contents of the files passed. It is assumed the
// files are of type ".js" and they are in the main bundle.
- (NSString*)joinedJSFilesWithFilenames:(NSArray<NSString*>*)filenames {
NSMutableString* fullScript = [NSMutableString string];
for (NSString* filename in filenames) {
NSString* path =
[[NSBundle mainBundle] pathForResource:filename ofType:@"js"];
NSData* scriptData = [[NSData alloc] initWithContentsOfFile:path];
NSString* scriptString =
[[NSString alloc] initWithData:scriptData
encoding:NSUTF8StringEncoding];
[fullScript appendFormat:@"%@\n", scriptString];
}
return fullScript;
}
- (NSString*)earlyJSStringMainFrame {
NSArray* filenames = @[
@"main_frame_web_bundle", @"chrome_bundle_main_frame",
@"manualfill_controller"
];
return [self joinedJSFilesWithFilenames:filenames];
}
- (NSString*)earlyJSStringAllFrames {
NSArray* filenames = @[
@"all_frames_web_bundle",
@"chrome_bundle_all_frames",
];
return [self joinedJSFilesWithFilenames:filenames];
}
// Returns a WKWebViewConfiguration with early scripts for the main frame and
// for all frames.
- (WKWebViewConfiguration*)webViewConfiguration {
WKWebViewConfiguration* configuration = [[WKWebViewConfiguration alloc] init];
WKUserScript* userScriptAllFrames = [[WKUserScript alloc]
initWithSource:[self earlyJSStringAllFrames]
injectionTime:WKUserScriptInjectionTimeAtDocumentStart
forMainFrameOnly:NO];
[configuration.userContentController addUserScript:userScriptAllFrames];
WKUserScript* userScriptMainFrame = [[WKUserScript alloc]
initWithSource:[self earlyJSStringMainFrame]
injectionTime:WKUserScriptInjectionTimeAtDocumentStart
forMainFrameOnly:YES];
[configuration.userContentController addUserScript:userScriptMainFrame];
return configuration;
}
@end @end
// Copyright 2018 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.
goog.provide('__crWeb.manualfill');
/* Beginning of anonymous object. */
(function() {
/**
* Namespace for this file. It depends on |__gCrWeb| having already been
* injected.
*/
__gCrWeb.manualfill = {};
__gCrWeb['manualfill'] = __gCrWeb.manualfill;
/**
* Returns the identifier to be used with `setValueForElementId`.
*
* @return {string} The id of the active element.
*/
__gCrWeb.manualfill.activeElementId = function() {
var activeElementId = document.activeElement.id;
return activeElementId;
};
/**
* Sets the value passed for the element with the identifier passed.
*
* @param {string} value The input to set in the element with the identifier.
* @param {string} identifier The identifier of the element, found with
* `activeElementId`.
*/
__gCrWeb.manualfill.setValueForElementId = function(value, identifier) {
if (!identifier) {
return
}
var field = document.getElementById(identifier);
if (!field) {
return;
}
if (!value || value.length === 0) {
return;
}
__gCrWeb.fill.setInputElementValue(value, field);
};
}()); // End of anonymous object
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