Bug 1635451 - Minimize content processes' connections to the X server. r=jgilbert,stransky,nika

This patch launches content processes with the `MOZ_HEADLESS` env var set
if they're using GTK with an X11 display (and there's no other reason
they'd need GTK).

The goal is to avoid exhausting Xorg's default limit of 256 clients if
there are many content processes due to Fission.  If these conditions
are met, the content process doesn't need to eagerly connect to the X
server.  This does not affect the sandbox policy, and content processes
can still use X if needed for, e.g.,  WebGL.

The boolean pref `dom.ipc.avoid-gtk`, set by default, controls this
feature.  In the future it could also be extended to minimize GTK use
with Wayland displays.

Note that disabling `widget.non-native-theme.enabled`, which is also
enabled by default, will restore the use of X11 in all content processes
even if this pref is set; the alternative is that widgets wouldn't render
in that case.

This change will also save some memory for now-unnecessary instances of
GTK's global state, and improve content process startup time.

Remove also the temp pref dom.ipc.remote-mozIcon because it cannot work
anymore with the content process being headless.

Differential Revision: https://phabricator.services.mozilla.com/D112197
This commit is contained in:
Jed Davis 2021-07-06 07:42:42 +00:00
Родитель dc3ae62544
Коммит 2257145e1c
13 изменённых файлов: 105 добавлений и 63 удалений

Просмотреть файл

@ -279,6 +279,7 @@
#ifdef MOZ_WIDGET_GTK
# include <gdk/gdk.h>
# include "mozilla/WidgetUtilsGtk.h"
#endif
#include "mozilla/RemoteSpellCheckEngineParent.h"
@ -2545,6 +2546,15 @@ bool ContentParent::BeginSubprocessLaunch(ProcessPriority aPriority) {
extraArgs.push_back("-parentBuildID");
extraArgs.push_back(parentBuildID.get());
#ifdef MOZ_WIDGET_GTK
// This is X11-only pending a solution for WebGL in Wayland mode.
if (StaticPrefs::dom_ipc_avoid_gtk() &&
StaticPrefs::widget_non_native_theme_enabled() &&
widget::GdkIsX11Display()) {
mSubprocess->SetEnv("MOZ_HEADLESS", "1");
}
#endif
// See also ActorDealloc.
mSelfRef = this;
mLaunchYieldTS = TimeStamp::Now();

Просмотреть файл

@ -37,3 +37,6 @@ support-files = file_dummy.html
skip-if =
verify
fission && os == "linux" && asan # Bug 1713905 - new Fission platform triage
[browser_very_fission.js]
support-files = file_dummy.html
run-if = widget == "gtk"

Просмотреть файл

@ -0,0 +1,38 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
// This test creates a large number of content processes as a
// regression test for bug 1635451.
const TEST_PAGE =
"http://mochi.test:8888/browser/dom/ipc/tests/file_dummy.html";
const NUM_TABS = 256;
add_task(async () => {
let promises = [];
for (let i = 0; i < NUM_TABS; ++i) {
promises.push(
BrowserTestUtils.openNewForegroundTab({
gBrowser,
opening: TEST_PAGE,
waitForLoad: true,
forceNewProcess: true,
})
);
}
let tabs = [];
for (const p of promises) {
tabs.push(await p);
}
ok(true, "All of the tabs loaded");
for (const t of tabs) {
BrowserTestUtils.removeTab(t);
}
});

Просмотреть файл

@ -17,14 +17,24 @@ using namespace mozilla::widget;
static class GLContextProviderX11 sGLContextProviderX11;
static class GLContextProviderEGL sGLContextProviderEGL;
// Note that if there is no GTK display, `GdkIsX11Display` and
// `GdkIsWaylandDisplay` will both return false. That case can
// currently happen only in X11 mode if the pref `dom.ipc.avoid-gtk`
// is set (and applicable to this process). Thus, these conditionals
// check for the presence of Wayland rather than the absence of X11.
//
// In the future we'll want `dom.ipc.avoid-gtk` to also apply to
// Wayland; at that time we'll need another way to communicate the
// choice of window system.
already_AddRefed<GLContext> GLContextProviderWayland::CreateForCompositorWidget(
CompositorWidget* aCompositorWidget, bool aHardwareWebRender,
bool aForceAccelerated) {
if (GdkIsX11Display()) {
return sGLContextProviderX11.CreateForCompositorWidget(
if (GdkIsWaylandDisplay()) {
return sGLContextProviderEGL.CreateForCompositorWidget(
aCompositorWidget, aHardwareWebRender, aForceAccelerated);
} else {
return sGLContextProviderEGL.CreateForCompositorWidget(
return sGLContextProviderX11.CreateForCompositorWidget(
aCompositorWidget, aHardwareWebRender, aForceAccelerated);
}
}
@ -32,28 +42,28 @@ already_AddRefed<GLContext> GLContextProviderWayland::CreateForCompositorWidget(
/*static*/
already_AddRefed<GLContext> GLContextProviderWayland::CreateHeadless(
const GLContextCreateDesc& desc, nsACString* const out_failureId) {
if (GdkIsX11Display()) {
return sGLContextProviderX11.CreateHeadless(desc, out_failureId);
} else {
if (GdkIsWaylandDisplay()) {
return sGLContextProviderEGL.CreateHeadless(desc, out_failureId);
} else {
return sGLContextProviderX11.CreateHeadless(desc, out_failureId);
}
}
/*static*/
GLContext* GLContextProviderWayland::GetGlobalContext() {
if (GdkIsX11Display()) {
return sGLContextProviderX11.GetGlobalContext();
} else {
if (GdkIsWaylandDisplay()) {
return sGLContextProviderEGL.GetGlobalContext();
} else {
return sGLContextProviderX11.GetGlobalContext();
}
}
/*static*/
void GLContextProviderWayland::Shutdown() {
if (GdkIsX11Display()) {
sGLContextProviderX11.Shutdown();
} else {
if (GdkIsWaylandDisplay()) {
sGLContextProviderEGL.Shutdown();
} else {
sGLContextProviderX11.Shutdown();
}
}

Просмотреть файл

@ -15,8 +15,6 @@
#include "mozilla/dom/ContentChild.h"
#include "mozilla/gfx/Swizzle.h"
#include "mozilla/ipc/ByteBuf.h"
#include "mozilla/Preferences.h"
#include "mozilla/StaticPrefs_dom.h"
#include <algorithm>
#include <gio/gio.h>
@ -407,8 +405,7 @@ nsresult nsIconChannel::Init(nsIURI* aURI) {
nsCOMPtr<nsIInputStream> stream;
using ContentChild = mozilla::dom::ContentChild;
auto* contentChild = ContentChild::GetSingleton();
if (contentChild && mozilla::StaticPrefs::dom_ipc_remote_mozIcon()) {
if (auto* contentChild = ContentChild::GetSingleton()) {
// Get the icon via IPC and translate the promise of a ByteBuf
// into an actually-existing channel.
RefPtr<ContentChild::GetSystemIconPromise> icon =

Просмотреть файл

@ -6,18 +6,6 @@
add_task(async function test_mozicon_file_no_sandbox() {
assertFileProcess();
assertMozIconIsRemote();
await createMozIconInFile("txt");
await createMozIconInFile("exe");
await createMozIconInFile("non-existent-bidule");
});
add_task(async function test_mozicon_file_no_sandbox_no_remote() {
await SpecialPowers.pushPrefEnv({
set: [["dom.ipc.remote-mozIcon", false]],
});
assertFileProcess();
assertMozIconIsNotRemote();
await createMozIconInFile("txt");
await createMozIconInFile("exe");
await createMozIconInFile("non-existent-bidule");

Просмотреть файл

@ -11,17 +11,3 @@ add_task(async function test_mozicon_file_with_sandbox() {
await createMozIconInFile("exe");
await createMozIconInFile("non-existent-bidule");
});
// https://bugzilla.mozilla.org/show_bug.cgi?id=1695381#c0
// with sandbox and no remote enabled, this is expected to fail
add_task(async function test_mozicon_file_with_sandbox_no_remote() {
await SpecialPowers.pushPrefEnv({
set: [["dom.ipc.remote-mozIcon", false]],
});
assertFileProcess();
assertSandboxHeadless();
assertMozIconIsNotRemote();
await createMozIconInFile("txt", false);
await createMozIconInFile("exe", false);
await createMozIconInFile("non-existent-bidule", false);
});

Просмотреть файл

@ -3,10 +3,6 @@ support-files =
head.js
prefs =
security.sandbox.content.headless=true
skip-if =
(os != 'linux') # the pref is only used on linux
tsan # timeout on test_mozicon_file_with_sandbox_no_remote
asan # timeout on test_mozicon_file_with_sandbox_no_remote
ccov # timeout on test_mozicon_file_with_sandbox_no_remote
skip-if = (os != 'linux') # the pref is only used on linux
[browser_mozicon_file_sandbox_headless.js]

Просмотреть файл

@ -42,14 +42,6 @@ function assertSandboxHeadless() {
assertPrefVal("security.sandbox.content.headless", true);
}
function assertMozIconIsRemote() {
assertPrefVal("dom.ipc.remote-mozIcon", true);
}
function assertMozIconIsNotRemote() {
assertPrefVal("dom.ipc.remote-mozIcon", false);
}
function getPage() {
let filePage = undefined;
const { Services } = ChromeUtils.import(

Просмотреть файл

@ -603,6 +603,13 @@ uint32_t GeckoChildProcessHost::sNextUniqueID = 1;
/* static */
uint32_t GeckoChildProcessHost::GetUniqueID() { return sNextUniqueID++; }
/* static */
void GeckoChildProcessHost::SetEnv(const char* aKey, const char* aValue) {
MOZ_ASSERT(mLaunchOptions);
mLaunchOptions->env_map[ENVIRONMENT_STRING(aKey)] =
ENVIRONMENT_STRING(aValue);
}
void GeckoChildProcessHost::PrepareLaunch() {
if (CrashReporter::GetEnabled()) {
CrashReporter::OOPInit();

Просмотреть файл

@ -84,6 +84,10 @@ class GeckoChildProcessHost : public ChildProcessHost,
static uint32_t GetUniqueID();
// Call this before launching to set an environment variable for the
// child process. The arguments must be UTF-8.
void SetEnv(const char* aKey, const char* aValue);
// Does not block. The IPC channel may not be initialized yet, and
// the child process may or may not have been created when this
// method returns.

Просмотреть файл

@ -2261,12 +2261,6 @@
value: 500
mirror: always
# Temporary pref to allow disabling remoting of MozIcon
- name: dom.ipc.remote-mozIcon
type: bool
value: true
mirror: always
#ifdef MOZ_ENABLE_FORKSERVER
- name: dom.ipc.forkserver.enable
type: bool
@ -2274,6 +2268,21 @@
mirror: once
#endif
#ifdef MOZ_WIDGET_GTK
#
# Avoid the use of GTK in content processes if possible, by running
# them in headless mode, to conserve resources (e.g., connections to
# the X server). See the usage in `ContentParent.cpp` for the full
# definition of "if possible".
#
# This does not affect sandbox policies; content processes may still
# dynamically connect to the display server for, e.g., WebGL.
- name: dom.ipc.avoid-gtk
type: bool
value: true
mirror: always
#endif
# Whether or not to collect a paired minidump when force-killing a
# content process.
- name: dom.ipc.tabs.createKillHardCrashReports

Просмотреть файл

@ -34,8 +34,6 @@ const kStrictKeyPressEvents =
SpecialPowers.getBoolPref("dom.keyboardevent.keypress.dispatch_non_printable_keys_only_system_group_in_content");
const kStrictKeyDownKeyUpEvents =
SpecialPowers.getBoolPref("dom.keyboardevent.dispatch_during_composition");
const kIsHeadless =
SpecialPowers.Cc["@mozilla.org/gfx/info;1"].getService(SpecialPowers.Ci.nsIGfxInfo).isHeadless;
info("\nProfile::EventUtilsLoadTime: " + (loadTime - start) + "\n");
function starttest() {
@ -48,6 +46,10 @@ function starttest() {
check = true;
}
const kIsHeadless = await SpecialPowers.spawnChrome([], () => {
return Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo).isHeadless;
});
if (navigator.appVersion.includes("Android")) {
// This is the workaround for test failure on debug build.
await SpecialPowers.pushPrefEnv({set: [["apz.zoom-to-focused-input.enabled", false]]});