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

#include <optional>

#include "base/base64.h"
#include "base/memory/raw_ptr.h"
#include "base/test/gmock_expected_support.h"
#include "base/values.h"
#include "chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.h"
#include "chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.h"
#include "components/permissions/permission_request_manager.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_devtools_protocol_client.h"
#include "content/public/test/test_navigation_observer.h"
#include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/blink/public/common/features.h"

namespace web_app {

namespace {

// Verifies that the command result does not contain an "error" field.
testing::Matcher<const base::DictValue*> IsSuccess() {
  return testing::Pointee(testing::ResultOf(
      "FindDict('error')",
      [](const base::DictValue& dict) { return dict.FindDict("error"); },
      testing::IsNull()));
}

// A simplified struct to make test assertions readable.
// Note: This does not cover all Mojo reader state flags,
// only the specific flags relevant to these tests.
struct TestReaderStateOut {
  std::string name;
  int event_count = 0;
  std::vector<uint8_t> atr;
  // Selected flags
  bool changed = false;
  bool present = false;
  bool empty = false;
};

}  // namespace

class SmartCardEmulationBrowserTest : public IsolatedWebAppBrowserTestHarness {
 public:
  SmartCardEmulationBrowserTest() {
    feature_list_.InitAndEnableFeature(blink::features::kSmartCard);
  }
  ~SmartCardEmulationBrowserTest() override = default;

  void SetUpOnMainThread() override {
    IsolatedWebAppBrowserTestHarness::SetUpOnMainThread();
    InstallAndLaunchIWA();
    AttachDevTools();
    SetAutoAcceptPermissions();
  }

  void TearDownOnMainThread() override {
    if (devtools_client_) {
      devtools_client_->DetachProtocolClient();
      devtools_client_.reset();
    }
    web_contents_ = nullptr;
    IsolatedWebAppBrowserTestHarness::TearDownOnMainThread();
  }

  const base::DictValue* SendCommand(const std::string& method,
                                     base::DictValue params = {}) {
    return devtools_client_->SendCommandSync(method, std::move(params));
  }

  base::DictValue WaitForNotificationParams(const std::string& event_name) {
    return devtools_client_->WaitForNotification(event_name, true);
  }

  void SendResponse(const std::string& response_method,
                    const base::DictValue& event_params,
                    base::DictValue result_fields = {}) {
    const std::string* req_id = event_params.FindString("requestId");
    ASSERT_TRUE(req_id) << "Event missing 'requestId'";

    result_fields.Set("requestId", *req_id);
    ASSERT_THAT(SendCommand(response_method, std::move(result_fields)),
                IsSuccess());
  }

  content::TestDevToolsProtocolClient* client() {
    return devtools_client_.get();
  }

  content::RenderFrameHost* app_frame() {
    return web_contents_->GetPrimaryMainFrame();
  }

  content::WebContents* web_contents() { return web_contents_; }

  // Helper: Waits for establishContext and reports success.
  void HandleEstablishContext(
      int context_id = 123,
      std::optional<std::string> error_code = std::nullopt) {
    auto params = WaitForNotificationParams(
        "SmartCardEmulation.establishContextRequested");
    if (error_code.has_value()) {
      SendResponse("SmartCardEmulation.reportError", params,
                   base::DictValue().Set("resultCode", *error_code));
      return;
    }
    SendResponse("SmartCardEmulation.reportEstablishContextResult", params,
                 base::DictValue().Set("contextId", context_id));
  }

  // Helper: Waits for listReaders and reports either success or error.
  void HandleListReaders(const std::vector<std::string>& readers = {"Reader A"},
                         int context_id = 123,
                         std::optional<std::string> error_code = std::nullopt) {
    auto params =
        WaitForNotificationParams("SmartCardEmulation.listReadersRequested");
    EXPECT_EQ(params.FindInt("contextId"), context_id);

    if (error_code.has_value()) {
      SendResponse("SmartCardEmulation.reportError", params,
                   base::DictValue().Set("resultCode", *error_code));
      return;
    }
    base::ListValue readers_list;
    for (const auto& reader : readers) {
      readers_list.Append(reader);
    }
    SendResponse("SmartCardEmulation.reportListReadersResult", params,
                 base::DictValue().Set("readers", std::move(readers_list)));
  }

  // Helper: Waits for connect and reports either success or error.
  void HandleConnect(const std::string& expected_reader = "Reader A",
                     const int handle = 123,
                     std::optional<std::string> active_protocol = std::nullopt,
                     std::optional<std::string> error_code = std::nullopt) {
    auto params =
        WaitForNotificationParams("SmartCardEmulation.connectRequested");

    // Verify the request is for the correct reader.
    const std::string* reader = params.FindString("reader");
    ASSERT_THAT(reader, testing::Pointee(testing::StrEq(expected_reader)))
        << "Connect request was for the wrong reader!";

    if (error_code.has_value()) {
      SendResponse("SmartCardEmulation.reportError", params,
                   base::DictValue().Set("resultCode", *error_code));
      return;
    }
    base::DictValue response;
    response.Set("handle", handle);
    if (active_protocol.has_value()) {
      response.Set("activeProtocol", *active_protocol);
    }
    SendResponse("SmartCardEmulation.reportConnectResult", params,
                 std::move(response));
  }

  // Helper: Waits for getStatusChangeRequested and sends a simulated response.
  void HandleGetStatusChange(
      const std::vector<TestReaderStateOut>& states,
      int context_id = 123,
      std::optional<std::string> error_code = std::nullopt) {
    auto params = WaitForNotificationParams(
        "SmartCardEmulation.getStatusChangeRequested");

    EXPECT_EQ(params.FindInt("contextId"), context_id);

    if (error_code.has_value()) {
      SendResponse("SmartCardEmulation.reportError", params,
                   base::DictValue().Set("resultCode", *error_code));
      return;
    }
    base::ListValue response_list;
    for (const auto& state : states) {
      base::DictValue dict;

      dict.Set("reader", state.name);
      dict.Set("eventCount", state.event_count);
      dict.Set("atr", base::Base64Encode(state.atr));

      base::DictValue flags;
      flags.Set("changed", state.changed);
      flags.Set("present", state.present);
      flags.Set("empty", state.empty);
      dict.Set("eventState", std::move(flags));

      response_list.Append(std::move(dict));
    }

    SendResponse(
        "SmartCardEmulation.reportGetStatusChangeResult", params,
        base::DictValue().Set("readerStates", std::move(response_list)));
  }

  // Helper: Waits for statusRequested and sends a response.
  void HandleStatus(const std::string& reader_name,
                    const std::string& state,
                    const std::string& protocol,
                    const std::vector<uint8_t>& atr,
                    int handle = 123,
                    std::optional<std::string> error_code = std::nullopt) {
    auto params =
        WaitForNotificationParams("SmartCardEmulation.statusRequested");

    EXPECT_EQ(params.FindInt("handle"), handle);

    if (error_code.has_value()) {
      SendResponse("SmartCardEmulation.reportError", params,
                   base::DictValue().Set("resultCode", *error_code));
      return;
    }
    base::DictValue response;
    response.Set("readerName", reader_name);
    response.Set("state", state);
    response.Set("atr", base::Base64Encode(atr));
    if (!protocol.empty()) {
      response.Set("protocol", protocol);
    }
    SendResponse("SmartCardEmulation.reportStatusResult", params,
                 std::move(response));
  }

  // Helper: Waits for transmitRequested and sends a response.
  void HandleTransmit(
      const std::vector<uint8_t>& expected_command,
      const base::expected<std::vector<uint8_t>, std::string>& result,
      int handle = 123) {
    auto params =
        WaitForNotificationParams("SmartCardEmulation.transmitRequested");

    EXPECT_EQ(params.FindInt("handle"), handle);

    const std::string* command_b64 = params.FindString("data");
    ASSERT_TRUE(command_b64) << "Transmit event missing 'data' field";

    std::string command_str;
    base::Base64Decode(*command_b64, &command_str);
    std::vector<uint8_t> actual_command(command_str.begin(), command_str.end());
    EXPECT_EQ(actual_command, expected_command)
        << "JS sent wrong command bytes";

    if (result.has_value()) {
      SendResponse(
          "SmartCardEmulation.reportDataResult", params,
          base::DictValue().Set("data", base::Base64Encode(result.value())));
    } else {
      SendResponse("SmartCardEmulation.reportError", params,
                   base::DictValue().Set("resultCode", result.error()));
    }
  }

  // Helper: Waits for controlRequested and sends a response.
  void HandleControl(
      int expected_control_code,
      const std::vector<uint8_t>& expected_data,
      const base::expected<std::vector<uint8_t>, std::string>& result,
      int handle = 123) {
    auto params =
        WaitForNotificationParams("SmartCardEmulation.controlRequested");

    EXPECT_EQ(params.FindInt("handle"), handle);
    EXPECT_EQ(params.FindInt("controlCode"), expected_control_code);

    const std::string* data_b64 = params.FindString("data");
    ASSERT_TRUE(data_b64) << "Control event missing 'data' field";

    std::string data_str;
    base::Base64Decode(*data_b64, &data_str);
    std::vector<uint8_t> actual_data(data_str.begin(), data_str.end());
    EXPECT_EQ(actual_data, expected_data) << "JS sent wrong control data bytes";

    if (result.has_value()) {
      SendResponse(
          "SmartCardEmulation.reportDataResult", params,
          base::DictValue().Set("data", base::Base64Encode(result.value())));
    } else {
      SendResponse("SmartCardEmulation.reportError", params,
                   base::DictValue().Set("resultCode", result.error()));
    }
  }

  // Helper: Waits for getAttributeRequested and sends a response.
  void HandleGetAttribute(
      int attrib_id,
      const base::expected<std::vector<uint8_t>, std::string>& result,
      int handle = 123) {
    auto params =
        WaitForNotificationParams("SmartCardEmulation.getAttribRequested");

    EXPECT_EQ(params.FindInt("handle"), handle);
    EXPECT_EQ(params.FindInt("attribId"), attrib_id);

    if (result.has_value()) {
      SendResponse(
          "SmartCardEmulation.reportDataResult", params,
          base::DictValue().Set("data", base::Base64Encode(result.value())));
    } else {
      SendResponse("SmartCardEmulation.reportError", params,
                   base::DictValue().Set("resultCode", result.error()));
    }
  }

  // Helper: Waits for disconnectRequested and sends a response.
  void HandleDisconnect(const std::string& expected_disposition = "leave-card",
                        int handle = 123,
                        std::optional<std::string> error_code = std::nullopt) {
    auto params =
        WaitForNotificationParams("SmartCardEmulation.disconnectRequested");

    EXPECT_EQ(params.FindInt("handle"), handle);
    EXPECT_EQ(*params.FindString("disposition"), expected_disposition);

    if (error_code.has_value()) {
      SendResponse("SmartCardEmulation.reportError", params,
                   base::DictValue().Set("resultCode", *error_code));
    } else {
      SendResponse("SmartCardEmulation.reportPlainResult", params, {});
    }
  }

  // Helper: Waits for beginTransactionRequested and sends a response.
  void HandleBeginTransaction(
      int handle = 123,
      std::optional<std::string> error_code = std::nullopt) {
    auto params = WaitForNotificationParams(
        "SmartCardEmulation.beginTransactionRequested");
    EXPECT_EQ(params.FindInt("handle"), handle);

    if (error_code.has_value()) {
      SendResponse("SmartCardEmulation.reportError", params,
                   base::DictValue().Set("resultCode", *error_code));
    } else {
      SendResponse("SmartCardEmulation.reportBeginTransactionResult", params,
                   base::DictValue().Set("handle", handle));
    }
  }

  // Helper: Waits for endTransactionRequested and sends a response.
  void HandleEndTransaction(
      const std::string& expected_disposition = "leave-card",
      int handle = 123,
      std::optional<std::string> error_code = std::nullopt) {
    auto params =
        WaitForNotificationParams("SmartCardEmulation.endTransactionRequested");

    EXPECT_EQ(params.FindInt("handle"), handle);
    EXPECT_EQ(*params.FindString("disposition"), expected_disposition);

    if (error_code.has_value()) {
      SendResponse("SmartCardEmulation.reportError", params,
                   base::DictValue().Set("resultCode", *error_code));
    } else {
      SendResponse("SmartCardEmulation.reportPlainResult", params, {});
    }
  }

  // Helper: Waits for cancelRequested and sends a response.
  void HandleCancel(int context_id = 123,
                    std::optional<std::string> error_code = std::nullopt) {
    auto params =
        WaitForNotificationParams("SmartCardEmulation.cancelRequested");
    EXPECT_EQ(params.FindInt("contextId"), context_id);

    if (error_code.has_value()) {
      SendResponse("SmartCardEmulation.reportError", params,
                   base::DictValue().Set("resultCode", *error_code));
    } else {
      SendResponse("SmartCardEmulation.reportPlainResult", params, {});
    }
  }

  // Helper: Waits for setAttribRequested (SCardSetAttrib)
  void HandleSetAttribute(
      int attrib_id,
      const std::vector<uint8_t>& expected_data,
      int handle = 123,
      std::optional<std::string> error_code = std::nullopt) {
    auto params =
        WaitForNotificationParams("SmartCardEmulation.setAttribRequested");
    EXPECT_EQ(params.FindInt("handle"), handle);
    EXPECT_EQ(params.FindInt("attribId"), attrib_id);

    const std::string* data_b64 = params.FindString("data");
    ASSERT_TRUE(data_b64) << "Event is missing the 'data' field";

    std::string data_str;
    base::Base64Decode(*data_b64, &data_str);
    std::vector<uint8_t> actual_data(data_str.begin(), data_str.end());
    EXPECT_EQ(actual_data, expected_data);

    if (error_code.has_value()) {
      SendResponse("SmartCardEmulation.reportError", params,
                   base::DictValue().Set("resultCode", *error_code));
    } else {
      SendResponse("SmartCardEmulation.reportPlainResult", params, {});
    }
  }

  void HandleReleaseContext(
      int context_id = 123,
      std::optional<std::string> error_code = std::nullopt) {
    auto params =
        WaitForNotificationParams("SmartCardEmulation.releaseContextRequested");

    EXPECT_EQ(params.FindInt("contextId"), context_id);

    if (error_code.has_value()) {
      SendResponse("SmartCardEmulation.reportError", params,
                   base::DictValue().Set("resultCode", *error_code));
    } else {
      SendResponse("SmartCardEmulation.reportReleaseContextResult", params, {});
    }
  }

  void ReloadPage() {
    content::TestNavigationObserver observer(web_contents_);
    web_contents_->GetController().Reload(content::ReloadType::NORMAL, false);
    observer.Wait();
  }

  void NavigateToSubPage() {
    GURL current_url = web_contents_->GetLastCommittedURL();
    GURL new_url = current_url.Resolve("/target.html");
    ASSERT_TRUE(content::NavigateToURL(web_contents_, new_url));
  }

 private:
  void InstallAndLaunchIWA() {
    auto app =
        IsolatedWebAppBuilder(
            ManifestBuilder().AddPermissionsPolicyWildcard(
                network::mojom::PermissionsPolicyFeature::kSmartCard))
            // Add a valid destination for navigation.
            .AddHtml("/target.html", "<html><body>Target Page</body></html>")
            .BuildBundle();

    app->TrustSigningKey();
    auto install_result = app->Install(profile());
    ASSERT_TRUE(install_result.has_value());

    auto url_info = install_result.value();
    raw_ptr<content::RenderFrameHost> app_frame = OpenApp(url_info.app_id());
    ASSERT_TRUE(app_frame);
    web_contents_ = content::WebContents::FromRenderFrameHost(app_frame);
    ASSERT_TRUE(web_contents_);
  }

  void AttachDevTools() {
    devtools_client_ = std::make_unique<content::TestDevToolsProtocolClient>();
    devtools_client_->AttachToWebContents(web_contents_);
  }

  void SetAutoAcceptPermissions() {
    permissions::PermissionRequestManager::FromWebContents(
        content::WebContents::FromRenderFrameHost(app_frame()))
        ->set_auto_response_for_test(
            permissions::PermissionRequestManager::ACCEPT_ALL);
  }

  base::test::ScopedFeatureList feature_list_;
  std::unique_ptr<content::TestDevToolsProtocolClient> devtools_client_;
  raw_ptr<content::WebContents> web_contents_ = nullptr;
};

IN_PROC_BROWSER_TEST_F(SmartCardEmulationBrowserTest, EnableDisableEmulation) {
  // Disable (Should be successful even if already disabled - Idempotency).
  EXPECT_THAT(SendCommand("SmartCardEmulation.disable"), IsSuccess());

  // Enable (Should activate the override).
  EXPECT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());

  // Re-Enable (Should be successful - Idempotency).
  EXPECT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());

  // Disable (Should clean up).
  EXPECT_THAT(SendCommand("SmartCardEmulation.disable"), IsSuccess());
}

IN_PROC_BROWSER_TEST_F(SmartCardEmulationBrowserTest,
                       ListReadersAndReportSuccess) {
  ASSERT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());

  content::DOMMessageQueue message_queue(app_frame());
  const std::string kScript = R"(
    (async () => {
      try {
        const context = await navigator.smartCard.establishContext();
        const readers = await context.listReaders();

        if (readers.length === 2 &&
            readers.includes("Reader A") &&
            readers.includes("Reader B")) {
           window.domAutomationController.send("Success");
        } else {
           window.domAutomationController.send(
               "Failure: Unexpected list: " + JSON.stringify(readers));
        }
      } catch (e) {
        window.domAutomationController.send(
          "Error: " + e.name + " - " + e.message);
      }
    })();
  )";
  content::ExecuteScriptAsync(app_frame(), kScript);

  HandleEstablishContext();
  HandleListReaders({"Reader A", "Reader B"});

  std::string message;
  ASSERT_TRUE(message_queue.WaitForMessage(&message));
  EXPECT_EQ("\"Success\"", message);
}

IN_PROC_BROWSER_TEST_F(SmartCardEmulationBrowserTest,
                       ListReadersAndReportError) {
  ASSERT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());

  content::DOMMessageQueue message_queue(app_frame());
  const std::string kScript = R"(
    (async () => {
      try {
        const context = await navigator.smartCard.establishContext();

        // This is expected to fail
        await context.listReaders();

        window.domAutomationController.send(
          "Failure: Should have thrown error");
      } catch (e) {
        window.domAutomationController.send("Error: " + e.name);
      }
    })();
  )";
  content::ExecuteScriptAsync(app_frame(), kScript);

  HandleEstablishContext();
  HandleListReaders({}, 123, "no-service");

  std::string message;
  ASSERT_TRUE(message_queue.WaitForMessage(&message));
  EXPECT_EQ("\"Error: SmartCardError\"", message);
}

IN_PROC_BROWSER_TEST_F(SmartCardEmulationBrowserTest,
                       ListReadersUnknownCardError) {
  ASSERT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());

  content::DOMMessageQueue message_queue(app_frame());
  const std::string kScript = R"(
    (async () => {
      try {
        const context = await navigator.smartCard.establishContext();
        await context.listReaders();
        window.domAutomationController.send(
          "Failure: Should have thrown error");
      } catch (e) {
        window.domAutomationController.send("Error: " +
            e.name + " - " + e.message);
      }
    })();
  )";
  content::ExecuteScriptAsync(app_frame(), kScript);

  HandleEstablishContext();
  HandleListReaders({}, 123, "unknown-card");

  std::string message;
  ASSERT_TRUE(message_queue.WaitForMessage(&message));
  EXPECT_THAT(message, testing::HasSubstr(
                           "The specified smart card name is not recognized."));
}

IN_PROC_BROWSER_TEST_F(SmartCardEmulationBrowserTest, ConnectAndReportSuccess) {
  ASSERT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());

  content::DOMMessageQueue message_queue(app_frame());
  const std::string kScript = R"(
    (async () => {
      try {
        const context = await navigator.smartCard.establishContext();
        const readers = await context.listReaders();
        const readerName = readers[0];
        const result = await context.connect(readerName, "shared",
            {preferredProtocols: ["t1"]});
        window.domAutomationController.send("Success");
      } catch (e) {
        window.domAutomationController.send(
          "Error: " + e.name + " - " + e.message);
      }
    })();
  )";
  content::ExecuteScriptAsync(app_frame(), kScript);

  HandleEstablishContext();
  HandleListReaders();
  HandleConnect();

  std::string message;
  ASSERT_TRUE(message_queue.WaitForMessage(&message));
  EXPECT_EQ("\"Success\"", message);
}

IN_PROC_BROWSER_TEST_F(SmartCardEmulationBrowserTest, ConnectAndReportError) {
  ASSERT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());

  content::DOMMessageQueue message_queue(app_frame());
  const std::string kScript = R"(
    (async () => {
      try {
        const context = await navigator.smartCard.establishContext();
        const readers = await context.listReaders();
        const readerName = readers[0];

        // This connect attempt is expected to fail.
        await context.connect(readerName, "shared",
            {preferredProtocols: ["t1"]});

        if (result.activeProtocol === "t1") {
           window.domAutomationController.send("Success");
        } else {
           window.domAutomationController.send("Failure: Wrong protocol");
        }

        window.domAutomationController.send(
          "Failure: Should have thrown error");
      } catch (e) {
        window.domAutomationController.send("Error: " + e.name);
      }
    })();
  )";
  content::ExecuteScriptAsync(app_frame(), kScript);

  HandleEstablishContext();
  HandleListReaders();
  HandleConnect("Reader A", 123, "t1", "reader-unavailable");

  std::string message;
  ASSERT_TRUE(message_queue.WaitForMessage(&message));
  EXPECT_EQ("\"Error: SmartCardError\"", message);
}

IN_PROC_BROWSER_TEST_F(SmartCardEmulationBrowserTest,
                       EmulationPersistsAcrossReload) {
  EXPECT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());
  content::DOMMessageQueue message_queue(web_contents());
  ReloadPage();

  const std::string kScript = R"(
    (async () => {
      try {
        const context = await navigator.smartCard.establishContext();
        window.domAutomationController.send("Success");
      } catch (e) {
        window.domAutomationController.send(
          "Error: " + e.name + " - " + e.message);
      }
    })();
  )";
  content::ExecuteScriptAsync(app_frame(), kScript);

  HandleEstablishContext();

  std::string message;
  ASSERT_TRUE(message_queue.WaitForMessage(&message));
  EXPECT_EQ("\"Success\"", message);
}

IN_PROC_BROWSER_TEST_F(SmartCardEmulationBrowserTest,
                       EmulationPersistsAcrossNavigation) {
  EXPECT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());
  content::DOMMessageQueue message_queue(web_contents());

  NavigateToSubPage();

  // Execute JS on the new page.
  const std::string kScript = R"(
    (async () => {
      try {
        const context = await navigator.smartCard.establishContext();
        window.domAutomationController.send("Success");
      } catch (e) {
        window.domAutomationController.send("Error: " + e.name);
      }
    })();
  )";
  content::ExecuteScriptAsync(app_frame(), kScript);

  HandleEstablishContext();

  std::string message;
  ASSERT_TRUE(message_queue.WaitForMessage(&message));
  EXPECT_EQ("\"Success\"", message);
}

IN_PROC_BROWSER_TEST_F(SmartCardEmulationBrowserTest,
                       IframeCanAccessEmulatedReaders) {
  ASSERT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());
  content::DOMMessageQueue message_queue(app_frame());

  // Inject Iframe
  const std::string kCreateIframeScript = R"(
      const iframe = document.createElement('iframe');
      iframe.src = 'about:blank';
      iframe.allow = 'smart-card';
      document.body.appendChild(iframe);
  )";
  EXPECT_TRUE(content::ExecJs(app_frame(), kCreateIframeScript));

  content::RenderFrameHost* iframe_host = content::ChildFrameAt(app_frame(), 0);
  ASSERT_TRUE(iframe_host);

  const std::string kScript = R"(
    (async () => {
      try {
        const context = await navigator.smartCard.establishContext();
        window.domAutomationController.send("Success");
      } catch (e) {
        window.domAutomationController.send("Error: " + e.name);
      }
    })();
  )";
  content::ExecuteScriptAsync(app_frame(), kScript);

  HandleEstablishContext();

  std::string message;
  ASSERT_TRUE(message_queue.WaitForMessage(&message));
  EXPECT_EQ("\"Success\"", message);
}

IN_PROC_BROWSER_TEST_F(SmartCardEmulationBrowserTest, GetStatusChangePnP) {
  ASSERT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());

  content::DOMMessageQueue message_queue(app_frame());

  const std::string kScript = R"(
    (async () => {
      try {
        const context = await navigator.smartCard.establishContext();
        const oldReaders = await context.listReaders();

        let allReaders = await context.getStatusChange(
            [{readerName: "\\\\?PnP?\\Notification", currentState: {}}],
            {timeout: 10000})
            .then((statesOut) => {
                return context.listReaders();
            });

        let newReaders = allReaders.filter(x => !oldReaders.includes(x));
        if (newReaders.length === 1 && newReaders[0] === "New Reader") {
           window.domAutomationController.send("Success");
        } else {
           window.domAutomationController.send(
            "Failure: " + JSON.stringify(newReaders));
        }
      } catch (e) {
        window.domAutomationController.send(
          "Error: " + e.name + " - " + e.message);
      }
    })();
  )";

  content::ExecuteScriptAsync(app_frame(), kScript);

  HandleEstablishContext();
  HandleListReaders({});
  HandleGetStatusChange(
      {{.name = "\\\\?PnP?\\Notification", .event_count = 1, .changed = true}});
  HandleListReaders({"New Reader"});

  std::string message;
  ASSERT_TRUE(message_queue.WaitForMessage(&message));
  EXPECT_EQ("\"Success\"", message);
}

IN_PROC_BROWSER_TEST_F(SmartCardEmulationBrowserTest,
                       GetStatusChangeReturnsError) {
  ASSERT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());

  content::DOMMessageQueue message_queue(app_frame());
  const std::string kScript = R"(
    (async () => {
      try {
        const context = await navigator.smartCard.establishContext();
        const readers = await context.listReaders();

        await context.getStatusChange(
            [{readerName: "Simulated Reader", currentState: {}}],
            {timeout: 10000}
        );

        window.domAutomationController.send(
          "Failure: Promise resolved but should have rejected.");

      } catch (e) {
        if (e.name === "SmartCardError") {
           window.domAutomationController.send("Success");
        } else {
           window.domAutomationController.send(
            "Failure: Wrong error type - " + e.name + ": " + e.message);
        }
      }
    })();
  )";
  content::ExecuteScriptAsync(app_frame(), kScript);

  HandleEstablishContext();
  HandleListReaders({"Simulated Reader"});
  HandleGetStatusChange({}, 123, "no-service");

  std::string message;
  ASSERT_TRUE(message_queue.WaitForMessage(&message));
  EXPECT_EQ("\"Success\"", message);
}

IN_PROC_BROWSER_TEST_F(SmartCardEmulationBrowserTest,
                       GetStatusChangeMultiReader) {
  ASSERT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());

  content::DOMMessageQueue message_queue(app_frame());
  const std::string kScript = R"(
    (async () => {
      try {
        const context = await navigator.smartCard.establishContext();
        const readers = await context.listReaders();
        if (readers.length !== 2 || !readers.includes("Reader A") ||
          !readers.includes("Reader B")) {
            window.domAutomationController.send(
              "Failure: Readers missing. Got: " + readers);
            return;
        }

        const states = await context.getStatusChange([
            {readerName: "Reader A", currentState: {empty: true}},
            {readerName: "Reader B", currentState: {empty: true}}
        ], {timeout: 10000});

        const stateA = states.find(s => s.readerName === "Reader A");
        const stateB = states.find(s => s.readerName === "Reader B");

        if (!stateA || !stateB) {
            throw new Error("Missing state for one or more readers.");
        }

        if (!stateA.eventState.changed || !stateA.eventState.present) {
             throw new Error(
              "Reader A failed: Expected changed/present. Got: " +
                JSON.stringify(stateA));
        }

        if (stateB.eventState.changed || !stateB.eventState.empty) {
             throw new Error(
              "Reader B failed: Expected !changed/empty. Got: " +
                JSON.stringify(stateB));
        }
        window.domAutomationController.send("Success");
      } catch (e) {
        window.domAutomationController.send(
          "Error: " + e.name + " - " + e.message);
      }
    })();
  )";
  content::ExecuteScriptAsync(app_frame(), kScript);

  HandleEstablishContext();
  HandleListReaders({"Reader A", "Reader B"});
  HandleGetStatusChange({{.name = "Reader A",

                          .event_count = 1,
                          .atr = {0x3B, 0x11},
                          .changed = true,
                          .present = true},
                         {.name = "Reader B",
                          .event_count = 0,
                          .atr = {},
                          .changed = false,
                          .empty = true}});

  std::string message;
  ASSERT_TRUE(message_queue.WaitForMessage(&message));
  EXPECT_EQ("\"Success\"", message);
}

IN_PROC_BROWSER_TEST_F(SmartCardEmulationBrowserTest, ConnectionStatus) {
  ASSERT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());

  content::DOMMessageQueue message_queue(app_frame());
  const std::string kScript = R"(
    (async () => {
      try {
        const context = await navigator.smartCard.establishContext();
        const readers = await context.listReaders();
        const connectResult = await context.connect(readers[0], "shared",
            {preferredProtocols: ["t1"]});

        const status = await connectResult.connection.status();

        if (status.readerName !== "Reader A") {
            throw new Error("Wrong readerName: " + status.readerName);
        }

        if (status.state !== "t1") {
            throw new Error("Wrong state: " + status.state);
        }

        const atrBytes = new Uint8Array(status.answerToReset);
        if (atrBytes.length !== 4 || atrBytes[0] !== 0x3B) {
            throw new Error("Wrong ATR bytes: " + atrBytes);
        }

        window.domAutomationController.send("Success");
      } catch (e) {
        window.domAutomationController.send("Error: " +
          e.name + " - " + e.message);
      }
    })();
  )";
  content::ExecuteScriptAsync(app_frame(), kScript);

  HandleEstablishContext();
  HandleListReaders();
  HandleConnect();

  HandleStatus("Reader A", "specific", "t1", {0x3B, 0x01, 0x02, 0x03});

  std::string message;
  ASSERT_TRUE(message_queue.WaitForMessage(&message));
  EXPECT_EQ("\"Success\"", message);
}

IN_PROC_BROWSER_TEST_F(SmartCardEmulationBrowserTest, ConnectionStatusError) {
  ASSERT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());

  content::DOMMessageQueue message_queue(app_frame());
  const std::string kScript = R"(
    (async () => {
      try {
        const context = await navigator.smartCard.establishContext();
        const readers = await context.listReaders();
        const connectResult = await context.connect(readers[0], "shared",
            {preferredProtocols: ["t1"]});
            await connectResult.connection.status();

            window.domAutomationController.send(
              "Failure: Promise resolved but should have rejected.");
        } catch (e) {
            if (e.name === "SmartCardError") {
                 window.domAutomationController.send("Success");
            } else {
                 window.domAutomationController.send(
                  "Failure: Wrong error type. Got: " +
                    e.name + ", " + e.message);
            }
        }
    })();
  )";
  content::ExecuteScriptAsync(app_frame(), kScript);

  HandleEstablishContext();
  HandleListReaders();
  HandleConnect();

  HandleStatus("", "", "", {}, 123, "reader-unavailable");

  std::string message;
  ASSERT_TRUE(message_queue.WaitForMessage(&message));
  EXPECT_EQ("\"Success\"", message);
}

IN_PROC_BROWSER_TEST_F(SmartCardEmulationBrowserTest, DataOperationsSuccess) {
  ASSERT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());
  content::DOMMessageQueue message_queue(app_frame());

  const std::string kScript = R"(
    (async () => {
      try {
        const context = await navigator.smartCard.establishContext();
        const readers = await context.listReaders();
        const connectResult = await context.connect(readers[0],
          "shared", {preferredProtocols: ["t1"]});
        const connection = connectResult.connection;

        // --- Transmit ---
        const txResponse = await connection.transmit(
          new Uint8Array([0x00, 0xA4, 0x04, 0x00]));
        const txBytes = new Uint8Array(txResponse);
        if (txBytes.length !== 2 || txBytes[0] !== 0x90 ||
          txBytes[1] !== 0x00) {
            throw new Error("Transmit failed. Got: " + txBytes.join(','));
        }

        // --- Control ---
        const ctrlResponse = await connection.control(42,
          new Uint8Array([1, 2, 3]));
        const ctrlBytes = new Uint8Array(ctrlResponse);
        if (ctrlBytes.length !== 2 || ctrlBytes[0] !== 10 ||
          ctrlBytes[1] !== 20) {
             throw new Error("Control failed. Got: " + ctrlBytes.join(','));
        }

        // --- GetAttribute ---
        // Tag 0x00090101 (SCARD_ATTR_VENDOR_NAME)
        const attrResponse = await connection.getAttribute(0x00090101);
        const vendorName = new TextDecoder().decode(attrResponse);
        if (vendorName !== "Chrome Reader") {
             throw new Error("GetAttribute failed. Got: " + vendorName);
        }

        window.domAutomationController.send("Success");
      } catch (e) {
        window.domAutomationController.send("Error: " + e.message);
      }
    })();
  )";
  content::ExecuteScriptAsync(app_frame(), kScript);

  HandleEstablishContext();
  HandleListReaders();
  HandleConnect("Reader A", 123, "t1");

  HandleTransmit({0x00, 0xA4, 0x04, 0x00}, std::vector<uint8_t>{0x90, 0x00});
  HandleControl(42, {1, 2, 3}, std::vector<uint8_t>{10, 20});

  std::string kName = "Chrome Reader";
  HandleGetAttribute(0x00090101,
                     std::vector<uint8_t>(kName.begin(), kName.end()));

  std::string message;
  ASSERT_TRUE(message_queue.WaitForMessage(&message));
  EXPECT_EQ("\"Success\"", message);
}

IN_PROC_BROWSER_TEST_F(SmartCardEmulationBrowserTest, DataOperationsErrors) {
  ASSERT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());
  content::DOMMessageQueue message_queue(app_frame());

  const std::string kScript = R"(
    (async () => {
      try {
        const context = await navigator.smartCard.establishContext();
        const readers = await context.listReaders();
        const connectResult = await context.connect(
          readers[0], "shared", {preferredProtocols: ["t1"]});
        const connection = connectResult.connection;

        // --- Transmit Error ---
        try {
            await connection.transmit(new Uint8Array([0x00, 0x00]));
            throw new Error("Transmit should have failed but succeeded.");
        } catch (e) {
            if (e.name !== "SmartCardError") {
              throw new Error("Transmit wrong error: " + e.name);
            }
        }

        // --- Control Error ---
        try {
            await connection.control(42, new Uint8Array([1]));
            throw new Error("Control should have failed but succeeded.");
        } catch (e) {
            if (e.name !== "SmartCardError") {
              throw new Error("Control wrong error: " + e.name);
            }
        }

        // --- GetAttribute Error ---
        try {
            await connection.getAttribute(1234);
            throw new Error("GetAttribute should have failed but succeeded.");
        } catch (e) {
            if (e.name !== "SmartCardError") {
              throw new Error("GetAttrib wrong error: " + e.name);
            }
        }

        window.domAutomationController.send("Success");
      } catch (e) {
        window.domAutomationController.send("Failure: " + e.message);
      }
    })();
  )";
  content::ExecuteScriptAsync(app_frame(), kScript);

  HandleEstablishContext();
  HandleListReaders();
  HandleConnect("Reader A", 123, "t1");

  HandleTransmit({0x00, 0x00}, base::unexpected("sharing-violation"));
  HandleControl(42, {1}, base::unexpected("unsupported-feature"));
  HandleGetAttribute(1234, base::unexpected("reader-unavailable"));

  std::string message;
  ASSERT_TRUE(message_queue.WaitForMessage(&message));
  EXPECT_EQ("\"Success\"", message);
}

IN_PROC_BROWSER_TEST_F(SmartCardEmulationBrowserTest, TransactionSuccess) {
  ASSERT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());
  content::DOMMessageQueue message_queue(app_frame());

  const std::string kScript = R"(
    (async () => {
      try {
        const context = await navigator.smartCard.establishContext();
        const readers = await context.listReaders();
        const connectResult = await context.connect(readers[0], "shared",
            {preferredProtocols: ["t1"]});
        const connection = connectResult.connection;

        await connection.startTransaction(async function() {
            return "leave";
        });
        await connection.disconnect();
        window.domAutomationController.send("Success");
      } catch (e) {
        window.domAutomationController.send("Error: " + e.message);
      }
    })();
  )";
  content::ExecuteScriptAsync(app_frame(), kScript);

  HandleEstablishContext();
  HandleListReaders();
  HandleConnect("Reader A", 123, "t1");
  HandleBeginTransaction();
  HandleEndTransaction();
  HandleDisconnect();

  std::string message;
  ASSERT_TRUE(message_queue.WaitForMessage(&message));
  EXPECT_EQ("\"Success\"", message);
}

IN_PROC_BROWSER_TEST_F(SmartCardEmulationBrowserTest, BeginTransactionFailure) {
  ASSERT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());
  content::DOMMessageQueue message_queue(app_frame());

  const std::string kScript = R"(
    (async () => {
      try {
        const context = await navigator.smartCard.establishContext();
        const readers = await context.listReaders();
        const connectResult = await context.connect(readers[0], "shared",
            {preferredProtocols: ["t1"]});
        const connection = connectResult.connection;

        try {
            // Attempt to start a transaction - this should fail immediately.
            await connection.startTransaction(async function() {
                throw new Error(
                  "Transaction callback should not run on failure!");
            });
            throw new Error("Transaction succeeded but was expected to fail.");
        } catch (txError) {
            if (txError.name === "SmartCardError") {
                window.domAutomationController.send("Success");
            } else {
                throw new Error("Caught unexpected error type: " +
                  txError.name + " - " + txError.message);
            }
        }
      } catch (e) {
        window.domAutomationController.send("Error: " + e.message);
      }
    })();
  )";
  content::ExecuteScriptAsync(app_frame(), kScript);

  HandleEstablishContext();
  HandleListReaders();
  HandleConnect("Reader A", 123, "t1");

  HandleBeginTransaction(123, "sharing-violation");

  std::string message;
  ASSERT_TRUE(message_queue.WaitForMessage(&message));
  EXPECT_EQ("\"Success\"", message);
}

IN_PROC_BROWSER_TEST_F(SmartCardEmulationBrowserTest, EndTransactionFailure) {
  ASSERT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());
  content::DOMMessageQueue message_queue(app_frame());

  const std::string kScript = R"(
    (async () => {
      try {
        const context = await navigator.smartCard.establishContext();
        const readers = await context.listReaders();
        const connectResult = await context.connect(readers[0], "shared",
            {preferredProtocols: ["t1"]});
        const connection = connectResult.connection;

        try {
            await connection.startTransaction(async function() {
                return "leave";
            });
            throw new Error("Transaction succeeded but was expected to fail.");
        } catch (e) {
             if (e.message.includes("communications error")) {
                 window.domAutomationController.send("Success");
             } else {
                 throw new Error("Unexpected error: " +
                  e.name + " - " + e.message);
             }
        }
      } catch (e) {
        window.domAutomationController.send("Error: " + e.message);
      }
    })();
  )";
  content::ExecuteScriptAsync(app_frame(), kScript);

  HandleEstablishContext();
  HandleListReaders();
  HandleConnect("Reader A", 123, "t1");

  HandleBeginTransaction();
  HandleEndTransaction("leave-card", 123, "comm-error");

  std::string message;
  ASSERT_TRUE(message_queue.WaitForMessage(&message));
  EXPECT_EQ("\"Success\"", message);
}

IN_PROC_BROWSER_TEST_F(SmartCardEmulationBrowserTest, SetAttributeSuccess) {
  ASSERT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());
  content::DOMMessageQueue message_queue(app_frame());

  const std::string kScript = R"(
    (async () => {
      try {
        const context = await navigator.smartCard.establishContext();
        const readers = await context.listReaders();
        const connectResult = await context.connect(readers[0],
          "shared", {preferredProtocols: ["t1"]});
        const connection = connectResult.connection;
        await connection.setAttribute(1234, new Uint8Array([0x96, 0x00]));

        window.domAutomationController.send("Success");
      } catch (e) {
        window.domAutomationController.send("Error: " + e.message);
      }
    })();
  )";
  content::ExecuteScriptAsync(app_frame(), kScript);

  HandleEstablishContext();
  HandleListReaders();
  HandleConnect("Reader A", 123, "t1");

  HandleSetAttribute(1234, {0x96, 0x00});

  std::string message;
  ASSERT_TRUE(message_queue.WaitForMessage(&message));
  EXPECT_EQ("\"Success\"", message);
}

IN_PROC_BROWSER_TEST_F(SmartCardEmulationBrowserTest, SetAttributeFailure) {
  ASSERT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());
  content::DOMMessageQueue message_queue(app_frame());

  const std::string kScript = R"(
    (async () => {
      try {
        const context = await navigator.smartCard.establishContext();
        const readers = await context.listReaders();
        const connectResult = await context.connect(readers[0],
          "shared", {preferredProtocols: ["t1"]});
        const connection = connectResult.connection;

        try {
          await connection.setAttribute(1234, new Uint8Array([0x96, 0x00]));
          throw new Error("setAttribute succeeded but was expected to fail.");
        } catch (e) {
           if (e.name === "SmartCardError" ) {
             window.domAutomationController.send("Success");
           } else {
             throw new Error(
              "Caught unexpected error: " + e.name + " - " + e.message);
           }
        }
      } catch (e) {
        window.domAutomationController.send("Error: " + e.message);
      }
    })();
  )";
  content::ExecuteScriptAsync(app_frame(), kScript);

  HandleEstablishContext();
  HandleListReaders();
  HandleConnect("Reader A", 123, "t1");

  HandleSetAttribute(1234, {0x96, 0x00}, 123, "sharing-violation");

  std::string message;
  ASSERT_TRUE(message_queue.WaitForMessage(&message));
  EXPECT_EQ("\"Success\"", message);
}

IN_PROC_BROWSER_TEST_F(SmartCardEmulationBrowserTest, CancelGetStatusChange) {
  ASSERT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());
  content::DOMMessageQueue message_queue(app_frame());

  const std::string kScript = R"(
    (async () => {
      try {
        const context = await navigator.smartCard.establishContext();
        const controller = new AbortController();
        const signal = controller.signal;
        const statusPromise = context.getStatusChange(
            [{
               readerName: "Simulated Reader",
               currentState: { empty: true }
            }],
            {signal: signal}
        );
        controller.abort();

        try {
            await statusPromise;
            throw new Error(
              "GetStatusChange should have been aborted but succeeded.");
        } catch (e) {
            if (e.name === 'AbortError') {
                 window.domAutomationController.send("Success");
            } else {
                 throw e;
            }
        }
      } catch (e) {
        window.domAutomationController.send("Error: " + e.message);
      }
    })();
  )";
  content::ExecuteScriptAsync(app_frame(), kScript);

  HandleEstablishContext();

  // Intercept GetStatusChange but do not answer yet.
  auto status_params =
      WaitForNotificationParams("SmartCardEmulation.getStatusChangeRequested");

  HandleCancel();

  base::DictValue response;
  response.Set("resultCode", "cancelled");
  SendResponse("SmartCardEmulation.reportError", status_params,
               std::move(response));

  std::string message;
  ASSERT_TRUE(message_queue.WaitForMessage(&message));
  EXPECT_EQ("\"Success\"", message);
}

IN_PROC_BROWSER_TEST_F(SmartCardEmulationBrowserTest,
                       CancelFailureOperationContinues) {
  ASSERT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());
  content::DOMMessageQueue message_queue(app_frame());

  const std::string kScript = R"(
    (async () => {
      try {
        const context = await navigator.smartCard.establishContext();
        const controller = new AbortController();

        const statusPromise = context.getStatusChange(
            [{readerName: "Simulated Reader", currentState: {empty: true}}],
            {signal: controller.signal}
        );

        controller.abort();
        const result = await statusPromise;
        console.log(result);
        if (result.length === 1 && result[0].readerName ===  "Reader A") {
            window.domAutomationController.send("Success");
        } else {
            throw new Error("Operation finished but returned wrong data.");
        }
      } catch (e) {
        window.domAutomationController.send(
          "Error: " + e.name + " - " + e.message);
      }
    })();
  )";
  content::ExecuteScriptAsync(app_frame(), kScript);

  HandleEstablishContext();

  auto status_params =
      WaitForNotificationParams("SmartCardEmulation.getStatusChangeRequested");

  HandleCancel(123, "comm-error");

  base::DictValue response;
  base::DictValue state;
  base::ListValue response_list;

  state.Set("reader", "Reader A");
  state.Set("eventCount", 1);
  state.Set("atr", base::Base64Encode({0x3B, 0x11}));

  base::DictValue flags;
  flags.Set("changed", true);

  state.Set("eventState", std::move(flags));

  response_list.Append(std::move(state));

  response.Set("readerStates", std::move(response_list));
  SendResponse("SmartCardEmulation.reportGetStatusChangeResult", status_params,
               std::move(response));

  std::string message;
  ASSERT_TRUE(message_queue.WaitForMessage(&message));
  EXPECT_EQ("\"Success\"", message);
}

IN_PROC_BROWSER_TEST_F(SmartCardEmulationBrowserTest, ReleaseContextOnGC) {
  ASSERT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());
  ASSERT_THAT(SendCommand("HeapProfiler.enable"), IsSuccess());

  content::DOMMessageQueue message_queue(app_frame());

  const std::string kScript = R"(
    (async () => {
      try {
        const context = await navigator.smartCard.establishContext();
        window.domAutomationController.send("Success");
      } catch (e) {
        window.domAutomationController.send("Error: " + e.message);
      }
    })();
  )";
  content::ExecuteScriptAsync(app_frame(), kScript);

  HandleEstablishContext();

  std::string message;
  ASSERT_TRUE(message_queue.WaitForMessage(&message));
  EXPECT_EQ("\"Success\"", message);

  ASSERT_THAT(SendCommand("HeapProfiler.collectGarbage"), IsSuccess());
  HandleReleaseContext();
}

IN_PROC_BROWSER_TEST_F(SmartCardEmulationBrowserTest,
                       ReleaseContextOnNavigation) {
  ASSERT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());
  content::DOMMessageQueue message_queue(app_frame());

  const std::string kSetupScript = R"(
    (async () => {
      window.smartCardContext = await navigator.smartCard.establishContext();
      window.domAutomationController.send("Success");
    })();
  )";
  content::ExecuteScriptAsync(app_frame(), kSetupScript);

  HandleEstablishContext(123);

  std::string message;
  ASSERT_TRUE(message_queue.WaitForMessage(&message));
  EXPECT_EQ("\"Success\"", message);

  content::ExecuteScriptAsync(app_frame(), "window.location.reload();");
  HandleReleaseContext();
}

IN_PROC_BROWSER_TEST_F(SmartCardEmulationBrowserTest, ReleaseContextFailure) {
  ASSERT_THAT(SendCommand("SmartCardEmulation.enable"), IsSuccess());
  content::DOMMessageQueue message_queue(app_frame());

  const std::string kSetupScript = R"(
    (async () => {
      window.smartCardContext = await navigator.smartCard.establishContext();
      window.domAutomationController.send("Success");
    })();
  )";
  content::ExecuteScriptAsync(app_frame(), kSetupScript);

  HandleEstablishContext(123);

  std::string message;
  ASSERT_TRUE(message_queue.WaitForMessage(&message));
  EXPECT_EQ("\"Success\"", message);

  content::ExecuteScriptAsync(app_frame(), "window.location.reload();");

  HandleReleaseContext(123, "internal-error");
}

}  // namespace web_app
