Bug 1337062 - Transfer initial gfxVars over command line - r?blassey draft
authorGerald Squelart <gsquelart@mozilla.com>
Tue, 28 Mar 2017 12:16:41 +1100
changeset 552850 686babed65fc76e8b9d885b855b0bfcd2fb2a009
parent 552691 272ce6c2572164f5f6a9fba2a980ba9ccf50770c
child 621933 ab2a5405f32320e40d545ef8b59083c094cf94d9
push id51492
push usergsquelart@mozilla.com
push dateWed, 29 Mar 2017 04:24:47 +0000
reviewersblassey
bugs1337062
milestone55.0a1
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
dom/ipc/ContentParent.cpp
dom/ipc/ContentProcess.cpp
gfx/config/gfxVars.cpp
gfx/config/gfxVars.h
--- 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