Commit 6b0b7526 authored by Olivier Robin's avatar Olivier Robin Committed by Commit Bot

Migrate Form activity observer to autofill::FormActivityObserver

Cq-Include-Trybots: luci.chromium.try:ios-simulator-full-configs;master.tryserver.chromium.mac:ios-simulator-cronet
Change-Id: Ib146d46a7b50ed06ee26434fdcbd7166672141fa
Reviewed-on: https://chromium-review.googlesource.com/1118549Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Reviewed-by: default avatarMoe Ahmadi <mahmadi@chromium.org>
Commit-Queue: Olivier Robin <olivierrobin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#574189}
parent 4feb130a
...@@ -35,6 +35,7 @@ source_set("browser") { ...@@ -35,6 +35,7 @@ source_set("browser") {
"//base", "//base",
"//components/autofill/core/browser", "//components/autofill/core/browser",
"//components/autofill/core/common", "//components/autofill/core/common",
"//components/autofill/ios/form_util",
"//components/prefs:prefs", "//components/prefs:prefs",
"//components/prefs/ios", "//components/prefs/ios",
"//google_apis", "//google_apis",
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include "components/autofill/ios/browser/autofill_util.h" #include "components/autofill/ios/browser/autofill_util.h"
#import "components/autofill/ios/browser/form_suggestion.h" #import "components/autofill/ios/browser/form_suggestion.h"
#import "components/autofill/ios/browser/js_autofill_manager.h" #import "components/autofill/ios/browser/js_autofill_manager.h"
#import "components/autofill/ios/form_util/form_activity_observer_bridge.h"
#import "components/prefs/ios/pref_observer_bridge.h" #import "components/prefs/ios/pref_observer_bridge.h"
#include "components/prefs/pref_change_registrar.h" #include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_service.h" #include "components/prefs/pref_service.h"
...@@ -89,7 +90,9 @@ void GetFormAndField(autofill::FormData* form, ...@@ -89,7 +90,9 @@ void GetFormAndField(autofill::FormData* form,
} // namespace } // namespace
@interface AutofillAgent ()<CRWWebStateObserver, PrefObserverDelegate> @interface AutofillAgent ()<CRWWebStateObserver,
FormActivityObserver,
PrefObserverDelegate>
// Notifies the autofill manager when forms are detected on a page. // Notifies the autofill manager when forms are detected on a page.
- (void)notifyAutofillManager:(autofill::AutofillManager*)autofillManager - (void)notifyAutofillManager:(autofill::AutofillManager*)autofillManager
...@@ -191,6 +194,10 @@ void GetFormAndField(autofill::FormData* form, ...@@ -191,6 +194,10 @@ void GetFormAndField(autofill::FormData* form,
std::unique_ptr<PrefObserverBridge> prefObserverBridge_; std::unique_ptr<PrefObserverBridge> prefObserverBridge_;
// Registrar for pref changes notifications. // Registrar for pref changes notifications.
PrefChangeRegistrar prefChangeRegistrar_; PrefChangeRegistrar prefChangeRegistrar_;
// Bridge to observe form activity in |webState_|.
std::unique_ptr<autofill::FormActivityObserverBridge>
formActivityObserverBridge_;
} }
- (instancetype)initWithPrefService:(PrefService*)prefService - (instancetype)initWithPrefService:(PrefService*)prefService
...@@ -204,6 +211,8 @@ void GetFormAndField(autofill::FormData* form, ...@@ -204,6 +211,8 @@ void GetFormAndField(autofill::FormData* form,
webStateObserverBridge_ = webStateObserverBridge_ =
std::make_unique<web::WebStateObserverBridge>(self); std::make_unique<web::WebStateObserverBridge>(self);
webState_->AddObserver(webStateObserverBridge_.get()); webState_->AddObserver(webStateObserverBridge_.get());
formActivityObserverBridge_ =
std::make_unique<autofill::FormActivityObserverBridge>(webState_, self);
prefObserverBridge_ = std::make_unique<PrefObserverBridge>(self); prefObserverBridge_ = std::make_unique<PrefObserverBridge>(self);
prefChangeRegistrar_.Init(prefService); prefChangeRegistrar_.Init(prefService);
prefObserverBridge_->ObserveChangesForPreference( prefObserverBridge_->ObserveChangesForPreference(
...@@ -222,6 +231,7 @@ void GetFormAndField(autofill::FormData* form, ...@@ -222,6 +231,7 @@ void GetFormAndField(autofill::FormData* form,
- (void)dealloc { - (void)dealloc {
if (webState_) { if (webState_) {
formActivityObserverBridge_.reset();
webState_->RemoveObserver(webStateObserverBridge_.get()); webState_->RemoveObserver(webStateObserverBridge_.get());
webStateObserverBridge_.reset(); webStateObserverBridge_.reset();
webState_ = nullptr; webState_ = nullptr;
...@@ -232,6 +242,7 @@ void GetFormAndField(autofill::FormData* form, ...@@ -232,6 +242,7 @@ void GetFormAndField(autofill::FormData* form,
[[NSNotificationCenter defaultCenter] removeObserver:self]; [[NSNotificationCenter defaultCenter] removeObserver:self];
if (webState_) { if (webState_) {
formActivityObserverBridge_.reset();
webState_->RemoveObserver(webStateObserverBridge_.get()); webState_->RemoveObserver(webStateObserverBridge_.get());
webStateObserverBridge_.reset(); webStateObserverBridge_.reset();
webState_ = nullptr; webState_ = nullptr;
...@@ -505,51 +516,6 @@ void GetFormAndField(autofill::FormData* form, ...@@ -505,51 +516,6 @@ void GetFormAndField(autofill::FormData* form,
pendingFormJSON_ = nil; pendingFormJSON_ = nil;
} }
- (void)webState:(web::WebState*)webState
didSubmitDocumentWithFormNamed:(const std::string&)formName
userInitiated:(BOOL)userInitiated
isMainFrame:(BOOL)isMainFrame {
if (!isMainFrame) {
// Saving from iframes is not implemented.
return;
}
if (![self isAutofillEnabled])
return;
__weak AutofillAgent* weakSelf = self;
id completionHandler = ^(BOOL success, const FormDataVector& forms) {
AutofillAgent* strongSelf = weakSelf;
if (!strongSelf || !success)
return;
autofill::AutofillManager* autofillManager =
[strongSelf autofillManagerFromWebState:webState];
if (!autofillManager || forms.empty())
return;
if (forms.size() > 1) {
DLOG(WARNING) << "Only one form should be extracted.";
return;
}
[strongSelf notifyAutofillManager:autofillManager
ofFormsSubmitted:forms
userInitiated:userInitiated];
};
web::URLVerificationTrustLevel trustLevel;
const GURL pageURL(webState->GetCurrentURL(&trustLevel));
// This code is racing against the new page loading and will not get the
// password form data if the page has changed. In most cases this code wins
// the race.
// TODO(crbug.com/418827): Fix this by passing in more data from the JS side.
[self fetchFormsFiltered:YES
withName:base::UTF8ToUTF16(formName)
minimumRequiredFieldsCount:1
pageURL:pageURL
completionHandler:completionHandler];
}
- (void)webState:(web::WebState*)webState - (void)webState:(web::WebState*)webState
didStartNavigation:(web::NavigationContext*)navigation { didStartNavigation:(web::NavigationContext*)navigation {
// Ignore navigations within the same document, e.g., history.pushState(). // Ignore navigations within the same document, e.g., history.pushState().
...@@ -620,8 +586,11 @@ void GetFormAndField(autofill::FormData* form, ...@@ -620,8 +586,11 @@ void GetFormAndField(autofill::FormData* form,
completionHandler:completionHandler]; completionHandler:completionHandler];
} }
#pragma mark -
#pragma mark FormActivityObserver
- (void)webState:(web::WebState*)webState - (void)webState:(web::WebState*)webState
didRegisterFormActivity:(const web::FormActivityParams&)params { registeredFormActivity:(const web::FormActivityParams&)params {
if (![self isAutofillEnabled]) if (![self isAutofillEnabled])
return; return;
...@@ -681,6 +650,51 @@ void GetFormAndField(autofill::FormData* form, ...@@ -681,6 +650,51 @@ void GetFormAndField(autofill::FormData* form,
completionHandler:completionHandler]; completionHandler:completionHandler];
} }
- (void)webState:(web::WebState*)webState
submittedDocumentWithFormNamed:(const std::string&)formName
hasUserGesture:(BOOL)hasUserGesture
formInMainFrame:(BOOL)formInMainFrame {
if (!formInMainFrame) {
// Saving from iframes is not implemented.
return;
}
if (![self isAutofillEnabled])
return;
__weak AutofillAgent* weakSelf = self;
id completionHandler = ^(BOOL success, const FormDataVector& forms) {
AutofillAgent* strongSelf = weakSelf;
if (!strongSelf || !success)
return;
autofill::AutofillManager* autofillManager =
[strongSelf autofillManagerFromWebState:webState];
if (!autofillManager || forms.empty())
return;
if (forms.size() > 1) {
DLOG(WARNING) << "Only one form should be extracted.";
return;
}
[strongSelf notifyAutofillManager:autofillManager
ofFormsSubmitted:forms
userInitiated:hasUserGesture];
};
web::URLVerificationTrustLevel trustLevel;
const GURL pageURL(webState->GetCurrentURL(&trustLevel));
// This code is racing against the new page loading and will not get the
// password form data if the page has changed. In most cases this code wins
// the race.
// TODO(crbug.com/418827): Fix this by passing in more data from the JS side.
[self fetchFormsFiltered:YES
withName:base::UTF8ToUTF16(formName)
minimumRequiredFieldsCount:1
pageURL:pageURL
completionHandler:completionHandler];
}
#pragma mark - PrefObserverDelegate #pragma mark - PrefObserverDelegate
- (void)onPreferenceChanged:(const std::string&)preferenceName { - (void)onPreferenceChanged:(const std::string&)preferenceName {
......
...@@ -50,6 +50,7 @@ source_set("autofill") { ...@@ -50,6 +50,7 @@ source_set("autofill") {
"//base:i18n", "//base:i18n",
"//components/autofill/core/browser", "//components/autofill/core/browser",
"//components/autofill/ios/browser", "//components/autofill/ios/browser",
"//components/autofill/ios/form_util",
"//components/keyed_service/core", "//components/keyed_service/core",
"//components/keyed_service/ios", "//components/keyed_service/ios",
"//components/prefs", "//components/prefs",
...@@ -123,6 +124,7 @@ source_set("unit_tests") { ...@@ -123,6 +124,7 @@ source_set("unit_tests") {
"//components/autofill/core/browser:test_support", "//components/autofill/core/browser:test_support",
"//components/autofill/core/common", "//components/autofill/core/common",
"//components/autofill/ios/browser", "//components/autofill/ios/browser",
"//components/autofill/ios/form_util",
"//components/infobars/core", "//components/infobars/core",
"//components/keyed_service/core", "//components/keyed_service/core",
"//components/security_state/ios", "//components/security_state/ios",
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/mac/scoped_block.h" #include "base/mac/scoped_block.h"
#import "components/autofill/core/browser/keyboard_accessory_metrics_logger.h" #import "components/autofill/core/browser/keyboard_accessory_metrics_logger.h"
#import "components/autofill/ios/browser/js_suggestion_manager.h" #import "components/autofill/ios/browser/js_suggestion_manager.h"
#import "components/autofill/ios/form_util/form_activity_observer_bridge.h"
#import "ios/chrome/browser/autofill/form_input_accessory_view.h" #import "ios/chrome/browser/autofill/form_input_accessory_view.h"
#import "ios/chrome/browser/autofill/form_input_accessory_view_provider.h" #import "ios/chrome/browser/autofill/form_input_accessory_view_provider.h"
#import "ios/chrome/browser/autofill/form_suggestion_tab_helper.h" #import "ios/chrome/browser/autofill/form_suggestion_tab_helper.h"
...@@ -124,7 +125,7 @@ NSArray* FindDescendantToolbarItemsForActionName( ...@@ -124,7 +125,7 @@ NSArray* FindDescendantToolbarItemsForActionName(
} // namespace } // namespace
@interface FormInputAccessoryViewController () @interface FormInputAccessoryViewController ()<FormActivityObserver>
// Allows injection of the JsSuggestionManager. // Allows injection of the JsSuggestionManager.
- (instancetype)initWithWebState:(web::WebState*)webState - (instancetype)initWithWebState:(web::WebState*)webState
...@@ -192,6 +193,10 @@ NSArray* FindDescendantToolbarItemsForActionName( ...@@ -192,6 +193,10 @@ NSArray* FindDescendantToolbarItemsForActionName(
// Logs UMA metrics for the keyboard accessory. // Logs UMA metrics for the keyboard accessory.
std::unique_ptr<autofill::KeyboardAccessoryMetricsLogger> std::unique_ptr<autofill::KeyboardAccessoryMetricsLogger>
_keyboardAccessoryMetricsLogger; _keyboardAccessoryMetricsLogger;
// Bridge to observe form activity in |_webState|.
std::unique_ptr<autofill::FormActivityObserverBridge>
_formActivityObserverBridge;
} }
@synthesize grayBackgroundView = _grayBackgroundView; @synthesize grayBackgroundView = _grayBackgroundView;
...@@ -222,6 +227,8 @@ NSArray* FindDescendantToolbarItemsForActionName( ...@@ -222,6 +227,8 @@ NSArray* FindDescendantToolbarItemsForActionName(
_webStateObserverBridge = _webStateObserverBridge =
std::make_unique<web::WebStateObserverBridge>(self); std::make_unique<web::WebStateObserverBridge>(self);
_webState->AddObserver(_webStateObserverBridge.get()); _webState->AddObserver(_webStateObserverBridge.get());
_formActivityObserverBridge =
std::make_unique<autofill::FormActivityObserverBridge>(_webState, self);
_providers = [providers copy]; _providers = [providers copy];
_suggestionsHaveBeenShown = NO; _suggestionsHaveBeenShown = NO;
_keyboardAccessoryMetricsLogger.reset( _keyboardAccessoryMetricsLogger.reset(
...@@ -241,6 +248,7 @@ NSArray* FindDescendantToolbarItemsForActionName( ...@@ -241,6 +248,7 @@ NSArray* FindDescendantToolbarItemsForActionName(
- (void)dealloc { - (void)dealloc {
if (_webState) { if (_webState) {
_formActivityObserverBridge.reset();
_webState->RemoveObserver(_webStateObserverBridge.get()); _webState->RemoveObserver(_webStateObserverBridge.get());
_webStateObserverBridge.reset(); _webStateObserverBridge.reset();
_webState = nullptr; _webState = nullptr;
...@@ -260,6 +268,8 @@ NSArray* FindDescendantToolbarItemsForActionName( ...@@ -260,6 +268,8 @@ NSArray* FindDescendantToolbarItemsForActionName(
_webStateObserverBridge = _webStateObserverBridge =
std::make_unique<web::WebStateObserverBridge>(self); std::make_unique<web::WebStateObserverBridge>(self);
_webState->AddObserver(_webStateObserverBridge.get()); _webState->AddObserver(_webStateObserverBridge.get());
_formActivityObserverBridge =
std::make_unique<autofill::FormActivityObserverBridge>(_webState, self);
_providers = @[ FormSuggestionTabHelper::FromWebState(_webState) _providers = @[ FormSuggestionTabHelper::FromWebState(_webState)
->GetAccessoryViewProvider() ]; ->GetAccessoryViewProvider() ];
...@@ -273,6 +283,7 @@ NSArray* FindDescendantToolbarItemsForActionName( ...@@ -273,6 +283,7 @@ NSArray* FindDescendantToolbarItemsForActionName(
- (void)detachFromWebState { - (void)detachFromWebState {
[self reset]; [self reset];
if (_webState) { if (_webState) {
_formActivityObserverBridge.reset();
_webState->RemoveObserver(_webStateObserverBridge.get()); _webState->RemoveObserver(_webStateObserverBridge.get());
_webStateObserverBridge.reset(); _webStateObserverBridge.reset();
_webState = nullptr; _webState = nullptr;
...@@ -453,6 +464,29 @@ NSArray* FindDescendantToolbarItemsForActionName( ...@@ -453,6 +464,29 @@ NSArray* FindDescendantToolbarItemsForActionName(
completionHandler]; completionHandler];
} }
#pragma mark -
#pragma mark FormActivityObserver
- (void)webState:(web::WebState*)webState
registeredFormActivity:(const web::FormActivityParams&)params {
DCHECK_EQ(_webState, webState);
web::URLVerificationTrustLevel trustLevel;
const GURL pageURL(webState->GetCurrentURL(&trustLevel));
if (params.input_missing ||
trustLevel != web::URLVerificationTrustLevel::kAbsolute ||
!web::UrlHasWebScheme(pageURL) || !webState->ContentIsHTML()) {
[self reset];
return;
}
if (params.type == "blur" || params.type == "change" ||
params.type == "form_changed") {
return;
}
[self retrieveAccessoryViewForForm:params webState:webState];
}
#pragma mark - #pragma mark -
#pragma mark CRWWebStateObserver #pragma mark CRWWebStateObserver
...@@ -503,26 +537,6 @@ NSArray* FindDescendantToolbarItemsForActionName( ...@@ -503,26 +537,6 @@ NSArray* FindDescendantToolbarItemsForActionName(
[self reset]; [self reset];
} }
- (void)webState:(web::WebState*)webState
didRegisterFormActivity:(const web::FormActivityParams&)params {
DCHECK_EQ(_webState, webState);
web::URLVerificationTrustLevel trustLevel;
const GURL pageURL(webState->GetCurrentURL(&trustLevel));
if (params.input_missing ||
trustLevel != web::URLVerificationTrustLevel::kAbsolute ||
!web::UrlHasWebScheme(pageURL) || !webState->ContentIsHTML()) {
[self reset];
return;
}
if (params.type == "blur" || params.type == "change" ||
params.type == "form_changed") {
return;
}
[self retrieveAccessoryViewForForm:params webState:webState];
}
- (void)webStateDestroyed:(web::WebState*)webState { - (void)webStateDestroyed:(web::WebState*)webState {
DCHECK_EQ(_webState, webState); DCHECK_EQ(_webState, webState);
[self detachFromWebState]; [self detachFromWebState];
......
...@@ -12,15 +12,17 @@ ...@@ -12,15 +12,17 @@
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#import "components/autofill/ios/browser/form_suggestion.h" #import "components/autofill/ios/browser/form_suggestion.h"
#import "components/autofill/ios/browser/form_suggestion_provider.h" #import "components/autofill/ios/browser/form_suggestion_provider.h"
#import "components/autofill/ios/form_util/form_activity_observer_bridge.h"
#import "ios/chrome/browser/autofill/form_input_accessory_view_controller.h" #import "ios/chrome/browser/autofill/form_input_accessory_view_controller.h"
#import "ios/chrome/browser/autofill/form_suggestion_view.h" #import "ios/chrome/browser/autofill/form_suggestion_view.h"
#include "ios/chrome/browser/ui/ui_util.h" #include "ios/chrome/browser/ui/ui_util.h"
#import "ios/chrome/browser/web/chrome_web_test.h"
#import "ios/web/public/navigation_manager.h" #import "ios/web/public/navigation_manager.h"
#import "ios/web/public/test/fakes/test_web_state.h"
#include "ios/web/public/web_state/form_activity_params.h" #include "ios/web/public/web_state/form_activity_params.h"
#import "ios/web/public/web_state/ui/crw_web_view_proxy.h" #import "ios/web/public/web_state/ui/crw_web_view_proxy.h"
#import "ios/web/public/web_state/web_state.h" #import "ios/web/public/web_state/web_state.h"
#import "testing/gtest_mac.h" #import "testing/gtest_mac.h"
#import "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h" #import "third_party/ocmock/OCMock/OCMock.h"
#include "third_party/ocmock/gtest_support.h" #include "third_party/ocmock/gtest_support.h"
...@@ -135,12 +137,12 @@ FormSuggestionView* GetSuggestionView(UIView* parent) { ...@@ -135,12 +137,12 @@ FormSuggestionView* GetSuggestionView(UIView* parent) {
} }
// Test fixture for FormSuggestionController testing. // Test fixture for FormSuggestionController testing.
class FormSuggestionControllerTest : public ChromeWebTest { class FormSuggestionControllerTest : public PlatformTest {
public: public:
FormSuggestionControllerTest() {} FormSuggestionControllerTest() {}
void SetUp() override { void SetUp() override {
ChromeWebTest::SetUp(); PlatformTest::SetUp();
// Mock out the JsSuggestionManager. // Mock out the JsSuggestionManager.
mock_js_suggestion_manager_ = mock_js_suggestion_manager_ =
...@@ -159,16 +161,12 @@ class FormSuggestionControllerTest : public ChromeWebTest { ...@@ -159,16 +161,12 @@ class FormSuggestionControllerTest : public ChromeWebTest {
[OCMockObject niceMockForProtocol:@protocol(CRWWebViewProxy)]; [OCMockObject niceMockForProtocol:@protocol(CRWWebViewProxy)];
[[[mock_web_view_proxy_ stub] andReturn:input_accessory_view_] [[[mock_web_view_proxy_ stub] andReturn:input_accessory_view_]
keyboardAccessory]; keyboardAccessory];
test_web_state_.SetWebViewProxy(mock_web_view_proxy_);
} }
void TearDown() override { void TearDown() override {
[suggestion_controller_ detachFromWebState]; [suggestion_controller_ detachFromWebState];
ChromeWebTest::TearDown(); PlatformTest::TearDown();
}
// Sets |url| to be current for WebState.
void SetCurrentUrl(const std::string& url) {
LoadHtml(@"<html></html>", GURL(url));
} }
protected: protected:
...@@ -176,13 +174,13 @@ class FormSuggestionControllerTest : public ChromeWebTest { ...@@ -176,13 +174,13 @@ class FormSuggestionControllerTest : public ChromeWebTest {
// FormSuggestionProviders. // FormSuggestionProviders.
void SetUpController(NSArray* providers) { void SetUpController(NSArray* providers) {
suggestion_controller_ = [[FormSuggestionController alloc] suggestion_controller_ = [[FormSuggestionController alloc]
initWithWebState:web_state() initWithWebState:&test_web_state_
providers:providers providers:providers
JsSuggestionManager:mock_js_suggestion_manager_]; JsSuggestionManager:mock_js_suggestion_manager_];
[suggestion_controller_ setWebViewProxy:mock_web_view_proxy_]; [suggestion_controller_ setWebViewProxy:mock_web_view_proxy_];
@autoreleasepool { @autoreleasepool {
accessory_controller_ = [[FormInputAccessoryViewController alloc] accessory_controller_ = [[FormInputAccessoryViewController alloc]
initWithWebState:web_state() initWithWebState:&test_web_state_
JSSuggestionManager:mock_js_suggestion_manager_ JSSuggestionManager:mock_js_suggestion_manager_
providers:@[ providers:@[
[suggestion_controller_ accessoryViewProvider] [suggestion_controller_ accessoryViewProvider]
...@@ -242,15 +240,17 @@ class FormSuggestionControllerTest : public ChromeWebTest { ...@@ -242,15 +240,17 @@ class FormSuggestionControllerTest : public ChromeWebTest {
// Accessory view controller. // Accessory view controller.
FormInputAccessoryViewController* accessory_controller_; FormInputAccessoryViewController* accessory_controller_;
// The fake WebState to simulate navigation and JavaScript events.
web::TestWebState test_web_state_;
DISALLOW_COPY_AND_ASSIGN(FormSuggestionControllerTest); DISALLOW_COPY_AND_ASSIGN(FormSuggestionControllerTest);
}; };
// Tests that pages whose URLs don't have a web scheme aren't processed. // Tests that pages whose URLs don't have a web scheme aren't processed.
TEST_F(FormSuggestionControllerTest, PageLoadShouldBeIgnoredWhenNotWebScheme) { TEST_F(FormSuggestionControllerTest, PageLoadShouldBeIgnoredWhenNotWebScheme) {
SetUpController(@[]); SetUpController(@[]);
SetCurrentUrl("data:text/html;charset=utf8;base64,"); test_web_state_.SetCurrentURL(GURL("data:text/html;charset=utf8;base64,"));
[suggestion_controller_ webState:web_state() didLoadPageWithSuccess:YES]; test_web_state_.OnPageLoaded(web::PageLoadCompletionStatus::SUCCESS);
EXPECT_FALSE(GetSuggestionView(input_accessory_view_)); EXPECT_FALSE(GetSuggestionView(input_accessory_view_));
EXPECT_OCMOCK_VERIFY(mock_js_suggestion_manager_); EXPECT_OCMOCK_VERIFY(mock_js_suggestion_manager_);
} }
...@@ -258,24 +258,10 @@ TEST_F(FormSuggestionControllerTest, PageLoadShouldBeIgnoredWhenNotWebScheme) { ...@@ -258,24 +258,10 @@ TEST_F(FormSuggestionControllerTest, PageLoadShouldBeIgnoredWhenNotWebScheme) {
// Tests that pages whose content isn't HTML aren't processed. // Tests that pages whose content isn't HTML aren't processed.
TEST_F(FormSuggestionControllerTest, PageLoadShouldBeIgnoredWhenNotHtml) { TEST_F(FormSuggestionControllerTest, PageLoadShouldBeIgnoredWhenNotHtml) {
SetUpController(@[]); SetUpController(@[]);
// Construct file:// URL for a PDF file.
base::FilePath path;
base::PathService::Get(base::DIR_MODULE, &path);
const char kPdfFilePath[] = "ios/testing/data/http_server_files/testpage.pdf";
path = path.Append(FILE_PATH_LITERAL(kPdfFilePath));
GURL url(base::StringPrintf("file://%s", path.value().c_str()));
// Load PDF file URL. // Load PDF file URL.
web::NavigationManager::WebLoadParams params(url); test_web_state_.SetContentIsHTML(false);
web_state()->GetNavigationManager()->LoadURLWithParams(params); test_web_state_.OnPageLoaded(web::PageLoadCompletionStatus::SUCCESS);
WaitForCondition(^{
return !web_state()->IsLoading();
});
ASSERT_EQ("application/pdf", web_state()->GetContentsMimeType());
EXPECT_FALSE(GetSuggestionView(input_accessory_view_)); EXPECT_FALSE(GetSuggestionView(input_accessory_view_));
EXPECT_OCMOCK_VERIFY(mock_js_suggestion_manager_);
} }
// Tests that the keyboard accessory view is reset and JavaScript is injected // Tests that the keyboard accessory view is reset and JavaScript is injected
...@@ -283,12 +269,7 @@ TEST_F(FormSuggestionControllerTest, PageLoadShouldBeIgnoredWhenNotHtml) { ...@@ -283,12 +269,7 @@ TEST_F(FormSuggestionControllerTest, PageLoadShouldBeIgnoredWhenNotHtml) {
TEST_F(FormSuggestionControllerTest, TEST_F(FormSuggestionControllerTest,
PageLoadShouldRestoreKeyboardAccessoryViewAndInjectJavaScript) { PageLoadShouldRestoreKeyboardAccessoryViewAndInjectJavaScript) {
SetUpController(@[]); SetUpController(@[]);
SetCurrentUrl("http://foo.com"); test_web_state_.SetCurrentURL(GURL("http://foo.com"));
// Load the page. The JS should be injected.
[[mock_js_suggestion_manager_ expect] inject];
[suggestion_controller_ webState:web_state() didLoadPageWithSuccess:YES];
EXPECT_OCMOCK_VERIFY(mock_js_suggestion_manager_);
// Trigger form activity, which should set up the suggestions view. // Trigger form activity, which should set up the suggestions view.
web::FormActivityParams params; web::FormActivityParams params;
...@@ -299,12 +280,12 @@ TEST_F(FormSuggestionControllerTest, ...@@ -299,12 +280,12 @@ TEST_F(FormSuggestionControllerTest,
params.type = "type"; params.type = "type";
params.value = "value"; params.value = "value";
params.input_missing = false; params.input_missing = false;
[accessory_controller_ webState:web_state() didRegisterFormActivity:params]; test_web_state_.OnFormActivity(params);
EXPECT_TRUE(GetSuggestionView(input_accessory_view_)); EXPECT_TRUE(GetSuggestionView(input_accessory_view_));
// Trigger another page load. The suggestions accessory view should // Trigger another page load. The suggestions accessory view should
// not be present. // not be present.
[accessory_controller_ webState:web_state() didLoadPageWithSuccess:YES]; test_web_state_.OnPageLoaded(web::PageLoadCompletionStatus::SUCCESS);
EXPECT_FALSE(GetSuggestionView(input_accessory_view_)); EXPECT_FALSE(GetSuggestionView(input_accessory_view_));
} }
...@@ -318,7 +299,7 @@ TEST_F(FormSuggestionControllerTest, FormActivityBlurShouldBeIgnored) { ...@@ -318,7 +299,7 @@ TEST_F(FormSuggestionControllerTest, FormActivityBlurShouldBeIgnored) {
params.type = "blur"; // blur! params.type = "blur"; // blur!
params.value = "value"; params.value = "value";
params.input_missing = false; params.input_missing = false;
[accessory_controller_ webState:web_state() didRegisterFormActivity:params]; test_web_state_.OnFormActivity(params);
EXPECT_FALSE(GetSuggestionView(input_accessory_view_)); EXPECT_FALSE(GetSuggestionView(input_accessory_view_));
} }
...@@ -327,7 +308,7 @@ TEST_F(FormSuggestionControllerTest, ...@@ -327,7 +308,7 @@ TEST_F(FormSuggestionControllerTest,
FormActivityShouldRetrieveSuggestions_NoProvidersAvailable) { FormActivityShouldRetrieveSuggestions_NoProvidersAvailable) {
// Set up the controller without any providers. // Set up the controller without any providers.
SetUpController(@[]); SetUpController(@[]);
SetCurrentUrl("http://foo.com"); test_web_state_.SetCurrentURL(GURL("http://foo.com"));
web::FormActivityParams params; web::FormActivityParams params;
params.form_name = "form"; params.form_name = "form";
params.field_name = "field"; params.field_name = "field";
...@@ -336,7 +317,7 @@ TEST_F(FormSuggestionControllerTest, ...@@ -336,7 +317,7 @@ TEST_F(FormSuggestionControllerTest,
params.type = "type"; params.type = "type";
params.value = "value"; params.value = "value";
params.input_missing = false; params.input_missing = false;
[accessory_controller_ webState:web_state() didRegisterFormActivity:params]; test_web_state_.OnFormActivity(params);
// The suggestions accessory view should be empty. // The suggestions accessory view should be empty.
FormSuggestionView* suggestionView = GetSuggestionView(input_accessory_view_); FormSuggestionView* suggestionView = GetSuggestionView(input_accessory_view_);
...@@ -355,7 +336,7 @@ TEST_F(FormSuggestionControllerTest, ...@@ -355,7 +336,7 @@ TEST_F(FormSuggestionControllerTest,
TestSuggestionProvider* provider2 = TestSuggestionProvider* provider2 =
[[TestSuggestionProvider alloc] initWithSuggestions:@[]]; [[TestSuggestionProvider alloc] initWithSuggestions:@[]];
SetUpController(@[ provider1, provider2 ]); SetUpController(@[ provider1, provider2 ]);
SetCurrentUrl("http://foo.com"); test_web_state_.SetCurrentURL(GURL("http://foo.com"));
web::FormActivityParams params; web::FormActivityParams params;
params.form_name = "form"; params.form_name = "form";
...@@ -365,7 +346,7 @@ TEST_F(FormSuggestionControllerTest, ...@@ -365,7 +346,7 @@ TEST_F(FormSuggestionControllerTest,
params.type = "type"; params.type = "type";
params.value = "value"; params.value = "value";
params.input_missing = false; params.input_missing = false;
[accessory_controller_ webState:web_state() didRegisterFormActivity:params]; test_web_state_.OnFormActivity(params);
// The providers should each be asked if they have suggestions for the // The providers should each be asked if they have suggestions for the
// form in question. // form in question.
...@@ -405,7 +386,7 @@ TEST_F(FormSuggestionControllerTest, ...@@ -405,7 +386,7 @@ TEST_F(FormSuggestionControllerTest,
TestSuggestionProvider* provider2 = TestSuggestionProvider* provider2 =
[[TestSuggestionProvider alloc] initWithSuggestions:@[]]; [[TestSuggestionProvider alloc] initWithSuggestions:@[]];
SetUpController(@[ provider1, provider2 ]); SetUpController(@[ provider1, provider2 ]);
SetCurrentUrl("http://foo.com"); test_web_state_.SetCurrentURL(GURL("http://foo.com"));
web::FormActivityParams params; web::FormActivityParams params;
params.form_name = "form"; params.form_name = "form";
...@@ -415,7 +396,7 @@ TEST_F(FormSuggestionControllerTest, ...@@ -415,7 +396,7 @@ TEST_F(FormSuggestionControllerTest,
params.type = "type"; params.type = "type";
params.value = "value"; params.value = "value";
params.input_missing = false; params.input_missing = false;
[accessory_controller_ webState:web_state() didRegisterFormActivity:params]; test_web_state_.OnFormActivity(params);
// Since the first provider has suggestions available, it and only it // Since the first provider has suggestions available, it and only it
// should have been asked. // should have been asked.
...@@ -446,7 +427,7 @@ TEST_F(FormSuggestionControllerTest, SelectingSuggestionShouldNotifyDelegate) { ...@@ -446,7 +427,7 @@ TEST_F(FormSuggestionControllerTest, SelectingSuggestionShouldNotifyDelegate) {
TestSuggestionProvider* provider = TestSuggestionProvider* provider =
[[TestSuggestionProvider alloc] initWithSuggestions:suggestions]; [[TestSuggestionProvider alloc] initWithSuggestions:suggestions];
SetUpController(@[ provider ]); SetUpController(@[ provider ]);
SetCurrentUrl("http://foo.com"); test_web_state_.SetCurrentURL(GURL("http://foo.com"));
web::FormActivityParams params; web::FormActivityParams params;
params.form_name = "form"; params.form_name = "form";
params.field_name = "field"; params.field_name = "field";
...@@ -455,7 +436,7 @@ TEST_F(FormSuggestionControllerTest, SelectingSuggestionShouldNotifyDelegate) { ...@@ -455,7 +436,7 @@ TEST_F(FormSuggestionControllerTest, SelectingSuggestionShouldNotifyDelegate) {
params.type = "type"; params.type = "type";
params.value = "value"; params.value = "value";
params.input_missing = false; params.input_missing = false;
[accessory_controller_ webState:web_state() didRegisterFormActivity:params]; test_web_state_.OnFormActivity(params);
// Selecting a suggestion should notify the delegate. // Selecting a suggestion should notify the delegate.
[suggestion_controller_ didSelectSuggestion:suggestions[0]]; [suggestion_controller_ didSelectSuggestion:suggestions[0]];
......
...@@ -50,6 +50,7 @@ source_set("passwords") { ...@@ -50,6 +50,7 @@ source_set("passwords") {
"//components/autofill/core/browser", "//components/autofill/core/browser",
"//components/autofill/core/common", "//components/autofill/core/common",
"//components/autofill/ios/browser", "//components/autofill/ios/browser",
"//components/autofill/ios/form_util",
"//components/browser_sync", "//components/browser_sync",
"//components/image_fetcher/core", "//components/image_fetcher/core",
"//components/image_fetcher/ios", "//components/image_fetcher/ios",
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "components/autofill/core/common/password_form.h" #include "components/autofill/core/common/password_form.h"
#include "components/autofill/core/common/password_form_fill_data.h" #include "components/autofill/core/common/password_form_fill_data.h"
#include "components/autofill/ios/browser/autofill_util.h" #include "components/autofill/ios/browser/autofill_util.h"
#import "components/autofill/ios/form_util/form_activity_observer_bridge.h"
#include "components/browser_sync/profile_sync_service.h" #include "components/browser_sync/profile_sync_service.h"
#include "components/infobars/core/infobar_manager.h" #include "components/infobars/core/infobar_manager.h"
#include "components/password_manager/core/browser/form_parsing/ios_form_parser.h" #include "components/password_manager/core/browser/form_parsing/ios_form_parser.h"
...@@ -121,7 +122,9 @@ void LogSuggestionShown(PasswordSuggestionType type) { ...@@ -121,7 +122,9 @@ void LogSuggestionShown(PasswordSuggestionType type) {
@end @end
@interface PasswordController ()<FormSuggestionProvider, PasswordFormFiller> @interface PasswordController ()<FormActivityObserver,
FormSuggestionProvider,
PasswordFormFiller>
// Parses the |jsonString| which contatins the password forms found on a web // Parses the |jsonString| which contatins the password forms found on a web
// page to populate the |forms| vector. // page to populate the |forms| vector.
...@@ -318,6 +321,10 @@ bool GetPageURLAndCheckTrustLevel(web::WebState* web_state, GURL* page_url) { ...@@ -318,6 +321,10 @@ bool GetPageURLAndCheckTrustLevel(web::WebState* web_state, GURL* page_url) {
// User credential waiting to be displayed in autosign-in snackbar, once tab // User credential waiting to be displayed in autosign-in snackbar, once tab
// becomes active. // becomes active.
std::unique_ptr<autofill::PasswordForm> pendingAutoSigninPasswordForm_; std::unique_ptr<autofill::PasswordForm> pendingAutoSigninPasswordForm_;
// Bridge to observe form activity in |webState_|.
std::unique_ptr<autofill::FormActivityObserverBridge>
formActivityObserverBridge_;
} }
@synthesize isWebStateDestroyed = _isWebStateDestroyed; @synthesize isWebStateDestroyed = _isWebStateDestroyed;
...@@ -353,6 +360,8 @@ bool GetPageURLAndCheckTrustLevel(web::WebState* web_state, GURL* page_url) { ...@@ -353,6 +360,8 @@ bool GetPageURLAndCheckTrustLevel(web::WebState* web_state, GURL* page_url) {
webStateObserverBridge_ = webStateObserverBridge_ =
std::make_unique<web::WebStateObserverBridge>(self); std::make_unique<web::WebStateObserverBridge>(self);
webState_->AddObserver(webStateObserverBridge_.get()); webState_->AddObserver(webStateObserverBridge_.get());
formActivityObserverBridge_ =
std::make_unique<autofill::FormActivityObserverBridge>(webState_, self);
passwordJsManager_ = [[JsPasswordManager alloc] passwordJsManager_ = [[JsPasswordManager alloc]
initWithReceiver:webState_->GetJSInjectionReceiver()]; initWithReceiver:webState_->GetJSInjectionReceiver()];
sentRequestToStore_ = NO; sentRequestToStore_ = NO;
...@@ -382,6 +391,7 @@ bool GetPageURLAndCheckTrustLevel(web::WebState* web_state, GURL* page_url) { ...@@ -382,6 +391,7 @@ bool GetPageURLAndCheckTrustLevel(web::WebState* web_state, GURL* page_url) {
[self detach]; [self detach];
if (webState_) { if (webState_) {
formActivityObserverBridge_.reset();
webState_->RemoveObserver(webStateObserverBridge_.get()); webState_->RemoveObserver(webStateObserverBridge_.get());
webStateObserverBridge_.reset(); webStateObserverBridge_.reset();
webState_ = nullptr; webState_ = nullptr;
...@@ -400,6 +410,7 @@ bool GetPageURLAndCheckTrustLevel(web::WebState* web_state, GURL* page_url) { ...@@ -400,6 +410,7 @@ bool GetPageURLAndCheckTrustLevel(web::WebState* web_state, GURL* page_url) {
- (void)detach { - (void)detach {
if (webState_) { if (webState_) {
formActivityObserverBridge_.reset();
webState_->RemoveScriptCommandCallback(kCommandPrefix); webState_->RemoveScriptCommandCallback(kCommandPrefix);
webState_->RemoveObserver(webStateObserverBridge_.get()); webState_->RemoveObserver(webStateObserverBridge_.get());
webStateObserverBridge_.reset(); webStateObserverBridge_.reset();
...@@ -444,6 +455,39 @@ bool GetPageURLAndCheckTrustLevel(web::WebState* web_state, GURL* page_url) { ...@@ -444,6 +455,39 @@ bool GetPageURLAndCheckTrustLevel(web::WebState* web_state, GURL* page_url) {
}]; }];
} }
#pragma mark -
#pragma mark FormActivityObserver
- (void)webState:(web::WebState*)webState
submittedDocumentWithFormNamed:(const std::string&)formName
hasUserGesture:(BOOL)hasUserGesture
formInMainFrame:(BOOL)formInMainFrame {
DCHECK_EQ(webState_, webState);
__weak PasswordController* weakSelf = self;
// This code is racing against the new page loading and will not get the
// password form data if the page has changed. In most cases this code wins
// the race.
// TODO(crbug.com/418827): Fix this by passing in more data from the JS side.
id completionHandler = ^(BOOL found, const autofill::PasswordForm& form) {
PasswordController* strongSelf = weakSelf;
if (!strongSelf || [strongSelf isWebStateDestroyed] ||
!strongSelf.passwordManager) {
return;
}
if (formInMainFrame) {
strongSelf.passwordManager->OnPasswordFormSubmitted(
strongSelf.passwordManagerDriver, form);
} else {
// Show a save prompt immediately because for iframes it is very hard to
// figure out correctness of password forms submission.
strongSelf.passwordManager->OnPasswordFormSubmittedNoChecks(
strongSelf.passwordManagerDriver, form);
}
};
[self extractSubmittedPasswordForm:formName
completionHandler:completionHandler];
}
#pragma mark - #pragma mark -
#pragma mark CRWWebStateObserver #pragma mark CRWWebStateObserver
...@@ -492,35 +536,6 @@ bool GetPageURLAndCheckTrustLevel(web::WebState* web_state, GURL* page_url) { ...@@ -492,35 +536,6 @@ bool GetPageURLAndCheckTrustLevel(web::WebState* web_state, GURL* page_url) {
[self findPasswordFormsAndSendThemToPasswordStore]; [self findPasswordFormsAndSendThemToPasswordStore];
} }
- (void)webState:(web::WebState*)webState
didSubmitDocumentWithFormNamed:(const std::string&)formName
userInitiated:(BOOL)userInitiated
isMainFrame:(BOOL)isMainFrame {
DCHECK_EQ(webState_, webState);
__weak PasswordController* weakSelf = self;
// This code is racing against the new page loading and will not get the
// password form data if the page has changed. In most cases this code wins
// the race.
// TODO(crbug.com/418827): Fix this by passing in more data from the JS side.
id completionHandler = ^(BOOL found, const autofill::PasswordForm& form) {
PasswordController* strongSelf = weakSelf;
if (strongSelf && ![strongSelf isWebStateDestroyed] &&
strongSelf.passwordManager) {
if (isMainFrame) {
strongSelf.passwordManager->OnPasswordFormSubmitted(
strongSelf.passwordManagerDriver, form);
} else {
// Show a save prompt immediately because for iframes it is very hard to
// figure out correctness of password forms submission.
strongSelf.passwordManager->OnPasswordFormSubmittedNoChecks(
strongSelf.passwordManagerDriver, form);
}
}
};
[self extractSubmittedPasswordForm:formName
completionHandler:completionHandler];
}
- (void)webStateDestroyed:(web::WebState*)webState { - (void)webStateDestroyed:(web::WebState*)webState {
DCHECK_EQ(webState_, webState); DCHECK_EQ(webState_, webState);
_isWebStateDestroyed = YES; _isWebStateDestroyed = YES;
......
...@@ -213,6 +213,7 @@ ios_web_view_deps = [ ...@@ -213,6 +213,7 @@ ios_web_view_deps = [
"//components/autofill/core/browser", "//components/autofill/core/browser",
"//components/autofill/core/common", "//components/autofill/core/common",
"//components/autofill/ios/browser", "//components/autofill/ios/browser",
"//components/autofill/ios/form_util",
"//components/content_settings/core/browser", "//components/content_settings/core/browser",
"//components/flags_ui", "//components/flags_ui",
"//components/image_fetcher/ios", "//components/image_fetcher/ios",
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "components/autofill/ios/browser/autofill_driver_ios_bridge.h" #include "components/autofill/ios/browser/autofill_driver_ios_bridge.h"
#import "components/autofill/ios/browser/js_autofill_manager.h" #import "components/autofill/ios/browser/js_autofill_manager.h"
#import "components/autofill/ios/browser/js_suggestion_manager.h" #import "components/autofill/ios/browser/js_suggestion_manager.h"
#import "components/autofill/ios/form_util/form_activity_observer_bridge.h"
#include "components/keyed_service/core/service_access_type.h" #include "components/keyed_service/core/service_access_type.h"
#include "ios/web/public/web_state/form_activity_params.h" #include "ios/web/public/web_state/form_activity_params.h"
#import "ios/web/public/web_state/js/crw_js_injection_receiver.h" #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
...@@ -34,7 +35,8 @@ ...@@ -34,7 +35,8 @@
@interface CWVAutofillController ()<AutofillDriverIOSBridge, @interface CWVAutofillController ()<AutofillDriverIOSBridge,
CRWWebStateObserver, CRWWebStateObserver,
CWVAutofillClientIOSBridge> CWVAutofillClientIOSBridge,
FormActivityObserver>
@end @end
...@@ -63,6 +65,9 @@ ...@@ -63,6 +65,9 @@
// The current credit card verifier. Can be nil if no verification is pending. // The current credit card verifier. Can be nil if no verification is pending.
// Held weak because |_delegate| is responsible for maintaing its lifetime. // Held weak because |_delegate| is responsible for maintaing its lifetime.
__weak CWVCreditCardVerifier* _verifier; __weak CWVCreditCardVerifier* _verifier;
std::unique_ptr<autofill::FormActivityObserverBridge>
_formActivityObserverBridge;
} }
@synthesize delegate = _delegate; @synthesize delegate = _delegate;
...@@ -85,6 +90,9 @@ ...@@ -85,6 +90,9 @@
std::make_unique<web::WebStateObserverBridge>(self); std::make_unique<web::WebStateObserverBridge>(self);
_webState->AddObserver(_webStateObserverBridge.get()); _webState->AddObserver(_webStateObserverBridge.get());
_formActivityObserverBridge =
std::make_unique<autofill::FormActivityObserverBridge>(webState, self);
_autofillClient.reset(new autofill::WebViewAutofillClientIOS( _autofillClient.reset(new autofill::WebViewAutofillClientIOS(
browserState->GetPrefs(), browserState->GetPrefs(),
ios_web_view::WebViewPersonalDataManagerFactory::GetForBrowserState( ios_web_view::WebViewPersonalDataManagerFactory::GetForBrowserState(
...@@ -111,6 +119,7 @@ ...@@ -111,6 +119,7 @@
- (void)dealloc { - (void)dealloc {
if (_webState) { if (_webState) {
_formActivityObserverBridge.reset();
_webState->RemoveObserver(_webStateObserverBridge.get()); _webState->RemoveObserver(_webStateObserverBridge.get());
_webStateObserverBridge.reset(); _webStateObserverBridge.reset();
_webState = nullptr; _webState = nullptr;
...@@ -332,7 +341,7 @@ showUnmaskPromptForCard:(const autofill::CreditCard&)creditCard ...@@ -332,7 +341,7 @@ showUnmaskPromptForCard:(const autofill::CreditCard&)creditCard
#pragma mark - CRWWebStateObserver #pragma mark - CRWWebStateObserver
- (void)webState:(web::WebState*)webState - (void)webState:(web::WebState*)webState
didRegisterFormActivity:(const web::FormActivityParams&)params { registeredFormActivity:(const web::FormActivityParams&)params {
DCHECK_EQ(_webState, webState); DCHECK_EQ(_webState, webState);
[_JSSuggestionManager inject]; [_JSSuggestionManager inject];
...@@ -376,9 +385,9 @@ showUnmaskPromptForCard:(const autofill::CreditCard&)creditCard ...@@ -376,9 +385,9 @@ showUnmaskPromptForCard:(const autofill::CreditCard&)creditCard
} }
- (void)webState:(web::WebState*)webState - (void)webState:(web::WebState*)webState
didSubmitDocumentWithFormNamed:(const std::string&)formName submittedDocumentWithFormNamed:(const std::string&)formName
userInitiated:(BOOL)userInitiated hasUserGesture:(BOOL)userInitiated
isMainFrame:(BOOL)isMainFrame { formInMainFrame:(BOOL)isMainFrame {
if ([_delegate respondsToSelector:@selector if ([_delegate respondsToSelector:@selector
(autofillController:didSubmitFormWithName:userInitiated (autofillController:didSubmitFormWithName:userInitiated
:isMainFrame:)]) { :isMainFrame:)]) {
...@@ -391,6 +400,7 @@ showUnmaskPromptForCard:(const autofill::CreditCard&)creditCard ...@@ -391,6 +400,7 @@ showUnmaskPromptForCard:(const autofill::CreditCard&)creditCard
- (void)webStateDestroyed:(web::WebState*)webState { - (void)webStateDestroyed:(web::WebState*)webState {
DCHECK_EQ(_webState, webState); DCHECK_EQ(_webState, webState);
_formActivityObserverBridge.reset();
[_autofillAgent detachFromWebState]; [_autofillAgent detachFromWebState];
_autofillClient.reset(); _autofillClient.reset();
_webState->RemoveObserver(_webStateObserverBridge.get()); _webState->RemoveObserver(_webStateObserverBridge.get());
......
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