chromium/fuchsia_web/webengine/browser/popup_browsertest.cc

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

#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "content/public/test/browser_test.h"
#include "fuchsia_web/common/test/frame_for_test.h"
#include "fuchsia_web/common/test/frame_test_util.h"
#include "fuchsia_web/common/test/test_navigation_listener.h"
#include "fuchsia_web/webengine/browser/frame_impl_browser_test_base.h"

namespace {

constexpr char kPage1Path[] = "/title1.html";
constexpr char kPage2Path[] = "/title2.html";
constexpr char kPage1Title[] = "title 1";

constexpr char kPopupParentPath[] = "/popup_parent.html";
constexpr char kPopupRedirectPath[] = "/popup_child.html";
constexpr char kPopupMultiplePath[] = "/popup_multiple.html";

constexpr char kChildQueryParamName[] = "child_url";
constexpr char kPopupChildFile[] = "popup_child.html";
constexpr char kAutoplayFileAndQuery[] =
    "play_video.html?autoplay=1&codecs=vp8";
constexpr char kAutoPlayBlockedTitle[] = "blocked";
constexpr char kAutoPlaySuccessTitle[] = "playing";

class TestPopupListener : public fuchsia::web::PopupFrameCreationListener {
 public:
  TestPopupListener() = default;
  ~TestPopupListener() override = default;

  TestPopupListener(const TestPopupListener&) = delete;
  TestPopupListener& operator=(const TestPopupListener&) = delete;

  void GetAndAckNextPopup(fuchsia::web::FramePtr* frame,
                          fuchsia::web::PopupFrameCreationInfo* creation_info) {
    if (!frame_) {
      base::RunLoop run_loop;
      received_popup_callback_ = run_loop.QuitClosure();
      run_loop.Run();
    }

    *frame = frame_.Bind();
    *creation_info = std::move(creation_info_);

    popup_ack_callback_();
    popup_ack_callback_ = {};
  }

 private:
  void OnPopupFrameCreated(fidl::InterfaceHandle<fuchsia::web::Frame> frame,
                           fuchsia::web::PopupFrameCreationInfo creation_info,
                           OnPopupFrameCreatedCallback callback) override {
    creation_info_ = std::move(creation_info);
    frame_ = std::move(frame);

    popup_ack_callback_ = std::move(callback);

    if (received_popup_callback_)
      std::move(received_popup_callback_).Run();
  }

  fidl::InterfaceHandle<fuchsia::web::Frame> frame_;
  fuchsia::web::PopupFrameCreationInfo creation_info_;
  base::OnceClosure received_popup_callback_;
  OnPopupFrameCreatedCallback popup_ack_callback_;
};

class PopupTest : public FrameImplTestBaseWithServer {
 public:
  PopupTest()
      : popup_listener_binding_(&popup_listener_),
        popup_nav_listener_binding_(&popup_nav_listener_) {}

  ~PopupTest() override = default;
  PopupTest(const PopupTest&) = delete;
  PopupTest& operator=(const PopupTest&) = delete;

 protected:
  // Builds a URL for the kPopupParentPath page to pop up a Frame with
  // |child_file_and_query|. |child_file_and_query| may optionally include a
  // query string.
  GURL GetParentPageTestServerUrl(const char* child) const {
    const std::string url = base::StringPrintf("%s?%s=%s", kPopupParentPath,
                                               kChildQueryParamName, child);

    return embedded_test_server()->GetURL(url);
  }

  // Loads a page that autoplays video in a popup, populates the popup_*
  // members, and returns its URL.
  GURL LoadAutoPlayingPageInPopup(
      fuchsia::web::AutoplayPolicy autoplay_policy) {
    GURL popup_parent_url = GetParentPageTestServerUrl(kAutoplayFileAndQuery);
    GURL popup_child_url = embedded_test_server()->GetURL(
        base::StringPrintf("/%s", kAutoplayFileAndQuery));

    auto parent_frame = FrameForTest::Create(context(), {});

    // Set `autoplay_policy` for the parent frame. It should be inherited by the
    // popup.
    fuchsia::web::ContentAreaSettings settings;
    settings.set_autoplay_policy(autoplay_policy);
    parent_frame->SetContentAreaSettings(std::move(settings));

    parent_frame->SetPopupFrameCreationListener(
        popup_listener_binding_.NewBinding());

    EXPECT_TRUE(LoadUrlAndExpectResponse(parent_frame.GetNavigationController(),
                                         {}, popup_parent_url.spec()));

    fuchsia::web::PopupFrameCreationInfo popup_info;
    popup_listener_.GetAndAckNextPopup(&popup_frame_, &popup_info);
    EXPECT_EQ(popup_info.initial_url(), popup_child_url);

    popup_frame_->SetNavigationEventListener2(
        popup_nav_listener_binding_.NewBinding(), /*flags=*/{});

    return popup_child_url;
  }

  fuchsia::web::FramePtr popup_frame_;

  TestPopupListener popup_listener_;
  fidl::Binding<fuchsia::web::PopupFrameCreationListener>
      popup_listener_binding_;

  TestNavigationListener popup_nav_listener_;
  fidl::Binding<fuchsia::web::NavigationEventListener>
      popup_nav_listener_binding_;
};

IN_PROC_BROWSER_TEST_F(PopupTest, PopupWindowRedirect) {
  GURL popup_parent_url = GetParentPageTestServerUrl(kPopupChildFile);
  GURL popup_child_url(embedded_test_server()->GetURL(kPopupRedirectPath));
  GURL title1_url(embedded_test_server()->GetURL(kPage1Path));
  auto frame = FrameForTest::Create(context(), {});

  frame->SetPopupFrameCreationListener(popup_listener_binding_.NewBinding());

  EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(), {},
                                       popup_parent_url.spec()));

  // Verify the popup's initial URL, "popup_child.html".
  fuchsia::web::PopupFrameCreationInfo popup_info;
  popup_listener_.GetAndAckNextPopup(&popup_frame_, &popup_info);
  EXPECT_EQ(popup_info.initial_url(), popup_child_url);

  // Verify that the popup eventually redirects to "title1.html".
  popup_frame_->SetNavigationEventListener2(
      popup_nav_listener_binding_.NewBinding(), /*flags=*/{});
  popup_nav_listener_.RunUntilUrlAndTitleEquals(title1_url, kPage1Title);
}

IN_PROC_BROWSER_TEST_F(PopupTest, MultiplePopups) {
  GURL popup_parent_url(embedded_test_server()->GetURL(kPopupMultiplePath));
  GURL title1_url(embedded_test_server()->GetURL(kPage1Path));
  GURL title2_url(embedded_test_server()->GetURL(kPage2Path));
  auto frame = FrameForTest::Create(context(), {});

  frame->SetPopupFrameCreationListener(popup_listener_binding_.NewBinding());

  EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(), {},
                                       popup_parent_url.spec()));

  fuchsia::web::PopupFrameCreationInfo popup_info;
  popup_listener_.GetAndAckNextPopup(&popup_frame_, &popup_info);
  EXPECT_EQ(popup_info.initial_url(), title1_url);

  popup_listener_.GetAndAckNextPopup(&popup_frame_, &popup_info);
  EXPECT_EQ(popup_info.initial_url(), title2_url);
}

// Verifies that the child popup Frame has the same default CreateFrameParams as
// the parent Frame by verifying that autoplay is blocked in the child. This
// mostly verifies that AutoPlaySucceedsis actually modifies behavior.
IN_PROC_BROWSER_TEST_F(PopupTest,
                       PopupFrameHasSameContentAreaSettings_AutoplayBlocked) {
  // Load the page and wait for the popup Frame to be created.
  GURL popup_child_url = LoadAutoPlayingPageInPopup(
      fuchsia::web::AutoplayPolicy::REQUIRE_USER_ACTIVATION);

  // Verify that the child does not autoplay media.
  popup_nav_listener_.RunUntilUrlAndTitleEquals(popup_child_url,
                                                kAutoPlayBlockedTitle);
}

// Verifies that the child popup Frame has the same CreateFrameParams as the
// parent Frame by allowing autoplay in the parent's params and verifying that
// autoplay succeeds in the child.
IN_PROC_BROWSER_TEST_F(PopupTest,
                       PopupFrameHasSameContentAreaSettings_AutoplaySucceeds) {
  // Load the page and wait for the popup Frame to be created.
  GURL popup_child_url =
      LoadAutoPlayingPageInPopup(fuchsia::web::AutoplayPolicy::ALLOW);

  // Verify that the child autoplays media.
  popup_nav_listener_.RunUntilUrlAndTitleEquals(popup_child_url,
                                                kAutoPlaySuccessTitle);
}

}  // namespace