blob: d01f12fc5aee4ef038cd5b0f3adbb253167983c7 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/public/test/web_test_support.h"
#include <stddef.h>
#include <algorithm>
#include <unordered_map>
#include <utility>
#include <vector>
#include "base/callback.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "cc/base/switches.h"
#include "cc/test/pixel_test_output_surface.h"
#include "components/viz/common/features.h"
#include "components/viz/common/frame_sinks/copy_output_request.h"
#include "components/viz/test/test_layer_tree_frame_sink.h"
#include "content/browser/bluetooth/bluetooth_device_chooser_controller.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/worker_host/shared_worker_service_impl.h"
#include "content/common/renderer.mojom.h"
#include "content/common/unique_name_helper.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/gpu_stream_constants.h"
#include "content/public/common/page_state.h"
#include "content/public/common/screen_info.h"
#include "content/renderer/compositor/layer_tree_view.h"
#include "content/renderer/input/render_widget_input_handler_delegate.h"
#include "content/renderer/loader/request_extra_data.h"
#include "content/renderer/loader/web_worker_fetch_context_impl.h"
#include "content/renderer/render_frame_impl.h"
#include "content/renderer/render_thread_impl.h"
#include "content/renderer/render_view_impl.h"
#include "content/renderer/render_widget.h"
#include "content/renderer/renderer_blink_platform_impl.h"
#include "content/renderer/web_test_dependencies.h"
#include "content/shell/common/shell_switches.h"
#include "content/shell/common/web_test/web_test_switches.h"
#include "content/shell/renderer/web_test/blink_test_runner.h"
#include "content/shell/renderer/web_test/web_test_render_thread_observer.h"
#include "content/shell/test_runner/test_common.h"
#include "content/shell/test_runner/web_frame_test_proxy.h"
#include "content/shell/test_runner/web_test_interfaces.h"
#include "content/shell/test_runner/web_view_test_proxy.h"
#include "content/shell/test_runner/web_widget_test_proxy.h"
#include "gpu/ipc/service/image_transport_surface.h"
#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
#include "third_party/blink/public/platform/web_float_rect.h"
#include "third_party/blink/public/platform/web_input_event.h"
#include "third_party/blink/public/platform/web_rect.h"
#include "third_party/blink/public/web/web_view.h"
#include "ui/base/ui_base_switches.h"
#include "ui/events/blink/blink_event_util.h"
#include "ui/gfx/geometry/vector2d.h"
#include "ui/gfx/icc_profile.h"
#include "ui/gfx/test/icc_profiles.h"
#if defined(OS_MACOSX)
#include "content/browser/frame_host/popup_menu_helper_mac.h"
#elif defined(OS_WIN)
#include "content/child/font_warmup_win.h"
#include "third_party/blink/public/web/win/web_font_rendering.h"
#include "third_party/skia/include/core/SkFontMgr.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "third_party/skia/include/ports/SkTypeface_win.h"
#include "ui/gfx/win/direct_write.h"
#endif
using blink::WebRect;
using blink::WebSize;
namespace content {
namespace {
RenderViewImpl* CreateWebViewTestProxy(CompositorDependencies* compositor_deps,
const mojom::CreateViewParams& params) {
test_runner::WebTestInterfaces* interfaces =
WebTestRenderThreadObserver::GetInstance()->test_interfaces();
auto* render_view_proxy =
new test_runner::WebViewTestProxy(compositor_deps, params);
BlinkTestRunner* test_runner = new BlinkTestRunner(render_view_proxy);
// TODO(lukasza): Using the 1st BlinkTestRunner as the main delegate is wrong,
// but it is difficult to change because this behavior has been baked for a
// long time into test assumptions (i.e. which PrintMessage gets delivered to
// the browser depends on this).
static bool first_test_runner = true;
if (first_test_runner) {
first_test_runner = false;
interfaces->SetDelegate(test_runner);
}
render_view_proxy->Initialize(interfaces, test_runner);
return render_view_proxy;
}
RenderWidget* CreateRenderWidgetForChildLocalRoot(
int32_t routing_id,
CompositorDependencies* compositor_deps,
const ScreenInfo& screen_info,
blink::WebDisplayMode display_mode,
bool swapped_out,
bool hidden,
bool never_visible) {
auto* render_widget_proxy = new test_runner::WebWidgetTestProxy(
routing_id, compositor_deps, screen_info, display_mode, swapped_out,
hidden, never_visible);
return render_widget_proxy;
}
void RenderWidgetForChildLocalRootInitialized(RenderWidget* render_widget) {
test_runner::WebTestInterfaces* interfaces =
WebTestRenderThreadObserver::GetInstance()->test_interfaces();
blink::WebWidget* web_widget = render_widget->GetWebWidget();
auto* web_frame_widget = static_cast<blink::WebFrameWidget*>(web_widget);
// RenderWidgets for a frame will have a local root with a RenderView.
blink::WebView* web_view = web_frame_widget->LocalRoot()->View();
RenderView* render_view = content::RenderView::FromWebView(web_view);
// RenderViews are always RenderViewImpls internally.
auto* render_view_impl = static_cast<RenderViewImpl*>(render_view);
// We are here because CreateWebWidgetTestProxy() was used to make the
// RenderWidget, and it creates a WebWidgetTestProxy instead, which is-a
// RenderWidget.
auto* render_widget_proxy =
static_cast<test_runner::WebWidgetTestProxy*>(render_widget);
render_widget_proxy->Initialize(interfaces, web_widget, render_view_impl);
}
RenderFrameImpl* CreateWebFrameTestProxy(RenderFrameImpl::CreateParams params) {
test_runner::WebTestInterfaces* interfaces =
WebTestRenderThreadObserver::GetInstance()->test_interfaces();
// RenderFrameImpl always has a RenderViewImpl for it.
RenderViewImpl* render_view_impl = params.render_view;
auto* render_frame_proxy =
new test_runner::WebFrameTestProxy(std::move(params));
render_frame_proxy->Initialize(interfaces, render_view_impl);
return render_frame_proxy;
}
float GetWindowToViewportScale(RenderWidget* render_widget) {
blink::WebFloatRect rect(0, 0, 1.0f, 0.0);
render_widget->ConvertWindowToViewport(&rect);
return rect.width;
}
#if defined(OS_WIN)
// DirectWrite only has access to %WINDIR%\Fonts by default. For developer
// side-loading, support kRegisterFontFiles to allow access to additional fonts.
void RegisterSideloadedTypefaces(SkFontMgr* fontmgr) {
for (const auto& file : switches::GetSideloadFontFiles()) {
blink::WebFontRendering::AddSideloadedFontForTesting(
fontmgr->makeFromFile(file.c_str()));
}
}
#endif // OS_WIN
} // namespace
test_runner::WebViewTestProxyBase* GetWebViewTestProxyBase(
RenderView* render_view) {
auto* render_view_proxy =
static_cast<test_runner::WebViewTestProxy*>(render_view);
return static_cast<test_runner::WebViewTestProxyBase*>(render_view_proxy);
}
test_runner::WebFrameTestProxyBase* GetWebFrameTestProxyBase(
RenderFrame* render_frame) {
auto* render_frame_proxy =
static_cast<test_runner::WebFrameTestProxy*>(render_frame);
return static_cast<test_runner::WebFrameTestProxyBase*>(render_frame_proxy);
}
test_runner::WebWidgetTestProxyBase* GetWebWidgetTestProxyBase(
blink::WebLocalFrame* frame) {
DCHECK(frame);
RenderFrame* local_root = RenderFrame::FromWebFrame(frame->LocalRoot());
RenderFrameImpl* local_root_impl = static_cast<RenderFrameImpl*>(local_root);
DCHECK(local_root);
// TODO(lfg): Simplify once RenderView no longer inherits from RenderWidget.
if (local_root->IsMainFrame()) {
// For main frames, since the RenderWidget is attached to the RenderView
// (subclassed by WebViewTestProxy), we grab the widget stuff through the
// view.
test_runner::WebViewTestProxyBase* web_view_test_proxy_base =
GetWebViewTestProxyBase(local_root->GetRenderView());
return web_view_test_proxy_base->web_widget_test_proxy_base();
} else {
// For sub frames, the RenderWidget is independent from the RenderView and
// hangs off the frame. The WebWidgetTestProxy then subclasses the
// RenderWidget since there's no RenderView in the way. From there we can
// go up to the WebWidgetTestProxyBase.
//
// Inheritance:
// RenderWidget WebWidgetTestProxyBase
// \\ //
// WebWidgetTestProxy
RenderWidget* render_widget = local_root_impl->GetLocalRootRenderWidget();
auto* proxy = static_cast<test_runner::WebWidgetTestProxy*>(render_widget);
return static_cast<test_runner::WebWidgetTestProxyBase*>(proxy);
}
}
void EnableWebTestProxyCreation() {
RenderViewImpl::InstallCreateHook(CreateWebViewTestProxy);
RenderFrameImpl::InstallCreateHook(CreateWebFrameTestProxy,
CreateRenderWidgetForChildLocalRoot,
RenderWidgetForChildLocalRootInitialized);
}
void FetchManifest(blink::WebView* view, FetchManifestCallback callback) {
RenderFrameImpl::FromWebFrame(view->MainFrame())
->GetManifestManager()
.RequestManifest(std::move(callback));
}
void SetWorkerRewriteURLFunction(RewriteURLFunction rewrite_url_function) {
WebWorkerFetchContextImpl::InstallRewriteURLFunction(rewrite_url_function);
}
namespace {
// Invokes a callback on commit (on the main thread) to obtain the output
// surface that should be used, then asks that output surface to submit the copy
// request at SwapBuffers time.
class CopyRequestSwapPromise : public cc::SwapPromise {
public:
using FindLayerTreeFrameSinkCallback =
base::Callback<viz::TestLayerTreeFrameSink*()>;
CopyRequestSwapPromise(
std::unique_ptr<viz::CopyOutputRequest> request,
FindLayerTreeFrameSinkCallback find_layer_tree_frame_sink_callback)
: copy_request_(std::move(request)),
find_layer_tree_frame_sink_callback_(
std::move(find_layer_tree_frame_sink_callback)) {}
// cc::SwapPromise implementation.
void OnCommit() override {
layer_tree_frame_sink_from_commit_ =
find_layer_tree_frame_sink_callback_.Run();
DCHECK(layer_tree_frame_sink_from_commit_);
}
void DidActivate() override {}
void WillSwap(viz::CompositorFrameMetadata*) override {
layer_tree_frame_sink_from_commit_->RequestCopyOfOutput(
std::move(copy_request_));
}
void DidSwap() override {}
void DidNotSwap(DidNotSwapReason r) override {
// The compositor should always swap in web test mode.
NOTREACHED() << "did not swap for reason " << r;
}
int64_t TraceId() const override { return 0; }
private:
std::unique_ptr<viz::CopyOutputRequest> copy_request_;
FindLayerTreeFrameSinkCallback find_layer_tree_frame_sink_callback_;
viz::TestLayerTreeFrameSink* layer_tree_frame_sink_from_commit_ = nullptr;
};
} // namespace
class WebTestDependenciesImpl : public WebTestDependencies,
public viz::TestLayerTreeFrameSinkClient {
public:
bool UseDisplayCompositorPixelDump() const override {
base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
return cmd->HasSwitch(switches::kEnableDisplayCompositorPixelDump);
}
std::unique_ptr<cc::LayerTreeFrameSink> CreateLayerTreeFrameSink(
int32_t routing_id,
scoped_refptr<gpu::GpuChannelHost> gpu_channel,
scoped_refptr<viz::ContextProvider> compositor_context_provider,
scoped_refptr<viz::RasterContextProvider> worker_context_provider,
gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
CompositorDependencies* deps) override {
// This could override the GpuChannel for a LayerTreeFrameSink that was
// previously being created but in that case the old GpuChannel would be
// lost as would the LayerTreeFrameSink.
gpu_channel_ = gpu_channel;
gpu_memory_buffer_manager_ = gpu_memory_buffer_manager;
auto* task_runner = deps->GetCompositorImplThreadTaskRunner().get();
bool synchronous_composite = !task_runner;
if (!task_runner)
task_runner = base::ThreadTaskRunnerHandle::Get().get();
viz::RendererSettings renderer_settings;
base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
renderer_settings.allow_antialiasing &=
!cmd->HasSwitch(cc::switches::kDisableCompositedAntialiasing);
renderer_settings.highp_threshold_min = 2048;
// Keep texture sizes exactly matching the bounds of the RenderPass to avoid
// floating point badness in texcoords.
renderer_settings.dont_round_texture_sizes_for_pixel_tests = true;
renderer_settings.use_skia_renderer = features::IsUsingSkiaRenderer();
constexpr bool disable_display_vsync = false;
constexpr double refresh_rate = 60.0;
auto layer_tree_frame_sink = std::make_unique<viz::TestLayerTreeFrameSink>(
std::move(compositor_context_provider),
std::move(worker_context_provider), gpu_memory_buffer_manager,
renderer_settings, task_runner, synchronous_composite,
disable_display_vsync, refresh_rate);
layer_tree_frame_sink->SetClient(this);
layer_tree_frame_sinks_[routing_id] = layer_tree_frame_sink.get();
return std::move(layer_tree_frame_sink);
}
std::unique_ptr<cc::SwapPromise> RequestCopyOfOutput(
int32_t routing_id,
std::unique_ptr<viz::CopyOutputRequest> request) override {
// Note that we can't immediately check layer_tree_frame_sinks_, since it
// may not have been created yet. Instead, we wait until OnCommit to find
// the currently active LayerTreeFrameSink for the given RenderWidget
// routing_id.
return std::make_unique<CopyRequestSwapPromise>(
std::move(request),
base::Bind(&WebTestDependenciesImpl::FindLayerTreeFrameSink,
// |this| will still be valid, because its lifetime is tied
// to RenderThreadImpl, which outlives web test execution.
base::Unretained(this), routing_id));
}
// TestLayerTreeFrameSinkClient implementation.
std::unique_ptr<viz::OutputSurface> CreateDisplayOutputSurface(
scoped_refptr<viz::ContextProvider> compositor_context_provider)
override {
// This is for an offscreen context for the compositor. So the default
// framebuffer doesn't need alpha, depth, stencil, antialiasing.
gpu::ContextCreationAttribs attributes;
attributes.alpha_size = -1;
attributes.depth_size = 0;
attributes.stencil_size = 0;
attributes.samples = 0;
attributes.sample_buffers = 0;
attributes.bind_generates_resource = false;
attributes.lose_context_when_out_of_memory = true;
const bool automatic_flushes = false;
const bool support_locking = false;
const bool support_grcontext = true;
scoped_refptr<viz::ContextProvider> context_provider;
gpu::ContextResult context_result = gpu::ContextResult::kTransientFailure;
while (context_result != gpu::ContextResult::kSuccess) {
context_provider = base::MakeRefCounted<ws::ContextProviderCommandBuffer>(
gpu_channel_, gpu_memory_buffer_manager_, kGpuStreamIdDefault,
kGpuStreamPriorityDefault, gpu::kNullSurfaceHandle,
GURL("chrome://gpu/"
"WebTestDependenciesImpl::CreateOutputSurface"),
automatic_flushes, support_locking, support_grcontext,
gpu::SharedMemoryLimits(), attributes,
ws::command_buffer_metrics::ContextType::FOR_TESTING);
context_result = context_provider->BindToCurrentThread();
// Web tests can't recover from a fatal or surface failure.
CHECK(!gpu::IsFatalOrSurfaceFailure(context_result));
}
bool flipped_output_surface = false;
return std::make_unique<cc::PixelTestOutputSurface>(
std::move(context_provider), flipped_output_surface);
}
void DisplayReceivedLocalSurfaceId(
const viz::LocalSurfaceId& local_surface_id) override {}
void DisplayReceivedCompositorFrame(
const viz::CompositorFrame& frame) override {}
void DisplayWillDrawAndSwap(bool will_draw_and_swap,
viz::RenderPassList* render_passes) override {}
void DisplayDidDrawAndSwap() override {}
private:
viz::TestLayerTreeFrameSink* FindLayerTreeFrameSink(int32_t routing_id) {
auto it = layer_tree_frame_sinks_.find(routing_id);
return it == layer_tree_frame_sinks_.end() ? nullptr : it->second;
}
// Entries are not removed, so this map can grow. However, it is only used in
// web tests, so this memory usage does not occur in production.
// Entries in this map will outlive the output surface, because this object is
// owned by RenderThreadImpl, which outlives web test execution.
std::unordered_map<int32_t, viz::TestLayerTreeFrameSink*>
layer_tree_frame_sinks_;
scoped_refptr<gpu::GpuChannelHost> gpu_channel_;
gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager_ = nullptr;
};
void EnableRendererWebTestMode() {
RenderThreadImpl::current()->set_web_test_dependencies(
std::make_unique<WebTestDependenciesImpl>());
UniqueNameHelper::PreserveStableUniqueNameForTesting();
#if defined(OS_WIN)
RegisterSideloadedTypefaces(SkFontMgr_New_DirectWrite().get());
#endif
}
void EnableBrowserWebTestMode() {
#if defined(OS_MACOSX)
PopupMenuHelper::DontShowPopupMenuForTesting();
#endif
RenderWidgetHostImpl::DisableResizeAckCheckForTesting();
}
void TerminateAllSharedWorkersForTesting(StoragePartition* storage_partition,
base::OnceClosure callback) {
static_cast<SharedWorkerServiceImpl*>(
storage_partition->GetSharedWorkerService())
->TerminateAllWorkersForTesting(std::move(callback));
}
int GetLocalSessionHistoryLength(RenderView* render_view) {
return static_cast<RenderViewImpl*>(render_view)
->GetLocalSessionHistoryLengthForTesting();
}
void SetFocusAndActivate(RenderView* render_view, bool enable) {
static_cast<RenderViewImpl*>(render_view)
->SetFocusAndActivateForTesting(enable);
}
void ForceResizeRenderView(RenderView* render_view, const WebSize& new_size) {
auto* render_view_impl = static_cast<RenderViewImpl*>(render_view);
gfx::Rect window_rect(render_view_impl->RootWindowRect().x,
render_view_impl->RootWindowRect().y, new_size.width,
new_size.height);
RenderWidget* render_widget = render_view_impl->GetWidget();
render_widget->SetWindowRectSynchronouslyForTesting(window_rect);
}
void SetDeviceScaleFactor(RenderView* render_view, float factor) {
RenderWidget* render_widget =
static_cast<RenderViewImpl*>(render_view)->GetWidget();
render_widget->SetDeviceScaleFactorForTesting(factor);
}
float GetWindowToViewportScale(RenderView* render_view) {
return GetWindowToViewportScale(
static_cast<RenderViewImpl*>(render_view)->GetWidget());
}
std::unique_ptr<blink::WebInputEvent> TransformScreenToWidgetCoordinates(
test_runner::WebWidgetTestProxyBase* web_widget_test_proxy_base,
const blink::WebInputEvent& event) {
DCHECK(web_widget_test_proxy_base);
// Two possible inheritance stories.
// A main frame:
// RenderWidget WebWidgetTestProxyBase
// \*\*\ (private) /*/*/ (private)
// RenderViewImpl WebViewTestProxyBase
// \\ //
// WebViewTestProxy
//
// And a sub frame:
// RenderWidget WebWidgetTestProxyBase
// \\ //
// WebWidgetTestProxy
RenderWidget* render_widget;
if (web_widget_test_proxy_base->main_frame_widget()) {
auto* proxy_base = web_widget_test_proxy_base->web_view_test_proxy_base();
auto* proxy = static_cast<test_runner::WebViewTestProxy*>(proxy_base);
auto* view = static_cast<RenderViewImpl*>(proxy);
render_widget = view->GetWidget();
} else {
auto* proxy = static_cast<test_runner::WebWidgetTestProxy*>(
web_widget_test_proxy_base);
render_widget = static_cast<RenderWidget*>(proxy);
}
blink::WebRect view_rect = render_widget->ViewRect();
float scale = GetWindowToViewportScale(render_widget);
gfx::Vector2d delta(-view_rect.x, -view_rect.y);
return ui::TranslateAndScaleWebInputEvent(event, delta, scale);
}
gfx::ColorSpace GetTestingColorSpace(const std::string& name) {
if (name == "genericRGB") {
return gfx::ICCProfileForTestingGenericRGB().GetColorSpace();
} else if (name == "sRGB") {
return gfx::ColorSpace::CreateSRGB();
} else if (name == "test" || name == "colorSpin") {
return gfx::ICCProfileForTestingColorSpin().GetColorSpace();
} else if (name == "adobeRGB") {
return gfx::ICCProfileForTestingAdobeRGB().GetColorSpace();
} else if (name == "reset") {
return display::Display::GetForcedDisplayColorProfile();
}
return gfx::ColorSpace();
}
void SetDeviceColorSpace(RenderView* render_view,
const gfx::ColorSpace& color_space) {
RenderWidget* render_widget =
static_cast<RenderViewImpl*>(render_view)->GetWidget();
render_widget->SetDeviceColorSpaceForTesting(color_space);
}
void SetTestBluetoothScanDuration(BluetoothTestScanDurationSetting setting) {
switch (setting) {
case BluetoothTestScanDurationSetting::kImmediateTimeout:
BluetoothDeviceChooserController::SetTestScanDurationForTesting(
BluetoothDeviceChooserController::TestScanDurationSetting::
IMMEDIATE_TIMEOUT);
break;
case BluetoothTestScanDurationSetting::kNeverTimeout:
BluetoothDeviceChooserController::SetTestScanDurationForTesting(
BluetoothDeviceChooserController::TestScanDurationSetting::
NEVER_TIMEOUT);
break;
}
}
void UseSynchronousResizeMode(RenderView* render_view, bool enable) {
RenderWidget* render_widget =
static_cast<RenderViewImpl*>(render_view)->GetWidget();
render_widget->UseSynchronousResizeModeForTesting(enable);
}
void EnableAutoResizeMode(RenderView* render_view,
const WebSize& min_size,
const WebSize& max_size) {
RenderWidget* render_widget =
static_cast<RenderViewImpl*>(render_view)->GetWidget();
render_widget->EnableAutoResizeForTesting(min_size, max_size);
}
void DisableAutoResizeMode(RenderView* render_view, const WebSize& new_size) {
RenderWidget* render_widget =
static_cast<RenderViewImpl*>(render_view)->GetWidget();
render_widget->DisableAutoResizeForTesting(new_size);
}
void SchedulerRunIdleTasks(base::OnceClosure callback) {
blink::scheduler::WebThreadScheduler* scheduler =
content::RenderThreadImpl::current()->GetWebMainThreadScheduler();
blink::scheduler::RunIdleTasksForTesting(scheduler, std::move(callback));
}
void ForceTextInputStateUpdateForRenderFrame(RenderFrame* frame) {
RenderWidget* render_widget =
static_cast<RenderFrameImpl*>(frame)->GetLocalRootRenderWidget();
render_widget->ShowVirtualKeyboard();
}
bool IsNavigationInitiatedByRenderer(const blink::WebURLRequest& request) {
RequestExtraData* extra_data =
static_cast<RequestExtraData*>(request.GetExtraData());
return extra_data && extra_data->navigation_initiated_by_renderer();
}
} // namespace content