chromium/content/browser/web_contents/web_contents_view_aura_unittest.cc

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

#include "content/browser/web_contents/web_contents_view_aura.h"

#include <memory>
#include <optional>
#include <string>

#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_command_line.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/common/content_features.h"
#include "content/public/test/test_renderer_host.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/drag_drop_client.h"
#include "ui/aura/test/test_windows.h"
#include "ui/aura/test/window_test_api.h"
#include "ui/aura/window.h"
#include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/dragdrop/drop_target_event.h"
#include "ui/base/dragdrop/mojom/drag_drop_types.mojom.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/base/ozone_buildflags.h"
#include "ui/display/display_switches.h"
#include "ui/events/base_event_utils.h"
#include "ui/gfx/geometry/rect.h"
#include "url/origin.h"

#if BUILDFLAG(IS_WIN)
#include "ui/base/dragdrop/os_exchange_data_provider_win.h"
#endif

#if BUILDFLAG(IS_LINUX) && BUILDFLAG(IS_OZONE_X11)
#include "ui/base/x/selection_utils.h"
#include "ui/base/x/x11_os_exchange_data_provider.h"
#include "ui/gfx/x/atom_cache.h"
#include "ui/gfx/x/connection.h"
#include "ui/ozone/public/ozone_platform.h"
#endif  // BUILDFLAG(IS_LINUX) && BUILDFLAG(IS_OZONE_X11)

namespace content {
namespace {

DragOperation;

constexpr gfx::Rect kBounds =;
constexpr gfx::PointF kClientPt =;
constexpr gfx::PointF kScreenPt =;

// Runs a specified callback when a ui::MouseEvent is received.
class RunCallbackOnActivation : public WebContentsDelegate {};

class PrivilegedWebContentsDelegate : public WebContentsDelegate {};

class TestDragDropClient : public aura::client::DragDropClient {};

}  // namespace

class WebContentsViewAuraTest : public RenderViewHostTestHarness {};

TEST_F(WebContentsViewAuraTest, EnableDisableOverscroll) {}

TEST_F(WebContentsViewAuraTest, ShowHideParent) {}

TEST_F(WebContentsViewAuraTest, WebContentsDestroyedDuringClick) {}

TEST_F(WebContentsViewAuraTest, OccludeView) {}

// TODO(crbug.com/40190725): Enable these tests on Fuchsia when
// OSExchangeDataProviderFactory::CreateProvider is implemented.
#if BUILDFLAG(IS_FUCHSIA)
#define MAYBE_DragDropFiles
#define MAYBE_DragDropFilesOriginateFromRenderer
#define MAYBE_DragDropImageFromRenderer
#else
#define MAYBE_DragDropFiles
#define MAYBE_DragDropFilesOriginateFromRenderer
#define MAYBE_DragDropImageFromRenderer
#endif

TEST_F(WebContentsViewAuraTest, MAYBE_DragDropFiles) {}

TEST_F(WebContentsViewAuraTest, MAYBE_DragDropFilesOriginateFromRenderer) {}

TEST_F(WebContentsViewAuraTest, MAYBE_DragDropImageFromRenderer) {}

#if BUILDFLAG(IS_WIN)

TEST_F(WebContentsViewAuraTest, DragDropVirtualFiles) {
  WebContentsViewAura* view = GetView();
  auto data = std::make_unique<ui::OSExchangeData>();

  const std::u16string string_data = u"Some string data";
  data->SetString(string_data);

  const std::vector<std::pair<base::FilePath, std::string>>
      test_filenames_and_contents = {
          {base::FilePath(FILE_PATH_LITERAL("filename.txt")),
           std::string("just some data")},
          {base::FilePath(FILE_PATH_LITERAL("another filename.txt")),
           std::string("just some data\0with\0nulls", 25)},
          {base::FilePath(FILE_PATH_LITERAL("and another filename.txt")),
           std::string("just some more data")},
      };

  data->provider().SetVirtualFileContentsForTesting(test_filenames_and_contents,
                                                    TYMED_ISTREAM);

  ui::DropTargetEvent event(*data.get(), kClientPt, kScreenPt,
                            ui::DragDropTypes::DRAG_COPY);

  // Simulate drag enter.
  EXPECT_EQ(nullptr, view->current_drag_data_);
  view->OnDragEntered(event);
  ASSERT_NE(nullptr, view->current_drag_data_);

  EXPECT_EQ(string_data, view->current_drag_data_->text);

  const base::FilePath path_placeholder(FILE_PATH_LITERAL("temp.tmp"));
  std::vector<ui::FileInfo> retrieved_file_infos =
      view->current_drag_data_->filenames;
  ASSERT_EQ(test_filenames_and_contents.size(), retrieved_file_infos.size());
  for (size_t i = 0; i < retrieved_file_infos.size(); i++) {
    EXPECT_EQ(test_filenames_and_contents[i].first,
              retrieved_file_infos[i].display_name);
    EXPECT_EQ(path_placeholder, retrieved_file_infos[i].path);
  }

  // Simulate drop (completes asynchronously since virtual file data is
  // present).
  auto callback = base::BindOnce(&WebContentsViewAuraTest::OnDropComplete,
                                 base::Unretained(this));
  view->RegisterDropCallbackForTesting(std::move(callback));

  base::RunLoop run_loop;
  async_drop_closure_ = run_loop.QuitClosure();

  auto drop_cb = view->GetDropCallback(event);
  ASSERT_TRUE(drop_cb);
  ui::mojom::DragOperation output_drag_op = ui::mojom::DragOperation::kNone;
  std::move(drop_cb).Run(std::move(data), output_drag_op,
                         /*drag_image_layer_owner=*/nullptr);
  run_loop.Run();

  CheckDropData(view);

  EXPECT_EQ(string_data, drop_complete_data_->drop_data.text);

  std::string read_contents;
  base::FilePath temp_dir;
  EXPECT_TRUE(base::GetTempDir(&temp_dir));

  retrieved_file_infos = drop_complete_data_->drop_data.filenames;
  ASSERT_EQ(test_filenames_and_contents.size(), retrieved_file_infos.size());
  for (size_t i = 0; i < retrieved_file_infos.size(); i++) {
    EXPECT_EQ(test_filenames_and_contents[i].first,
              retrieved_file_infos[i].display_name);
    // Check if the temp files that back the virtual files are actually created
    // in the temp directory. Need to compare long file paths here because
    // GetTempDir can return a short ("8.3") path if the test is run
    // under a username that is too long.
    EXPECT_EQ(base::MakeLongFilePath(temp_dir),
              base::MakeLongFilePath(retrieved_file_infos[i].path.DirName()));
    EXPECT_EQ(test_filenames_and_contents[i].first.Extension(),
              retrieved_file_infos[i].path.Extension());
    EXPECT_TRUE(
        base::ReadFileToString(retrieved_file_infos[i].path, &read_contents));
    EXPECT_EQ(test_filenames_and_contents[i].second, read_contents);
  }
}

TEST_F(WebContentsViewAuraTest, DragDropVirtualFilesOriginateFromRenderer) {
  WebContentsViewAura* view = GetView();
  auto data = std::make_unique<ui::OSExchangeData>();

  const std::u16string string_data = u"Some string data";
  data->SetString(string_data);

  const std::vector<std::pair<base::FilePath, std::string>>
      test_filenames_and_contents = {
          {base::FilePath(FILE_PATH_LITERAL("filename.txt")),
           std::string("just some data")},
          {base::FilePath(FILE_PATH_LITERAL("another filename.txt")),
           std::string("just some data\0with\0nulls", 25)},
          {base::FilePath(FILE_PATH_LITERAL("and another filename.txt")),
           std::string("just some more data")},
      };

  data->provider().SetVirtualFileContentsForTesting(test_filenames_and_contents,
                                                    TYMED_ISTREAM);

  // Simulate the drag originating in the renderer process, in which case
  // any file data should be filtered out (anchor drag scenario).
  data->MarkRendererTaintedFromOrigin(url::Origin());

  ui::DropTargetEvent event(*data.get(), kClientPt, kScreenPt,
                            ui::DragDropTypes::DRAG_COPY);

  // Simulate drag enter.
  EXPECT_EQ(nullptr, view->current_drag_data_);
  view->OnDragEntered(event);
  ASSERT_NE(nullptr, view->current_drag_data_);

  EXPECT_EQ(string_data, view->current_drag_data_->text);

  ASSERT_TRUE(view->current_drag_data_->filenames.empty());

  // Simulate drop (completes asynchronously since virtual file data is
  // present).
  auto callback = base::BindOnce(&WebContentsViewAuraTest::OnDropComplete,
                                 base::Unretained(this));
  view->RegisterDropCallbackForTesting(std::move(callback));

  base::RunLoop run_loop;
  async_drop_closure_ = run_loop.QuitClosure();

  auto drop_cb = view->GetDropCallback(event);
  ASSERT_TRUE(drop_cb);
  ui::mojom::DragOperation output_drag_op = ui::mojom::DragOperation::kNone;
  std::move(drop_cb).Run(std::move(data), output_drag_op,
                         /*drag_image_layer_owner=*/nullptr);
  run_loop.Run();

  CheckDropData(view);

  EXPECT_EQ(string_data, drop_complete_data_->drop_data.text);

  ASSERT_TRUE(drop_complete_data_->drop_data.filenames.empty());
}

TEST_F(WebContentsViewAuraTest, DragDropUrlData) {
  WebContentsViewAura* view = GetView();
  auto data = std::make_unique<ui::OSExchangeData>();
  data->MarkRendererTaintedFromOrigin(url::Origin());

  const std::string url_spec = "https://www.wikipedia.org/";
  const GURL url(url_spec);
  const std::u16string url_title = u"Wikipedia";
  data->SetURL(url, url_title);

  // SetUrl should also add a virtual .url (internet shortcut) file.
  std::optional<std::vector<ui::FileInfo>> file_infos =
      data->GetVirtualFilenames();
  ASSERT_TRUE(file_infos.has_value());
  ASSERT_EQ(1ULL, file_infos.value().size());
  EXPECT_EQ(base::FilePath(base::UTF16ToWide(url_title) + L".url"),
            file_infos.value()[0].display_name);

  ui::DropTargetEvent event(*data.get(), kClientPt, kScreenPt,
                            ui::DragDropTypes::DRAG_COPY);

  // Simulate drag enter.
  EXPECT_EQ(nullptr, view->current_drag_data_);
  view->OnDragEntered(event);
  ASSERT_NE(nullptr, view->current_drag_data_);

  EXPECT_EQ(url_spec, view->current_drag_data_->url);
  EXPECT_EQ(url_title, view->current_drag_data_->url_title);

  // Virtual files should not have been retrieved if url data present.
  EXPECT_TRUE(view->current_drag_data_->filenames.empty());
  // Shortcut *.url file contents created by SetURL() should be ignored
  // (https://crbug.com/1274395).
  EXPECT_TRUE(view->current_drag_data_->file_contents_source_url.is_empty());
  EXPECT_TRUE(view->current_drag_data_->file_contents.empty());

  // Simulate drop (completes asynchronously since virtual file data is
  // present).
  auto callback = base::BindOnce(&WebContentsViewAuraTest::OnDropComplete,
                                 base::Unretained(this));
  view->RegisterDropCallbackForTesting(std::move(callback));

  base::RunLoop run_loop;
  async_drop_closure_ = run_loop.QuitClosure();

  auto drop_cb = view->GetDropCallback(event);
  ASSERT_TRUE(drop_cb);
  ui::mojom::DragOperation output_drag_op = ui::mojom::DragOperation::kNone;
  std::move(drop_cb).Run(std::move(data), output_drag_op,
                         /*drag_image_layer_owner=*/nullptr);
  run_loop.Run();

  CheckDropData(view);

  EXPECT_EQ(url_spec, drop_complete_data_->drop_data.url);
  EXPECT_EQ(url_title, drop_complete_data_->drop_data.url_title);

  // Virtual files should not have been retrieved if url data present.
  EXPECT_TRUE(drop_complete_data_->drop_data.filenames.empty());
  EXPECT_TRUE(
      drop_complete_data_->drop_data.file_contents_source_url.is_empty());
  EXPECT_TRUE(drop_complete_data_->drop_data.file_contents.empty());
}
#endif  // BUILDFLAG(IS_WIN)

#if BUILDFLAG(IS_CHROMEOS_ASH)

TEST_F(WebContentsViewAuraTest, StartDragging) {
  const char kGmailUrl[] = "http://mail.google.com/";
  NavigateAndCommit(GURL(kGmailUrl));
  FocusWebContentsOnMainFrame();

  TestDragDropClient drag_drop_client;
  aura::client::SetDragDropClient(root_window(), &drag_drop_client);

  WebContentsViewAura* view = GetView();
  // This condition is needed to avoid calling WebContentsViewAura::EndDrag
  // which will result NOTREACHED being called in
  // `RenderWidgetHostViewBase::TransformPointToCoordSpaceForView`.
  view->drag_in_progress_ = true;

  DropData drop_data;
  drop_data.text.emplace(u"Hello World!");
  view->StartDragging(drop_data, url::Origin(),
                      blink::DragOperationsMask::kDragOperationNone,
                      gfx::ImageSkia(), gfx::Vector2d(), gfx::Rect(),
                      blink::mojom::DragEventSourceInfo(),
                      RenderWidgetHostImpl::From(rvh()->GetWidget()));

  ui::OSExchangeData* exchange_data = drag_drop_client.GetDragDropData();
  EXPECT_TRUE(exchange_data);
  EXPECT_TRUE(exchange_data->GetSource());
  EXPECT_TRUE(exchange_data->GetSource()->IsUrlType());
  EXPECT_EQ(*(exchange_data->GetSource()->GetURL()), GURL(kGmailUrl));
}

#endif  // BUILDFLAG(IS_CHROMEOS_ASH)

TEST_F(WebContentsViewAuraTest,
       RejectDragFromPrivilegedWebContentsToNonPrivilegedWebContents) {}

TEST_F(WebContentsViewAuraTest,
       AcceptDragFromPrivilegedWebContentsToPrivilegedWebContents) {}

TEST_F(WebContentsViewAuraTest,
       RejectDragFromNonPrivilegedWebContentsToPrivilegedWebContents) {}

TEST_F(WebContentsViewAuraTest, StartDragFromPrivilegedWebContents) {}

TEST_F(WebContentsViewAuraTest, EmptyTextInDropDataIsNonNullInOSExchangeData) {}

TEST_F(WebContentsViewAuraTest,
       EmptyTextWithUrlInDropDataIsEmptyInOSExchangeDataGetString) {}

TEST_F(WebContentsViewAuraTest,
       UrlInDropDataReturnsUrlInOSExchangeDataGetString) {}

}  // namespace content