blob: 63599fc7247cd27ac86ce7bef06c94571bacebbe [file] [log] [blame]
Ken Rockotb86921c2020-05-29 20:35:501// Copyright 2020 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Maksim Sisov864826d2020-08-18 20:18:505#include "base/base_switches.h"
Ken Rockot0634566b2020-07-10 00:34:266#include "base/cfi_buildflags.h"
Ken Rockotb86921c2020-05-29 20:35:507#include "base/command_line.h"
8#include "base/files/file_util.h"
9#include "base/files/scoped_temp_dir.h"
10#include "base/process/launch.h"
11#include "base/run_loop.h"
Ken Rockot2fc09262020-06-19 03:21:3012#include "base/strings/utf_string_conversions.h"
Guido Urdanetaef4e91942020-11-09 15:06:2413#include "base/test/bind.h"
Ken Rockotb86921c2020-05-29 20:35:5014#include "build/build_config.h"
Yuta Hijikatad0a8b6b2020-11-20 16:36:1515#include "build/chromeos_buildflags.h"
Ken Rockot2fc09262020-06-19 03:21:3016#include "content/public/common/content_switches.h"
Ken Rockotb86921c2020-05-29 20:35:5017#include "content/public/test/browser_test.h"
18#include "content/public/test/content_browser_test.h"
19#include "content/shell/common/shell_controller.test-mojom.h"
20#include "content/shell/common/shell_switches.h"
21#include "mojo/public/cpp/bindings/remote.h"
22#include "mojo/public/cpp/platform/platform_channel.h"
23#include "mojo/public/cpp/system/invitation.h"
Ken Rockotb86921c2020-05-29 20:35:5024#include "testing/gtest/include/gtest/gtest.h"
25
Maksim Sisov864826d2020-08-18 20:18:5026#if defined(USE_OZONE)
27#include "ui/ozone/public/ozone_switches.h"
28#endif
29
Yuta Hijikatad0a8b6b2020-11-20 16:36:1530#if BUILDFLAG(IS_CHROMEOS_ASH)
Ken Rockotef1887e42020-09-29 18:18:1231#include "ui/gl/gl_switches.h"
32#endif
33
Ken Rockotb86921c2020-05-29 20:35:5034namespace content {
35namespace {
36
Xiaohan Wang1ecfd002022-01-19 22:33:1037#if BUILDFLAG(IS_WIN)
Ken Rockotb86921c2020-05-29 20:35:5038const char kShellExecutableName[] = "content_shell.exe";
39#else
40const char kShellExecutableName[] = "content_shell";
Ken Rockot2fc09262020-06-19 03:21:3041const char kMojoCoreLibraryName[] = "libmojo_core.so";
Ken Rockotb86921c2020-05-29 20:35:5042#endif
43
44base::FilePath GetCurrentDirectory() {
45 base::FilePath current_directory;
46 CHECK(base::GetCurrentDirectory(&current_directory));
47 return current_directory;
48}
49
50class LaunchAsMojoClientBrowserTest : public ContentBrowserTest {
51 public:
52 LaunchAsMojoClientBrowserTest() { CHECK(temp_dir_.CreateUniqueTempDir()); }
53
54 ~LaunchAsMojoClientBrowserTest() override {
55 // Ensure that the launched Content Shell process is dead before the test
56 // tears down, otherwise the temp profile dir may fail to delete. Note that
57 // tests must explicitly request shutdown through ShellController before
58 // finishing, otherwise this will time out.
59 CHECK(content_shell_process_.WaitForExit(nullptr));
60 CHECK(temp_dir_.Delete());
61 }
62
63 base::CommandLine MakeShellCommandLine() {
64 base::CommandLine command_line(
65 GetFilePathNextToCurrentExecutable(kShellExecutableName));
66 command_line.AppendSwitchPath(switches::kContentShellDataPath,
67 temp_dir_.GetPath());
Maksim Sisov2ac2c312021-11-03 08:00:1268#if defined(USE_OZONE)
Maksim Sisov864826d2020-08-18 20:18:5069 const base::CommandLine& cmdline = *base::CommandLine::ForCurrentProcess();
Maksim Sisov2ac2c312021-11-03 08:00:1270 const char* kSwitchesToCopy[] = {
71 // Keep the kOzonePlatform switch that the Ozone must use.
72 switches::kOzonePlatform,
73 };
Maksim Sisov864826d2020-08-18 20:18:5074 command_line.CopySwitchesFrom(cmdline, kSwitchesToCopy,
Daniel Chengad44af2f2022-02-26 18:07:5475 std::size(kSwitchesToCopy));
Maksim Sisov2ac2c312021-11-03 08:00:1276#endif
Ken Rockotef1887e42020-09-29 18:18:1277
Yuta Hijikatad0a8b6b2020-11-20 16:36:1578#if BUILDFLAG(IS_CHROMEOS_ASH)
Alexis Hetubda66cce2021-04-08 13:04:5679 command_line.AppendSwitchASCII(switches::kUseGL,
Alexis Hetu8a3885d2022-01-17 16:16:1580 gl::kGLImplementationANGLEName);
81 command_line.AppendSwitchASCII(switches::kUseANGLE,
82 gl::kANGLEImplementationSwiftShaderName);
Ken Rockotef1887e42020-09-29 18:18:1283#endif
Ken Rockotb86921c2020-05-29 20:35:5084 return command_line;
85 }
86
87 mojo::Remote<mojom::ShellController> LaunchContentShell(
88 const base::CommandLine& command_line) {
89 mojo::PlatformChannel channel;
90 base::LaunchOptions options;
91 base::CommandLine shell_command_line(command_line);
92 channel.PrepareToPassRemoteEndpoint(&options, &shell_command_line);
93 content_shell_process_ = base::LaunchProcess(shell_command_line, options);
94 channel.RemoteProcessLaunchAttempted();
95
96 mojo::OutgoingInvitation invitation;
Hidehiko Abe23e45e82020-08-25 08:41:4897 mojo::Remote<mojom::ShellController> controller(
98 mojo::PendingRemote<mojom::ShellController>(
Ken Rockotb86921c2020-05-29 20:35:5099 invitation.AttachMessagePipe(0), /*version=*/0));
100 mojo::OutgoingInvitation::Send(std::move(invitation),
101 content_shell_process_.Handle(),
102 channel.TakeLocalEndpoint());
Ken Rockotb86921c2020-05-29 20:35:50103 return controller;
104 }
105
Xiaohan Wang1ecfd002022-01-19 22:33:10106#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
Ken Rockot2fc09262020-06-19 03:21:30107 base::FilePath GetMojoCoreLibraryPath() {
108 return GetFilePathNextToCurrentExecutable(kMojoCoreLibraryName);
109 }
110#endif
111
Ken Rockotb86921c2020-05-29 20:35:50112 private:
113 base::FilePath GetFilePathNextToCurrentExecutable(
114 const std::string& filename) {
115 base::FilePath executable_dir =
116 base::CommandLine::ForCurrentProcess()->GetProgram().DirName();
117 if (executable_dir.IsAbsolute())
118 return executable_dir.AppendASCII(filename);
119
120 // If the current executable path is relative, resolve it to an absolute
121 // path before swapping in |filename|. This ensures that the path is OK to
122 // use with base::LaunchProcess. Otherwise we could end up with a path
123 // containing only |filename|, and this can fail to execute in environments
124 // where "." is not in the PATH (common on e.g. Linux).
125 return current_directory_.Append(executable_dir).AppendASCII(filename);
126 }
127
128 base::ScopedTempDir temp_dir_;
129 const base::FilePath current_directory_ = GetCurrentDirectory();
130 base::Process content_shell_process_;
131 mojo::Remote<mojom::ShellController> shell_controller_;
132};
133
134IN_PROC_BROWSER_TEST_F(LaunchAsMojoClientBrowserTest, LaunchAndBindInterface) {
135 // Verifies that we can launch an instance of Content Shell with a Mojo
136 // invitation on the command line and reach the new browser process's exposed
137 // ShellController interface.
138
139 const char kExtraSwitchName[] = "extra-switch-for-testing";
140 const char kExtraSwitchValue[] = "42";
141
142 base::CommandLine command_line = MakeShellCommandLine();
143 command_line.AppendSwitchASCII(kExtraSwitchName, kExtraSwitchValue);
144 mojo::Remote<mojom::ShellController> shell_controller =
145 LaunchContentShell(command_line);
146
147 base::RunLoop loop;
148 shell_controller->GetSwitchValue(
149 kExtraSwitchName,
Anton Bikineevf62d1bf2021-05-15 17:56:07150 base::BindLambdaForTesting([&](const absl::optional<std::string>& value) {
Ken Rockotb86921c2020-05-29 20:35:50151 ASSERT_TRUE(value);
152 EXPECT_EQ(kExtraSwitchValue, *value);
153 loop.Quit();
154 }));
155 loop.Run();
156
157 shell_controller->ShutDown();
158}
159
Xiaohan Wang1ecfd002022-01-19 22:33:10160#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
Ken Rockotb66b0b12021-12-09 02:53:15161// TODO(crbug.com/1259557): This test implementation fundamentally conflicts
162// with a fix for the linked bug because it causes a browser process to behave
163// partially as a broker and partially as a non-broker. This can be re-enabled
164// when we migrate away from the current Mojo implementation. It's OK to disable
165// for now because no production code relies on this feature.
Ken Rockot0634566b2020-07-10 00:34:26166IN_PROC_BROWSER_TEST_F(LaunchAsMojoClientBrowserTest,
Ken Rockotb66b0b12021-12-09 02:53:15167 DISABLED_WithMojoCoreLibrary) {
Ken Rockot2fc09262020-06-19 03:21:30168 // Instructs a newly launched Content Shell browser to initialize Mojo Core
169 // dynamically from a shared library, rather than using the version linked
170 // into the Content Shell binary.
171 //
172 // This exercises end-to-end JS in order to cover real IPC behavior between
173 // the browser and a renderer.
174
175 base::CommandLine command_line = MakeShellCommandLine();
176 command_line.AppendSwitchPath(switches::kMojoCoreLibraryPath,
177 GetMojoCoreLibraryPath());
178 mojo::Remote<mojom::ShellController> shell_controller =
179 LaunchContentShell(command_line);
180
181 // Indisputable proof that we're evaluating JavaScript.
182 const std::string kExpressionToEvaluate = "'ba'+ +'a'+'as'";
183 const base::Value kExpectedValue("baNaNas");
184
185 base::RunLoop loop;
186 shell_controller->ExecuteJavaScript(
187 base::ASCIIToUTF16(kExpressionToEvaluate),
188 base::BindLambdaForTesting([&](base::Value value) {
189 EXPECT_EQ(kExpectedValue, value);
190 loop.Quit();
191 }));
192 loop.Run();
193
194 shell_controller->ShutDown();
195}
Xiaohan Wang1ecfd002022-01-19 22:33:10196#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
Ken Rockot2fc09262020-06-19 03:21:30197
Ken Rockotb86921c2020-05-29 20:35:50198} // namespace
199} // namespace content