Commit faebdb6d authored by Olivier Robin's avatar Olivier Robin Committed by Commit Bot

Track form changes on the web page.

Some web sites (like apple.com or llbean.com) build forms after the page
loading. This prevent using the server side type resolution.

Add a tracker to update the autofill forms regularly.

Bug: 798675
Cq-Include-Trybots: master.tryserver.chromium.mac:ios-simulator-cronet;master.tryserver.chromium.mac:ios-simulator-full-configs
Change-Id: Ieaaf502a7bff38756d90bd96e94c058977dcab7b
Reviewed-on: https://chromium-review.googlesource.com/943321
Commit-Queue: Olivier Robin <olivierrobin@chromium.org>
Reviewed-by: default avatarDirk Pranke <dpranke@chromium.org>
Reviewed-by: default avatarSylvain Defresne <sdefresne@chromium.org>
Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Reviewed-by: default avatarMoe Ahmadi <mahmadi@chromium.org>
Reviewed-by: default avatarSebastien Seguin-Gagnon <sebsg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#544331}
parent 0e9f578b
......@@ -46,6 +46,7 @@ template("java_action") {
"deps",
"outputs",
"sources",
"testonly",
"visibility",
])
}
......@@ -95,6 +96,7 @@ template("java_action_foreach") {
"deps",
"outputs",
"sources",
"testonly",
"visibility",
])
}
......
......@@ -175,6 +175,7 @@ test("components_unittests") {
if (is_ios) {
deps += [
"//components/autofill/ios/browser:unit_tests",
"//components/autofill/ios/fill:unit_tests",
"//components/image_fetcher/ios:unit_tests",
"//components/signin/ios/browser:unit_tests",
"//components/translate/ios/browser:unit_tests",
......
......@@ -726,7 +726,11 @@ void GetFormAndField(autofill::FormData* form,
web::URLVerificationTrustLevel trustLevel;
const GURL pageURL(webState->GetCurrentURL(&trustLevel));
[jsAutofillManager_ trackFormUpdates];
[self scanFormsInPage:webState pageURL:pageURL];
}
- (void)scanFormsInPage:(web::WebState*)webState pageURL:(const GURL&)pageURL {
__weak AutofillAgent* weakSelf = self;
id completionHandler = ^(BOOL success, const FormDataVector& forms) {
AutofillAgent* strongSelf = weakSelf;
......@@ -764,6 +768,17 @@ void GetFormAndField(autofill::FormData* form,
// page is fully loaded.
[self processPage:webState];
web::URLVerificationTrustLevel trustLevel;
const GURL pageURL(webState->GetCurrentURL(&trustLevel));
// If the event is a form_changed, then the event concerns the whole page and
// not a particular form. The whole page need to be reparsed to find the new
// forms.
if (params.type.compare("form_changed") == 0) {
[self scanFormsInPage:webState pageURL:pageURL];
return;
}
// Blur not handled; we don't reset the suggestion state because if the
// keyboard is about to be dismissed there's no point. If not it means the
// next focus event will update the suggestion state within milliseconds, so
......@@ -785,9 +800,6 @@ void GetFormAndField(autofill::FormData* form,
}
};
web::URLVerificationTrustLevel trustLevel;
const GURL pageURL(webState->GetCurrentURL(&trustLevel));
// Re-extract the active form and field only. There is no minimum field
// requirement because key/value suggestions are offered even on short forms.
[self fetchFormsFiltered:YES
......
......@@ -52,6 +52,9 @@
// Adds a delay between filling the form fields.
- (void)addJSDelay;
// Installs a tracker to check the form updates in the page every 500ms.
- (void)trackFormUpdates;
// Designated initializer. |receiver| should not be nil.
- (instancetype)initWithReceiver:(CRWJSInjectionReceiver*)receiver
NS_DESIGNATED_INITIALIZER;
......
......@@ -74,6 +74,11 @@
}];
}
- (void)trackFormUpdates {
[_receiver executeJavaScript:@"__gCrWeb.form.trackFormUpdates(500)"
completionHandler:nil];
}
- (void)fillForm:(NSString*)dataString
forceFillFieldName:(NSString*)forceFillFieldName
completionHandler:(ProceduralBlock)completionHandler {
......
# 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.
import("//ios/web/js_compile.gni")
import("//testing/test.gni")
source_set("unit_tests") {
testonly = true
configs += [ "//build/config/compiler:enable_arc" ]
sources = [
"form_unittest.mm",
]
deps = [
":form_js",
"//base",
"//ios/web/public/test",
"//ios/web/public/test/fakes",
"//ios/web/web_state/js",
"//testing/gtest",
]
}
js_compile_checked("form_js") {
visibility = [ ":unit_tests" ]
testonly = true
sources = [
"resources/form.js",
]
}
specific_include_rules = {
"form_unittest\.mm": [
"+ios/web/web_state/js/page_script_util.h",
],
}
......@@ -2,25 +2,41 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/web/public/browser_state.h"
#import "ios/web/public/test/fakes/test_web_client.h"
#include "ios/web/public/test/fakes/test_web_state_observer.h"
#import "ios/web/public/test/web_js_test.h"
#import "ios/web/public/test/web_test_with_web_state.h"
#import "ios/web/web_state/js/page_script_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace web {
class FormTestClient : public web::TestWebClient {
public:
NSString* GetDocumentStartScriptForAllFrames(
web::BrowserState* browser_state) const override {
return web::GetPageScript(@"form");
}
};
using FormJsTest = WebTestWithWebState;
// Text fixture to test password controller.
class FormJsTest : public web::WebJsTest<web::WebTestWithWebState> {
public:
FormJsTest()
: web::WebJsTest<web::WebTestWithWebState>(
std::make_unique<FormTestClient>()) {}
};
// Tests that keyup event correctly delivered to WebStateObserver.
TEST_F(FormJsTest, KeyUpEvent) {
TestWebStateObserver observer(web_state());
web::TestWebStateObserver observer(web_state());
LoadHtml(@"<p></p>");
ASSERT_FALSE(observer.form_activity_info());
ExecuteJavaScript(@"document.dispatchEvent(new KeyboardEvent('keyup'));");
TestFormActivityInfo* info = observer.form_activity_info();
web::TestFormActivityInfo* info = observer.form_activity_info();
ASSERT_TRUE(info);
EXPECT_EQ("keyup", info->form_activity.type);
EXPECT_FALSE(info->form_activity.input_missing);
......@@ -28,7 +44,7 @@ TEST_F(FormJsTest, KeyUpEvent) {
// Tests that focus event correctly delivered to WebStateObserver.
TEST_F(FormJsTest, FocusMainFrame) {
TestWebStateObserver observer(web_state());
web::TestWebStateObserver observer(web_state());
LoadHtml(
@"<form>"
"<input type='text' name='username' id='id1'>"
......@@ -36,7 +52,7 @@ TEST_F(FormJsTest, FocusMainFrame) {
"</form>");
ASSERT_FALSE(observer.form_activity_info());
ExecuteJavaScript(@"document.getElementById('id1').focus();");
TestFormActivityInfo* info = observer.form_activity_info();
web::TestFormActivityInfo* info = observer.form_activity_info();
ASSERT_TRUE(info);
EXPECT_EQ("focus", info->form_activity.type);
EXPECT_FALSE(info->form_activity.input_missing);
......@@ -44,7 +60,7 @@ TEST_F(FormJsTest, FocusMainFrame) {
// Tests that submit event correctly delivered to WebStateObserver.
TEST_F(FormJsTest, FormSubmitMainFrame) {
TestWebStateObserver observer(web_state());
web::TestWebStateObserver observer(web_state());
LoadHtml(
@"<form id='form1'>"
"<input type='password'>"
......@@ -52,7 +68,7 @@ TEST_F(FormJsTest, FormSubmitMainFrame) {
"</form>");
ASSERT_FALSE(observer.submit_document_info());
ExecuteJavaScript(@"document.getElementById('submit_input').click();");
TestSubmitDocumentInfo* info = observer.submit_document_info();
web::TestSubmitDocumentInfo* info = observer.submit_document_info();
ASSERT_TRUE(info);
EXPECT_EQ("form1", info->form_name);
}
......@@ -60,7 +76,7 @@ TEST_F(FormJsTest, FormSubmitMainFrame) {
// Tests that focus event from same-origin iframe correctly delivered to
// WebStateObserver.
TEST_F(FormJsTest, FocusSameOriginIFrame) {
TestWebStateObserver observer(web_state());
web::TestWebStateObserver observer(web_state());
LoadHtml(@"<iframe id='frame1'></iframe>");
ExecuteJavaScript(
@"document.getElementById('frame1').contentDocument.body.innerHTML = "
......@@ -72,7 +88,7 @@ TEST_F(FormJsTest, FocusSameOriginIFrame) {
ExecuteJavaScript(
@"document.getElementById('frame1').contentDocument.getElementById('id1')"
@".focus()");
TestFormActivityInfo* info = observer.form_activity_info();
web::TestFormActivityInfo* info = observer.form_activity_info();
ASSERT_TRUE(info);
EXPECT_EQ("focus", info->form_activity.type);
EXPECT_FALSE(info->form_activity.input_missing);
......@@ -81,7 +97,7 @@ TEST_F(FormJsTest, FocusSameOriginIFrame) {
// Tests that submit event from same-origin iframe correctly delivered to
// WebStateObserver.
TEST_F(FormJsTest, FormSameOriginIFrame) {
TestWebStateObserver observer(web_state());
web::TestWebStateObserver observer(web_state());
LoadHtml(@"<iframe id='frame1'></iframe>");
ExecuteJavaScript(
@"document.getElementById('frame1').contentDocument.body.innerHTML = "
......@@ -92,9 +108,45 @@ TEST_F(FormJsTest, FormSameOriginIFrame) {
ExecuteJavaScript(
@"document.getElementById('frame1').contentDocument.getElementById('"
@"submit_input').click();");
TestSubmitDocumentInfo* info = observer.submit_document_info();
web::TestSubmitDocumentInfo* info = observer.submit_document_info();
ASSERT_TRUE(info);
EXPECT_EQ("form1", info->form_name);
}
} // namespace web
// Tests that new form triggers form changed event.
TEST_F(FormJsTest, FocusAddForm) {
web::TestWebStateObserver observer(web_state());
LoadHtml(@"<form/>");
ExecuteJavaScript(
@"__gCrWeb.form.trackFormUpdates(10);"
@"var form = document.createElement('form');"
@"document.body.appendChild(form);");
web::TestWebStateObserver* block_observer = &observer;
__block web::TestFormActivityInfo* info = nil;
WaitForCondition(^{
info = block_observer->form_activity_info();
return info != nil;
});
EXPECT_EQ("form_changed", info->form_activity.type);
EXPECT_FALSE(info->form_activity.input_missing);
}
// Tests that new input triggers form changed event.
TEST_F(FormJsTest, FocusAddInput) {
web::TestWebStateObserver observer(web_state());
LoadHtml(@"<form id='formId'/>");
ExecuteJavaScript(
@"__gCrWeb.form.trackFormUpdates(10);"
@"var input = document.createElement('input');"
@"document.getElementById('formId').appendChild(input);");
web::TestWebStateObserver* block_observer = &observer;
__block web::TestFormActivityInfo* info = nil;
WaitForCondition(^{
info = block_observer->form_activity_info();
return info != nil;
});
EXPECT_EQ("form_changed", info->form_activity.type);
EXPECT_FALSE(info->form_activity.input_missing);
}
......@@ -11,8 +11,6 @@
goog.provide('__crWeb.form');
goog.require('__crWeb.message');
/**
* Namespace for this file. It depends on |__gCrWeb| having already been
* injected. String 'form' is used in |__gCrWeb['form']| as it needs to be
......@@ -50,6 +48,17 @@ __gCrWeb.form.kNamelessFormIDPrefix = 'gChrome~form~';
*/
__gCrWeb.form.kNamelessFieldIDPrefix = 'gChrome~field~';
/**
* The interval watching for form changes.
*/
__gCrWeb.form.formWatcherInterval = null;
/**
* The value of the form signature last time formWatcherInterval was
* triggerred.
*/
__gCrWeb.form.lastFormSignature = {};
/**
* Based on Element::isFormControlElement() (WebKit)
* @param {Element} element A DOM element.
......@@ -282,6 +291,48 @@ var getFullyQualifiedUrl_ = function(originalURL) {
return anchor.href;
};
/**
* Returns a simple signature of the form content of the page. Must be fast
* as it is called regularly.
*/
var getFormSignature_ = function() {
return {
forms: document.forms.length,
input: document.getElementsByTagName('input').length
};
};
/**
* Install a watcher to check the form changes. Delay is the interval between
* checks in milliseconds.
*/
__gCrWeb.form['trackFormUpdates'] = function(delay) {
if (__gCrWeb.form.formWatcherInterval) {
clearInterval(__gCrWeb.form.formWatcherInterval);
__gCrWeb.form.formWatcherInterval = null;
}
if (delay) {
__gCrWeb.form.lastFormSignature = getFormSignature_();
__gCrWeb.form.formWatcherInterval = setInterval(function() {
var signature = getFormSignature_();
var old_signature = __gCrWeb.form.lastFormSignature;
if (signature.forms != old_signature.forms ||
signature.input != old_signature.input) {
var msg = {
'command': 'form.activity',
'formName': '',
'fieldName': '',
'fieldType': '',
'type': 'form_changed',
'value': ''
};
__gCrWeb.form.lastFormSignature = signature;
__gCrWeb.message.invokeOnHost(msg);
}
}, delay);
}
};
/** Flush the message queue. */
if (__gCrWeb.message) {
__gCrWeb.message.invokeQueues();
......
......@@ -192,7 +192,7 @@ js_compile_checked("injected_js") {
"resources/password_controller.js",
]
js_modules = [
"//ios/web/web_state/js/resources/form.js",
"//components/autofill/ios/fill/resources/form.js",
"//components/autofill/ios/fill/resources/fill.js",
]
}
......@@ -22,8 +22,10 @@ class PasswordControllerJsTest
: public web::WebJsTest<web::WebTestWithWebState> {
public:
PasswordControllerJsTest()
: web::WebJsTest<web::WebTestWithWebState>(
@[ @"chrome_bundle", @"password_controller" ]) {}
: web::WebJsTest<web::WebTestWithWebState>(@[
@"chrome_bundle_all_frames", @"chrome_bundle_main_frame",
@"password_controller"
]) {}
};
// IDs used in the Username and Password <input> elements.
......
......@@ -108,12 +108,20 @@ source_set("unit_tests") {
]
}
js_compile_bundle("chrome_bundle") {
closure_entry_point = "__crWeb.chromeBundle"
js_compile_bundle("chrome_bundle_all_frames") {
closure_entry_point = "__crWeb.chromeBundleAllFrames"
sources = [
"//components/autofill/ios/fill/resources/form.js",
"resources/chrome_bundle_all_frames.js",
]
}
js_compile_bundle("chrome_bundle_main_frame") {
closure_entry_point = "__crWeb.chromeBundleMainFrame"
sources = [
"//components/autofill/ios/browser/resources/autofill_controller.js",
"//components/autofill/ios/fill/resources/fill.js",
"resources/chrome_bundle.js",
"resources/chrome_bundle_main_frame.js",
"resources/print.js",
]
......@@ -155,7 +163,8 @@ source_set("web_internal") {
"web_state_printer.h",
]
deps = [
":chrome_bundle",
":chrome_bundle_all_frames",
":chrome_bundle_main_frame",
"//base",
"//components/content_settings/core/browser",
"//components/dom_distiller/core",
......
......@@ -38,6 +38,8 @@ class ChromeWebClient : public web::WebClient {
std::vector<std::string>* additional_schemes) override;
void PostBrowserURLRewriterCreation(
web::BrowserURLRewriter* rewriter) override;
NSString* GetDocumentStartScriptForAllFrames(
web::BrowserState* browser_state) const override;
NSString* GetDocumentStartScriptForMainFrame(
web::BrowserState* browser_state) const override;
void AllowCertificateError(
......
......@@ -173,10 +173,15 @@ void ChromeWebClient::PostBrowserURLRewriterCreation(
rewriter->AddURLRewriter(&WillHandleWebBrowserAboutURL);
}
NSString* ChromeWebClient::GetDocumentStartScriptForAllFrames(
web::BrowserState* browser_state) const {
return GetPageScript(@"chrome_bundle_all_frames");
}
NSString* ChromeWebClient::GetDocumentStartScriptForMainFrame(
web::BrowserState* browser_state) const {
NSMutableArray* scripts = [NSMutableArray array];
[scripts addObject:GetPageScript(@"chrome_bundle")];
[scripts addObject:GetPageScript(@"chrome_bundle_main_frame")];
if (base::FeatureList::IsEnabled(features::kCredentialManager)) {
[scripts addObject:GetPageScript(@"credential_manager")];
......
// Copyright 2017 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.
// The set of scripts to be injected into the web view as early as possible.
goog.provide('__crWeb.chromeBundleAllFrames');
goog.require('__crWeb.form');
......@@ -3,7 +3,7 @@
// found in the LICENSE file.
// The set of scripts to be injected into the web view as early as possible.
goog.provide('__crWeb.chromeBundle');
goog.provide('__crWeb.chromeBundleMainFrame');
goog.require('__crWeb.autofill');
goog.require('__crWeb.fill');
......
......@@ -570,7 +570,6 @@ test("ios_web_inttests") {
"url_loader_inttest.mm",
"web_state/favicon_callbacks_inttest.mm",
"web_state/http_auth_inttest.mm",
"web_state/js/form_inttest.mm",
"web_state/navigation_and_load_callbacks_inttest.mm",
"webui/web_ui_mojo_inttest.mm",
]
......@@ -614,7 +613,6 @@ js_compile_bundle("all_frames_web_bundle") {
"web_state/js/resources/all_frames_web_bundle.js",
"web_state/js/resources/base.js",
"web_state/js/resources/common.js",
"web_state/js/resources/form.js",
"web_state/js/resources/message.js",
]
}
......
......@@ -44,6 +44,7 @@ template("js_compile_bundle") {
java_action(compile_js_target_name) {
visibility = [ ":$_target_name" ]
forward_variables_from(invoker, [ "testonly" ])
script = closure_compiler_path
sources = invoker.sources
outputs = [
......@@ -68,6 +69,7 @@ template("js_compile_bundle") {
"data_deps",
"deps",
"public_deps",
"testonly",
"visibility",
])
......@@ -114,6 +116,7 @@ template("js_compile_checked") {
java_action_foreach(compile_js_target_name) {
visibility = [ ":$_target_name" ]
forward_variables_from(invoker, [ "testonly" ])
script = closure_compiler_path
sources = invoker.sources
_js_modules = [
......@@ -178,6 +181,7 @@ template("js_compile_checked") {
"data_deps",
"deps",
"public_deps",
"testonly",
"visibility",
])
......@@ -197,6 +201,7 @@ template("js_compile_checked") {
"data_deps",
"deps",
"public_deps",
"testonly",
"visibility",
])
......@@ -233,6 +238,7 @@ template("js_compile_unchecked") {
java_action_foreach(compile_js_target_name) {
visibility = [ ":$_target_name" ]
forward_variables_from(invoker, [ "testonly" ])
script = closure_compiler_path
sources = invoker.sources
outputs = [
......@@ -255,6 +261,7 @@ template("js_compile_unchecked") {
"data_deps",
"deps",
"public_deps",
"testonly",
"visibility",
])
......@@ -274,6 +281,7 @@ template("js_compile_unchecked") {
"data_deps",
"deps",
"public_deps",
"testonly",
"visibility",
])
......
......@@ -6,6 +6,10 @@ source_set("test") {
configs += [ "//build/config/compiler:enable_arc" ]
testonly = true
public_deps = [
"//ios/web/public/test/fakes",
]
deps = [
"//base",
"//base/test:test_support",
......@@ -13,7 +17,6 @@ source_set("test") {
"//ios/web:web",
"//ios/web/interstitials",
"//ios/web/navigation:core",
"//ios/web/public/test/fakes",
"//ios/web/public/test/http_server",
"//ios/web/test:test_support",
"//testing/gtest",
......
......@@ -8,7 +8,6 @@ source_set("fakes") {
deps = [
"//base",
"//ios/web/public",
"//ios/web/public/download",
"//ios/web/test:test_constants",
"//ios/web/web_state:navigation_context",
......@@ -20,6 +19,10 @@ source_set("fakes") {
"//ui/base",
]
public_deps = [
"//ios/web/public",
]
sources = [
"crw_test_js_injection_receiver.h",
"crw_test_js_injection_receiver.mm",
......
......@@ -171,7 +171,7 @@ void WebTestWithWebState::WaitForBackgroundTasks() {
void WebTestWithWebState::WaitForCondition(ConditionBlock condition) {
base::test::ios::WaitUntilCondition(condition, true,
base::TimeDelta::FromSeconds(10));
base::TimeDelta::FromSeconds(1000));
}
id WebTestWithWebState::ExecuteJavaScript(NSString* script) {
......
......@@ -120,6 +120,16 @@ class WebClient {
// Gives the embedder a chance to provide the JavaScript to be injected into
// the web view as early as possible. Result must not be nil.
// The script returned will be injected in all frames (main and subframes).
//
// TODO(crbug.com/703964): Change the return value to NSArray<NSString*> to
// improve performance.
virtual NSString* GetDocumentStartScriptForAllFrames(
BrowserState* browser_state) const;
// Gives the embedder a chance to provide the JavaScript to be injected into
// the web view as early as possible. Result must not be nil.
// The script returned will only be injected in the main frame.
//
// TODO(crbug.com/703964): Change the return value to NSArray<NSString*> to
// improve performance.
......
......@@ -138,6 +138,7 @@ class WebStateObserver {
// Called on form submission in the main frame or in a same-origin iframe.
// |user_initiated| is true if the user interacted with the page.
// |is_main_frame| is true if the submitted form is in the main frame.
// TODO(crbug.com/823285): move this handler to components/autofill.
virtual void DocumentSubmitted(WebState* web_state,
const std::string& form_name,
bool user_initiated,
......@@ -146,6 +147,7 @@ class WebStateObserver {
// Called when the user is typing on a form field in the main frame or in a
// same-origin iframe. |params.input_missing| is indicating if there is any
// error when parsing the form field information.
// TODO(crbug.com/823285): move this handler to components/autofill.
virtual void FormActivityRegistered(WebState* web_state,
const FormActivityParams& params) {}
......
......@@ -70,6 +70,11 @@ base::RefCountedMemory* WebClient::GetDataResourceBytes(int resource_id) const {
return nullptr;
}
NSString* WebClient::GetDocumentStartScriptForAllFrames(
BrowserState* browser_state) const {
return @"";
}
NSString* WebClient::GetDocumentStartScriptForMainFrame(
BrowserState* browser_state) const {
return @"";
......
......@@ -93,8 +93,14 @@ NSString* GetDocumentStartScriptForMainFrame(BrowserState* browser_state) {
}
NSString* GetDocumentStartScriptForAllFrames(BrowserState* browser_state) {
return MakeScriptInjectableOnce(@"start_all_frames",
GetPageScript(@"all_frames_web_bundle"));
DCHECK(GetWebClient());
NSString* embedder_page_script =
GetWebClient()->GetDocumentStartScriptForAllFrames(browser_state);
DCHECK(embedder_page_script);
NSString* web_bundle = GetPageScript(@"all_frames_web_bundle");
NSString* script =
[NSString stringWithFormat:@"%@; %@", web_bundle, embedder_page_script];
return MakeScriptInjectableOnce(@"start_all_frames", script);
}
NSString* GetDocumentEndScriptForAllFrames(BrowserState* browser_state) {
......
......@@ -5,4 +5,7 @@
// Set of scripts required by web layer backed up by WKWebView.
goog.provide('__crWeb.allFramesWebBundle');
goog.require('__crWeb.form');
goog.require('__crWeb.base');
goog.require('__crWeb.common');
goog.require('__crWeb.message');
......@@ -831,9 +831,11 @@ typedef void (^ViewportStateCompletion)(const web::PageViewportState*);
- (BOOL)handleDocumentFaviconsMessage:(base::DictionaryValue*)message
context:(NSDictionary*)context;
// Handles 'document.submit' message.
// TODO(crbug.com/823285): move this handler to components/autofill.
- (BOOL)handleDocumentSubmitMessage:(base::DictionaryValue*)message
context:(NSDictionary*)context;
// Handles 'form.activity' message.
// TODO(crbug.com/823285): move this handler to components/autofill.
- (BOOL)handleFormActivityMessage:(base::DictionaryValue*)message
context:(NSDictionary*)context;
// Handles 'window.error' message.
......
......@@ -312,6 +312,7 @@ js_compile_bundle("web_view_bundle") {
sources = [
"//components/autofill/ios/browser/resources/autofill_controller.js",
"//components/autofill/ios/fill/resources/fill.js",
"//components/autofill/ios/fill/resources/form.js",
"resources/web_view_bundle.js",
]
}
......
......@@ -7,3 +7,4 @@ goog.provide('__crWeb.webViewBundle');
goog.require('__crWeb.autofill');
goog.require('__crWeb.fill');
goog.require('__crWeb.form');
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