Bug 1337062 - Transfer initial gfxVars over command line - r?blassey
When a subprocess is launched, gfxVars updates (for non-default values) are
serialized and passed on the command line, up to a limit of 1023 characters,
and ensuring it should not overflow the command line size.
When the child starts, the command line parameter is given to gfxVars, so the
updates can be used during gfxVars::Initialize(), instead of doing a sync
request to the parent.
In case the updates are not sent, or in the unlikely case the child cannot
parse them, we fallback to the sync request -- The former case should be rare
enough that a slow sync request is acceptable: It should only happen if D3D
block-list is *modified* (most people would either use the default, or just
overwrite these prefs with short strings.)
MozReview-Commit-ID: 6MoJC0fe59Q
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -2019,16 +2019,45 @@ ContentParent::LaunchSubprocess(ProcessP
extraArgs.push_back("-intPrefs");
extraArgs.push_back(intPrefs.get());
extraArgs.push_back("-boolPrefs");
extraArgs.push_back(boolPrefs.get());
extraArgs.push_back("-stringPrefs");
extraArgs.push_back(stringPrefs.get());
+ char gfxBuf[1024];
+ nsFixedCString gfxVarsUpdates(gfxBuf, 1024, 0);
+ if (gfxVars::SerializeNonDefaultVarUpdates(gfxVarsUpdates, 1023)) {
+ // Don't add gfxVarsUpdates if they risk taking too much command line space.
+ const size_t maxArg =
+#ifdef XP_WIN
+ 8191;
+#else
+ size_t(sysconf(_SC_ARG_MAX));
+#endif
+ size_t extraArgsLength = 0;
+ for (const std::string& arg : extraArgs) {
+ extraArgsLength += arg.size() + 1;
+ }
+ if (extraArgsLength + size_t(gfxVarsUpdates.Length()) + 1024 < maxArg) {
+ extraArgs.push_back("-gfxVars");
+ if (gfxVarsUpdates.IsEmpty()) {
+ // Don't pass an empty string.
+ extraArgs.push_back("0");
+ } else {
+ extraArgs.push_back(gfxVarsUpdates.get());
+ }
+
+ // Since we have sent all gfxVarsUpdates known so far, we can start
+ // listening for updates.
+ gfxVars::AddReceiver(this);
+ }
+ }
+
if (gSafeMode) {
extraArgs.push_back("-safeMode");
}
if (!mSubprocess->LaunchAndWaitForProcessHandle(extraArgs)) {
MarkAsDead();
return false;
}
--- a/dom/ipc/ContentProcess.cpp
+++ b/dom/ipc/ContentProcess.cpp
@@ -15,16 +15,18 @@
#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
#include "mozilla/Preferences.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsDirectoryService.h"
#include "nsDirectoryServiceDefs.h"
#endif
+#include "mozilla/gfx/gfxVars.h"
+
using mozilla::ipc::IOThreadChild;
namespace mozilla {
namespace dom {
#if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
static bool
IsSandboxTempDirRequired()
@@ -198,18 +200,24 @@ ContentProcess::Init(int aArgc, char* aA
MaybePrefValue value(PrefValue(nsCString(str, length)));
PrefSetting pref(nsCString(ContentPrefs::GetContentPref(index)), value, MaybePrefValue());
prefsArray.AppendElement(pref);
str += length + 1;
MOZ_ASSERT(*(str - 1) == '|');
}
SET_PREF_PHASE(END_INIT_PREFS);
foundStringPrefs = true;
- }
- else if (!strcmp(aArgv[idx], "-safeMode")) {
+ } else if (!strcmp(aArgv[idx], "-gfxVars")) {
+ SET_PREF_PHASE(BEGIN_INIT_PREFS);
+ char* str = aArgv[idx + 1];
+ if (str[0] != '0' || str[1] != '\0') {
+ gfxVars::GotSerializedInitialVarUpdates(aArgv[idx + 1]);
+ }
+ SET_PREF_PHASE(END_INIT_PREFS);
+ } else if (!strcmp(aArgv[idx], "-safeMode")) {
gSafeMode = true;
}
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
else if (!strcmp(aArgv[idx], "-profile")) {
MOZ_ASSERT(!foundProfile);
if (foundProfile) {
continue;
--- a/gfx/config/gfxVars.cpp
+++ b/gfx/config/gfxVars.cpp
@@ -2,43 +2,49 @@
/* vim: set sts=2 ts=8 sw=2 tw=99 et: */
/* 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/. */
#include "gfxVars.h"
#include "gfxVarReceiver.h"
#include "mozilla/dom/ContentChild.h"
+#include "nsPrintfCString.h"
+
namespace mozilla {
namespace gfx {
StaticAutoPtr<gfxVars> gfxVars::sInstance;
StaticAutoPtr<nsTArray<gfxVars::VarBase*>> gfxVars::sVarList;
+const char* gfxVars::sSerializedInitialVarUpdates = nullptr;
-void
+/* static */ void
gfxVars::Initialize()
{
if (sInstance) {
return;
}
// sVarList must be initialized first since it's used in the constructor for
// sInstance.
sVarList = new nsTArray<gfxVars::VarBase*>();
sInstance = new gfxVars;
// Like Preferences, we want content to synchronously get initial data on
// init. Note the GPU process is not handled here - it cannot send sync
// messages, so instead the initial data is pushed down.
if (XRE_IsContentProcess()) {
- InfallibleTArray<GfxVarUpdate> vars;
- dom::ContentChild::GetSingleton()->SendGetGfxVars(&vars);
- for (const auto& var : vars) {
- ApplyUpdate(var);
+ if (!ApplySerializedVarUpdates(sSerializedInitialVarUpdates)) {
+ // No initial updates, or could not apply them -> Try sync retrieval.
+ InfallibleTArray<GfxVarUpdate> vars;
+ dom::ContentChild::GetSingleton()->SendGetGfxVars(&vars);
+ for (const auto& var : vars) {
+ ApplyUpdate(var);
+ }
}
}
}
gfxVars::gfxVars()
{
}
@@ -96,16 +102,193 @@ gfxVars::FetchNonDefaultVars()
var->GetValue(&value);
updates.AppendElement(GfxVarUpdate(i, value));
}
return updates;
}
+/* static */ bool
+gfxVars::SerializeNonDefaultVarUpdates(nsACString& aUpdates, size_t aMax)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!sVarList) {
+ return false;
+ }
+
+ for (size_t i = 0; i < sVarList->Length(); i++) {
+ VarBase* var = sVarList->ElementAt(i);
+ if (var->HasDefaultValue()) {
+ continue;
+ }
+
+ GfxVarValue value;
+ var->GetValue(&value);
+
+ switch (value.type()) {
+ case mozilla::gfx::GfxVarValue::TBackendType: {
+ nsPrintfCString t("T%zu:%d|", i, int(value.get_BackendType()));
+ if (aUpdates.Length() + t.Length() > aMax) {
+ return false;
+ }
+ aUpdates.Append(t);
+ }
+ break;
+ case mozilla::gfx::GfxVarValue::Tbool: {
+ nsPrintfCString b("B%zu:%d|",
+ i, int(value.get_bool()));
+ if (aUpdates.Length() + b.Length() > aMax) {
+ return false;
+ }
+ aUpdates.Append(b);
+ }
+ break;
+ case mozilla::gfx::GfxVarValue::TgfxImageFormat: {
+ nsPrintfCString f("F%zu:%d|", i, int(value.get_gfxImageFormat()));
+ if (aUpdates.Length() + f.Length() > aMax) {
+ return false;
+ }
+ aUpdates.Append(f);
+ }
+ break;
+ case mozilla::gfx::GfxVarValue::TIntSize: {
+ nsPrintfCString z("Z%zu:%dx%d|",
+ i,
+ int(value.get_IntSize().width),
+ int(value.get_IntSize().height));
+ if (aUpdates.Length() + z.Length() > aMax) {
+ return false;
+ }
+ aUpdates.Append(z);
+ }
+ break;
+ case mozilla::gfx::GfxVarValue::TnsCString: {
+ nsPrintfCString s("S%zu:%u;%s|",
+ i,
+ value.get_nsCString().Length(),
+ value.get_nsCString().get());
+ if (aUpdates.Length() + s.Length() > aMax) {
+ return false;
+ }
+ aUpdates.Append(s);
+ }
+ break;
+ default:
+ MOZ_ASSERT(false);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* static */ void
+gfxVars::GotSerializedInitialVarUpdates(const char* aUpdates)
+{
+ sSerializedInitialVarUpdates = aUpdates;
+}
+
+/* static */ bool
+gfxVars::ApplySerializedVarUpdates(const char* aUpdates)
+{
+ if (!aUpdates) {
+ return false;
+ }
+
+ for (const char* p = aUpdates; *p;) {
+ switch (*p++) {
+ case 'T': {
+ int32_t index = strtol(p, const_cast<char**>(&p), 10);
+ if (*p != ':') {
+ return false;
+ }
+ p++;
+ int32_t type = strtol(p, const_cast<char**>(&p), 10);
+ if (*p != '|') {
+ return false;
+ }
+ p++;
+ ApplyUpdate(GfxVarUpdate(index, GfxVarValue(BackendType(type))));
+ }
+ break;
+ case 'B': {
+ int32_t index = strtol(p, const_cast<char**>(&p), 10);
+ if (*p != ':') {
+ return false;
+ }
+ p++;
+ int32_t b = strtol(p, const_cast<char**>(&p), 10);
+ if (*p != '|') {
+ return false;
+ }
+ p++;
+ ApplyUpdate(GfxVarUpdate(index, GfxVarValue(bool(b))));
+ }
+ break;
+ case 'F': {
+ int32_t index = strtol(p, const_cast<char**>(&p), 10);
+ if (*p != ':') {
+ return false;
+ }
+ p++;
+ int32_t format = strtol(p, const_cast<char**>(&p), 10);
+ if (*p != '|') {
+ return false;
+ }
+ p++;
+ ApplyUpdate(GfxVarUpdate(index, GfxVarValue(gfxImageFormat(format))));
+ }
+ break;
+ case 'Z': {
+ int32_t index = strtol(p, const_cast<char**>(&p), 10);
+ if (*p != ':') {
+ return false;
+ }
+ p++;
+ int32_t width = strtol(p, const_cast<char**>(&p), 10);
+ if (*p != 'x') {
+ return false;
+ }
+ p++;
+ int32_t height = strtol(p, const_cast<char**>(&p), 10);
+ if (*p != '|') {
+ return false;
+ }
+ p++;
+ ApplyUpdate(GfxVarUpdate(index, GfxVarValue(IntSize(width, height))));
+ }
+ break;
+ case 'S': {
+ int32_t index = strtol(p, const_cast<char**>(&p), 10);
+ if (*p != ':') {
+ return false;
+ }
+ p++;
+ int32_t length = strtol(p, const_cast<char**>(&p), 10);
+ if (*p != ';') {
+ return false;
+ }
+ p++;
+ ApplyUpdate(GfxVarUpdate(index, GfxVarValue(nsCString(p, length))));
+ p += length;
+ if (*p++ != '|') {
+ return false;
+ }
+ }
+ break;
+ default:
+ return false;
+ }
+ }
+
+ return true;
+}
+
gfxVars::VarBase::VarBase()
{
mIndex = gfxVars::sVarList->Length();
gfxVars::sVarList->AppendElement(this);
}
void
gfxVars::NotifyReceivers(VarBase* aVar)
--- a/gfx/config/gfxVars.h
+++ b/gfx/config/gfxVars.h
@@ -58,16 +58,22 @@ public:
static void Shutdown();
static void ApplyUpdate(const GfxVarUpdate& aUpdate);
static void AddReceiver(gfxVarReceiver* aReceiver);
static void RemoveReceiver(gfxVarReceiver* aReceiver);
// Return a list of updates for all variables with non-default values.
static nsTArray<GfxVarUpdate> FetchNonDefaultVars();
+ // Fill aUpdates with serialized updates for all variables with non-default
+ // values. Return true if ok, false if length>aMax or other issue.
+ static bool SerializeNonDefaultVarUpdates(nsACString& aUpdates, size_t aMax);
+ // Inform gfxVars that some initial updates are available, to be used from
+ // a child's Initialize(), instead of doing a sync request to the parent.
+ static void GotSerializedInitialVarUpdates(const char* aUpdates);
public:
// Each variable must expose Set and Get methods for IPDL.
class VarBase
{
public:
VarBase();
virtual void SetValue(const GfxVarValue& aValue) = 0;
@@ -78,16 +84,17 @@ public:
}
private:
size_t mIndex;
};
private:
static StaticAutoPtr<gfxVars> sInstance;
static StaticAutoPtr<nsTArray<VarBase*>> sVarList;
+ static const char* sSerializedInitialVarUpdates;
template <typename T, T Default()>
class VarImpl final : public VarBase
{
public:
VarImpl()
: mValue(Default())
{}
@@ -133,16 +140,18 @@ public:
}
GFX_VARS_LIST(GFX_VAR_DECL)
#undef GFX_VAR_DECL
private:
gfxVars();
+ static bool ApplySerializedVarUpdates(const char* aUpdates);
+
void NotifyReceivers(VarBase* aVar);
private:
nsTArray<gfxVarReceiver*> mReceivers;
};
#undef GFX_VARS_LIST