chromium/components/autofill/ios/browser/autofill_driver_ios_factory_unittest.mm

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "components/autofill/ios/browser/autofill_driver_ios_factory.h"

#import <memory>

#import "components/autofill/core/browser/test_autofill_client.h"
#import "components/autofill/core/common/test_matchers.h"
#import "components/autofill/ios/browser/autofill_driver_ios.h"
#import "components/autofill/ios/browser/autofill_driver_ios_bridge.h"
#import "components/autofill/ios/browser/autofill_driver_ios_factory.h"
#import "components/autofill/ios/browser/autofill_java_script_feature.h"
#import "ios/web/public/test/fakes/fake_browser_state.h"
#import "ios/web/public/test/fakes/fake_web_frame.h"
#import "ios/web/public/test/fakes/fake_web_frames_manager.h"
#import "ios/web/public/test/fakes/fake_web_state.h"
#import "ios/web/public/test/web_test.h"
#import "testing/gmock/include/gmock/gmock.h"
#import "testing/gtest/include/gtest/gtest.h"

namespace autofill {
namespace {

using ::autofill::test::LazyRef;
using ::autofill::test::SaveArgPtr;
using ::testing::_;
using ::testing::AllOf;
using ::testing::DoAll;
using ::testing::Eq;
using ::testing::InSequence;
using ::testing::MockFunction;
using ::testing::Property;
using ::testing::Ref;

using LifecycleState = AutofillDriver::LifecycleState;
using enum LifecycleState;

auto HasState(LifecycleState expected_state) {
  return Property("AutofillDriver::GetLifecycleState",
                  &AutofillDriver::GetLifecycleState, expected_state);
}

class MockWebFramesManagerObserver : public web::WebFramesManager::Observer {
 public:
  MockWebFramesManagerObserver() = default;
  MockWebFramesManagerObserver(const MockWebFramesManagerObserver&) = delete;
  MockWebFramesManagerObserver& operator=(const MockWebFramesManagerObserver&) =
      delete;
  ~MockWebFramesManagerObserver() override = default;
  MOCK_METHOD(void,
              WebFrameBecameAvailable,
              (web::WebFramesManager * web_frames_manager,
               web::WebFrame* web_frame),
              (override));
  MOCK_METHOD(void,
              WebFrameBecameUnavailable,
              (web::WebFramesManager * web_frames_manager,
               const std::string& frame_id),
              (override));
};

class MockAutofillDriverIOSFactoryObserver
    : public AutofillDriverIOSFactory::Observer {
 public:
  MockAutofillDriverIOSFactoryObserver() = default;
  MockAutofillDriverIOSFactoryObserver(
      const MockAutofillDriverIOSFactoryObserver&) = delete;
  MockAutofillDriverIOSFactoryObserver& operator=(
      const MockAutofillDriverIOSFactoryObserver&) = delete;
  ~MockAutofillDriverIOSFactoryObserver() override = default;
  MOCK_METHOD(void,
              OnAutofillDriverIOSFactoryDestroyed,
              (AutofillDriverIOSFactory & factory),
              (override));
  MOCK_METHOD(void,
              OnAutofillDriverIOSCreated,
              (AutofillDriverIOSFactory & factory, AutofillDriverIOS& driver),
              (override));
  MOCK_METHOD(void,
              OnAutofillDriverIOSStateChanged,
              (AutofillDriverIOSFactory & factory,
               AutofillDriverIOS& driver,
               AutofillDriver::LifecycleState old_state,
               AutofillDriver::LifecycleState new_state),
              (override));
};

class AutofillDriverIOSFactoryTest : public web::WebTest {
 public:
  AutofillDriverIOSFactoryTest() = default;
  ~AutofillDriverIOSFactoryTest() override = default;

  void SetUp() override {
    web::WebTest::SetUp();

    OverrideJavaScriptFeatures({AutofillJavaScriptFeature::GetInstance()});

    web_state_.SetWebFramesManager(
        content_world(), std::make_unique<web::FakeWebFramesManager>());
    web_state_.SetContentIsHTML(true);
    web_state_.SetBrowserState(GetBrowserState());
    web_frames_manager().AddObserver(&pre_factory_);
    ASSERT_FALSE(AutofillDriverIOSFactory::FromWebState(&web_state_));
    AutofillDriverIOSFactory::CreateForWebState(&web_state_, &client_, nil,
                                                "en-US");
    factory().AddObserver(&factory_observer_);
    web_frames_manager().AddObserver(&post_factory_);
  }

  void TearDown() override {
    web_frames_manager().RemoveObserver(&pre_factory_);
    factory().RemoveObserver(&factory_observer_);
    web_frames_manager().RemoveObserver(&post_factory_);
  }

  std::unique_ptr<web::FakeWebFrame> CreateMainFrame() {
    std::unique_ptr<web::FakeWebFrame> frame =
        web::FakeWebFrame::CreateMainWebFrame(GURL());
    frame->set_browser_state(GetBrowserState());
    return frame;
  }

  std::unique_ptr<web::FakeWebFrame> CreateChildFrame() {
    std::unique_ptr<web::FakeWebFrame> frame =
        web::FakeWebFrame::CreateChildWebFrame(GURL());
    frame->set_browser_state(GetBrowserState());
    return frame;
  }

  web::WebFrame* AddFrame(std::unique_ptr<web::WebFrame> frame) {
    web::WebFrame* raw = frame.get();
    web_frames_manager().AddWebFrame(std::move(frame));
    return raw;
  }

  void RemoveFrame(const std::string& frame_id) {
    web_frames_manager().RemoveWebFrame(frame_id);
  }

  AutofillDriverIOSFactory& factory() {
    return *AutofillDriverIOSFactory::FromWebState(&web_state_);
  }

  MockWebFramesManagerObserver& pre_factory() { return pre_factory_; }

  MockWebFramesManagerObserver& post_factory() { return post_factory_; }

  MockAutofillDriverIOSFactoryObserver& factory_observer() {
    return factory_observer_;
  }

 private:
  web::ContentWorld content_world() {
    return AutofillJavaScriptFeature::GetInstance()->GetSupportedContentWorld();
  }

  web::FakeWebFramesManager& web_frames_manager() {
    return static_cast<web::FakeWebFramesManager&>(
        *web_state_.GetWebFramesManager(content_world()));
  }

  MockWebFramesManagerObserver pre_factory_;
  MockWebFramesManagerObserver post_factory_;
  MockAutofillDriverIOSFactoryObserver factory_observer_;
  TestAutofillClient client_;
  web::FakeWebState web_state_;
};

// Two helper macros to make tests below more readable.
#define EXPECT_DRIVER_CREATED(driver_ptr_ptr)                                  \
  EXPECT_CALL(factory_observer(),                                              \
              OnAutofillDriverIOSCreated(Ref(factory()), HasState(kInactive))) \
      .WillOnce(DoAll(SaveArgPtr<1>((driver_ptr_ptr))))
#define EXPECT_LIFECYCLE_CHANGE(driver_matcher, from, to)                  \
  EXPECT_CALL(factory_observer(),                                          \
              OnAutofillDriverIOSStateChanged(                             \
                  Ref(factory()), AllOf((driver_matcher), HasState((to))), \
                  (from), (to)))

// Tests that the basic lifecycle state changes of a driver.
TEST_F(AutofillDriverIOSFactoryTest, DriverLifecycle) {
  std::unique_ptr<web::WebFrame> owned_frame = CreateMainFrame();
  web::WebFrame* frame = owned_frame.get();
  AutofillDriverIOS* driver = nullptr;

  MockFunction<void(std::string_view)> checkpoint;
  {
    InSequence sequence;
    EXPECT_CALL(checkpoint, Call("Available"));
    EXPECT_CALL(checkpoint, Call("DriverForFrame"));
    EXPECT_DRIVER_CREATED(&driver);
    EXPECT_LIFECYCLE_CHANGE(LazyRef(driver), kInactive, kActive);
    EXPECT_CALL(checkpoint, Call("Unavailable"));
    EXPECT_LIFECYCLE_CHANGE(LazyRef(driver), kActive, kPendingDeletion);
    EXPECT_CALL(checkpoint, Call("Finish"));
  }

  checkpoint.Call("Available");
  AddFrame(std::move(owned_frame));
  checkpoint.Call("DriverForFrame");
  factory().DriverForFrame(frame);
  checkpoint.Call("Unavailable");
  RemoveFrame(frame->GetFrameId());
  checkpoint.Call("Finish");
}

// Tests that only the first call of DriverForFrame() triggers events.
TEST_F(AutofillDriverIOSFactoryTest, DriverForFrameCalledMultipleTimes) {
  std::unique_ptr<web::WebFrame> owned_frame = CreateMainFrame();
  web::WebFrame* frame = owned_frame.get();
  AutofillDriverIOS* driver = nullptr;

  MockFunction<void(std::string_view)> checkpoint;
  {
    InSequence sequence;
    EXPECT_CALL(checkpoint, Call("Available"));
    EXPECT_CALL(checkpoint, Call("DriverForFrame"));
    EXPECT_DRIVER_CREATED(&driver);
    EXPECT_LIFECYCLE_CHANGE(LazyRef(driver), kInactive, kActive);
    EXPECT_CALL(checkpoint, Call("Unavailable"));
    EXPECT_LIFECYCLE_CHANGE(LazyRef(driver), kActive, kPendingDeletion);
    EXPECT_CALL(checkpoint, Call("Finish"));
  }

  checkpoint.Call("Available");
  AddFrame(std::move(owned_frame));
  checkpoint.Call("DriverForFrame");
  factory().DriverForFrame(frame);
  factory().DriverForFrame(frame);
  factory().DriverForFrame(frame);
  checkpoint.Call("Unavailable");
  RemoveFrame(frame->GetFrameId());
  checkpoint.Call("Finish");
}

// Tests that the factory can manage multiple drivers.
TEST_F(AutofillDriverIOSFactoryTest, MultipleDrivers) {
  std::unique_ptr<web::WebFrame> owned_frame1 = CreateMainFrame();
  std::unique_ptr<web::WebFrame> owned_frame2 = CreateChildFrame();
  web::WebFrame* frame1 = owned_frame1.get();
  web::WebFrame* frame2 = owned_frame2.get();
  AutofillDriverIOS* driver1 = nullptr;
  AutofillDriverIOS* driver2 = nullptr;

  MockFunction<void(std::string_view)> checkpoint;
  {
    InSequence sequence;
    EXPECT_CALL(checkpoint, Call("Available"));
    EXPECT_CALL(checkpoint, Call("DriverForFrame"));
    EXPECT_DRIVER_CREATED(&driver1);
    EXPECT_LIFECYCLE_CHANGE(LazyRef(driver1), kInactive, kActive);
    EXPECT_DRIVER_CREATED(&driver2);
    EXPECT_LIFECYCLE_CHANGE(LazyRef(driver2), kInactive, kActive);
    EXPECT_CALL(checkpoint, Call("Unavailable"));
    EXPECT_LIFECYCLE_CHANGE(LazyRef(driver2), kActive, kPendingDeletion);
    EXPECT_LIFECYCLE_CHANGE(LazyRef(driver1), kActive, kPendingDeletion);
    EXPECT_CALL(checkpoint, Call("Finish"));
  }

  checkpoint.Call("Available");
  AddFrame(std::move(owned_frame1));
  AddFrame(std::move(owned_frame2));
  checkpoint.Call("DriverForFrame");
  factory().DriverForFrame(frame1);
  factory().DriverForFrame(frame2);
  checkpoint.Call("Unavailable");
  RemoveFrame(frame2->GetFrameId());
  RemoveFrame(frame1->GetFrameId());
  checkpoint.Call("Finish");
}

// Tests that the factory creates drivers lazily.
TEST_F(AutofillDriverIOSFactoryTest,
       DriverForFrameBetweenAvailableAndUnavailable) {
  std::unique_ptr<web::WebFrame> owned_frame = CreateMainFrame();
  web::WebFrame* frame = owned_frame.get();
  AutofillDriverIOS* driver = nullptr;

  MockFunction<void(std::string_view)> checkpoint;
  {
    InSequence sequence;
    EXPECT_CALL(checkpoint, Call("Available"));
    EXPECT_CALL(pre_factory(), WebFrameBecameAvailable);
    EXPECT_CALL(post_factory(), WebFrameBecameAvailable);
    EXPECT_CALL(checkpoint, Call("DriverForFrame"));
    EXPECT_DRIVER_CREATED(&driver);
    EXPECT_LIFECYCLE_CHANGE(LazyRef(driver), kInactive, kActive);
    EXPECT_CALL(checkpoint, Call("Unavailable"));
    EXPECT_CALL(pre_factory(), WebFrameBecameUnavailable);
    EXPECT_LIFECYCLE_CHANGE(LazyRef(driver), kActive, kPendingDeletion);
    EXPECT_CALL(post_factory(), WebFrameBecameUnavailable);
    EXPECT_CALL(checkpoint, Call("Finish"));
  }

  checkpoint.Call("Available");
  AddFrame(std::move(owned_frame));
  checkpoint.Call("DriverForFrame");
  factory().DriverForFrame(frame);
  checkpoint.Call("Unavailable");
  RemoveFrame(frame->GetFrameId());
  checkpoint.Call("Finish");
}

// Tests that DriverForFrame() creates a driver when called *before* the factory
// has been notified about WebFrameBecameAvailable().
TEST_F(AutofillDriverIOSFactoryTest, DriverForFrameBeforeAvailable) {
  std::unique_ptr<web::WebFrame> owned_frame = CreateMainFrame();
  web::WebFrame* frame = owned_frame.get();
  AutofillDriverIOS* driver = nullptr;

  MockFunction<void(std::string_view)> checkpoint;
  {
    InSequence sequence;
    EXPECT_CALL(checkpoint, Call("Available"));
    EXPECT_CALL(pre_factory(), WebFrameBecameAvailable).WillOnce([&] {
      factory().DriverForFrame(frame);
    });
    EXPECT_DRIVER_CREATED(&driver);
    EXPECT_LIFECYCLE_CHANGE(LazyRef(driver), kInactive, kActive);
    EXPECT_CALL(post_factory(), WebFrameBecameAvailable);
    EXPECT_CALL(checkpoint, Call("Unavailable"));
    EXPECT_CALL(pre_factory(), WebFrameBecameUnavailable);
    EXPECT_LIFECYCLE_CHANGE(LazyRef(driver), kActive, kPendingDeletion);
    EXPECT_CALL(post_factory(), WebFrameBecameUnavailable);
    EXPECT_CALL(checkpoint, Call("Finish"));
  }

  checkpoint.Call("Available");
  AddFrame(std::move(owned_frame));
  checkpoint.Call("Unavailable");
  RemoveFrame(frame->GetFrameId());
  checkpoint.Call("Finish");
}

// Tests that DriverForFrame() becomes nullptr *after* the factory has been
// notified about WebFrameBecameUnavailable().
TEST_F(AutofillDriverIOSFactoryTest, DriverForFrameAfterUnavailable) {
  std::unique_ptr<web::WebFrame> owned_frame = CreateMainFrame();
  web::WebFrame* frame = owned_frame.get();
  AutofillDriverIOS* driver = nullptr;

  MockFunction<void(std::string_view)> checkpoint;
  {
    InSequence sequence;
    EXPECT_CALL(checkpoint, Call("Available"));
    EXPECT_CALL(pre_factory(), WebFrameBecameAvailable);
    EXPECT_CALL(post_factory(), WebFrameBecameAvailable);
    EXPECT_CALL(checkpoint, Call("DriverForFrame"));
    EXPECT_DRIVER_CREATED(&driver);
    EXPECT_LIFECYCLE_CHANGE(LazyRef(driver), kInactive, kActive);
    EXPECT_CALL(checkpoint, Call("Unavailable"));
    EXPECT_CALL(pre_factory(), WebFrameBecameUnavailable).WillOnce([&] {
      EXPECT_EQ(factory().DriverForFrame(frame), driver);
    });
    EXPECT_LIFECYCLE_CHANGE(LazyRef(driver), kActive, kPendingDeletion);
    EXPECT_CALL(post_factory(), WebFrameBecameUnavailable).WillOnce([&] {
      EXPECT_EQ(factory().DriverForFrame(frame), nullptr);
    });
    EXPECT_CALL(checkpoint, Call("Finish"));
  }

  checkpoint.Call("Available");
  AddFrame(std::move(owned_frame));
  checkpoint.Call("DriverForFrame");
  factory().DriverForFrame(frame);
  checkpoint.Call("Unavailable");
  RemoveFrame(frame->GetFrameId());
  checkpoint.Call("Finish");
}

// Tests that DriverForFrame() returns nullptr when called *after* the factory
// has been notified about WebFrameBecameUnavailable() (i.e., when this is the
// only call).
TEST_F(AutofillDriverIOSFactoryTest, DriverForFrameAfterUnavailable2) {
  std::unique_ptr<web::WebFrame> owned_frame = CreateMainFrame();
  web::WebFrame* frame = owned_frame.get();

  MockFunction<void(std::string_view)> checkpoint;
  {
    InSequence sequence;
    EXPECT_CALL(checkpoint, Call("Available"));
    EXPECT_CALL(pre_factory(), WebFrameBecameAvailable);
    EXPECT_CALL(post_factory(), WebFrameBecameAvailable);
    EXPECT_CALL(checkpoint, Call("Unavailable"));
    EXPECT_CALL(pre_factory(), WebFrameBecameUnavailable);
    EXPECT_CALL(post_factory(), WebFrameBecameUnavailable).WillOnce([&] {
      EXPECT_EQ(factory().DriverForFrame(frame), nullptr);
    });
    EXPECT_CALL(checkpoint, Call("Finish"));
  }

  checkpoint.Call("Available");
  AddFrame(std::move(owned_frame));
  checkpoint.Call("Unavailable");
  RemoveFrame(frame->GetFrameId());
  checkpoint.Call("Finish");
}

}  // namespace
}  // namespace autofill