Bug 1406146 - Support a simple WindowProxy in the jsshell. r=jandem
authorTed Campbell <tcampbell@mozilla.com>
Wed, 10 Apr 2019 13:11:57 +0000
changeset 468796 dc43810c71fd7969ddae1f9cf4fc35736c50ba1b
parent 468795 fd75d6285f74bf868368020ce0ec37f81911705d
child 468797 32cbcac2b40a061701bb8a9ab9fb5c6d47f30d37
push id35850
push userdvarga@mozilla.com
push dateWed, 10 Apr 2019 21:52:56 +0000
treeherdermozilla-central@9d3dbe3fef26 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1406146
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1406146 - Support a simple WindowProxy in the jsshell. r=jandem This adds an option to the 'newGlobal' shell command to create globals that have a simple WindowProxy. This is intended for testing code that distinguishes between GlobalObject and WindowProxy. No typical window behavior such as navigation will be supported. Differential Revision: https://phabricator.services.mozilla.com/D26837
js/src/shell/js.cpp
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -528,18 +528,46 @@ static bool OOM_printAllocationCount = f
 static bool SetTimeoutValue(JSContext* cx, double t);
 
 static void KillWatchdog(JSContext* cx);
 
 static bool ScheduleWatchdog(JSContext* cx, double t);
 
 static void CancelExecution(JSContext* cx);
 
+enum class ShellGlobalKind {
+  GlobalObject,
+  WindowProxy,
+};
+
 static JSObject* NewGlobalObject(JSContext* cx, JS::RealmOptions& options,
-                                 JSPrincipals* principals);
+                                 JSPrincipals* principals,
+                                 ShellGlobalKind kind);
+
+/*
+ * A toy WindowProxy class for the shell. This is intended for testing code
+ * where global |this| is a WindowProxy. All requests are forwarded to the
+ * underlying global and no navigation is supported.
+ */
+const js::Class ShellWindowProxyClass =
+    PROXY_CLASS_DEF("ShellWindowProxy", JSCLASS_HAS_RESERVED_SLOTS(1));
+
+JSObject* NewShellWindowProxy(JSContext* cx, JS::HandleObject global) {
+  MOZ_ASSERT(global->is<GlobalObject>());
+
+  js::WrapperOptions options;
+  options.setClass(&ShellWindowProxyClass);
+  options.setSingleton(true);
+
+  JSAutoRealm ar(cx, global);
+  JSObject* obj =
+      js::Wrapper::New(cx, global, &js::Wrapper::singleton, options);
+  MOZ_ASSERT_IF(obj, js::IsWindowProxy(obj));
+  return obj;
+}
 
 /*
  * A toy principals type for the shell.
  *
  * In the shell, a principal is simply a 32-bit mask: P subsumes Q if the
  * set bits in P are a superset of those in Q. Thus, the principal 0 is
  * subsumed by everything, and the principal ~0 subsumes everything.
  *
@@ -3997,29 +4025,32 @@ static void WorkerMain(WorkerInput* inpu
   SetWorkerContextOptions(cx);
 
   JS_SetFutexCanWait(cx);
   JS::SetWarningReporter(cx, WarningReporter);
   js::SetPreserveWrapperCallback(cx, DummyPreserveWrapperCallback);
   JS_InitDestroyPrincipalsCallback(cx, ShellPrincipals::destroy);
   JS_SetDestroyCompartmentCallback(cx, DestroyShellCompartmentPrivate);
 
+  js::SetWindowProxyClass(cx, &ShellWindowProxyClass);
+
   js::UseInternalJobQueues(cx);
 
   if (!JS::InitSelfHostedCode(cx)) {
     return;
   }
 
   EnvironmentPreparer environmentPreparer(cx);
 
   do {
-    JS::RealmOptions compartmentOptions;
-    SetStandardRealmOptions(compartmentOptions);
-
-    RootedObject global(cx, NewGlobalObject(cx, compartmentOptions, nullptr));
+    JS::RealmOptions realmOptions;
+    SetStandardRealmOptions(realmOptions);
+
+    RootedObject global(cx, NewGlobalObject(cx, realmOptions, nullptr,
+                                            ShellGlobalKind::GlobalObject));
     if (!global) {
       break;
     }
 
     JSAutoRealm ar(cx, global);
 
     JS::CompileOptions options(cx);
     options.setFileAndLine("<string>", 1).setIsRunOnce(true);
@@ -6112,16 +6143,17 @@ static bool WrapWithProto(JSContext* cx,
 }
 
 static bool NewGlobal(JSContext* cx, unsigned argc, Value* vp) {
   JSPrincipals* principals = nullptr;
 
   JS::RealmOptions options;
   JS::RealmCreationOptions& creationOptions = options.creationOptions();
   JS::RealmBehaviors& behaviors = options.behaviors();
+  ShellGlobalKind kind = ShellGlobalKind::GlobalObject;
 
   SetStandardRealmOptions(options);
 
   // Default to creating the global in the current compartment unless
   // --more-compartments is used.
   if (defaultToSameCompartment) {
     creationOptions.setExistingCompartment(cx->global());
   } else {
@@ -6171,16 +6203,24 @@ static bool NewGlobal(JSContext* cx, uns
 
     if (!JS_GetProperty(cx, opts, "disableLazyParsing", &v)) {
       return false;
     }
     if (v.isBoolean()) {
       behaviors.setDisableLazyParsing(v.toBoolean());
     }
 
+    if (!JS_GetProperty(cx, opts, "useWindowProxy", &v)) {
+      return false;
+    }
+    if (v.isBoolean()) {
+      kind = v.toBoolean() ? ShellGlobalKind::WindowProxy
+                           : ShellGlobalKind::GlobalObject;
+    }
+
     if (!JS_GetProperty(cx, opts, "enableBigInt", &v)) {
       return false;
     }
     if (v.isBoolean()) {
       creationOptions.setBigIntEnabled(v.toBoolean());
     }
 
     if (!JS_GetProperty(cx, opts, "systemPrincipal", &v)) {
@@ -6206,29 +6246,30 @@ static bool NewGlobal(JSContext* cx, uns
       JS_HoldPrincipals(principals);
     }
   }
 
   if (!CheckRealmOptions(cx, options, principals)) {
     return false;
   }
 
-  RootedObject global(cx, NewGlobalObject(cx, options, principals));
+  RootedObject global(cx, NewGlobalObject(cx, options, principals, kind));
   if (principals) {
     JS_DropPrincipals(cx, principals);
   }
   if (!global) {
     return false;
   }
 
-  if (!JS_WrapObject(cx, &global)) {
-    return false;
-  }
-
-  args.rval().setObject(*global);
+  RootedObject wrapped(cx, ToWindowProxyIfWindow(global));
+  if (!JS_WrapObject(cx, &wrapped)) {
+    return false;
+  }
+
+  args.rval().setObject(*wrapped);
   return true;
 }
 
 static bool NukeCCW(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   if (args.length() != 1 || !args[0].isObject() ||
       !IsCrossCompartmentWrapper(&args[0].toObject())) {
@@ -8750,16 +8791,18 @@ JS_FN_HELP("parseBin", BinParse, 1, 0,
 "      cloneSingletons: If true, always clone the objects baked into\n"
 "         scripts, even if it's a top-level script that will only run once\n"
 "         (defaults to using them directly in scripts that will only run\n"
 "         once).\n"
 "      invisibleToDebugger: If true, the global will be invisible to the\n"
 "         debugger (default false)\n"
 "      disableLazyParsing: If true, don't create lazy scripts for functions\n"
 "         (default false).\n"
+"      useWindowProxy: the global will be created with a WindowProxy attached. In this\n"
+"          case, the WindowProxy will be returned.\n"
 "      principal: if present, its value converted to a number must be an\n"
 "         integer that fits in 32 bits; use that as the new realm's\n"
 "         principal. Shell principals are toys, meant only for testing; one\n"
 "         shell principal subsumes another if its set bits are a superset of\n"
 "         the other's. Thus, a principal of 0 subsumes nothing, while a\n"
 "         principals of ~0 subsumes all other principals. The absence of a\n"
 "         principal is treated as if its bits were 0xffff, for subsumption\n"
 "         purposes. If this property is omitted, supply no principal.\n"
@@ -9821,27 +9864,36 @@ static bool TimesAccessed(JSContext* cx,
   args.rval().setInt32(++accessed);
   return true;
 }
 
 static const JSPropertySpec TestingProperties[] = {
     JS_PSG("timesAccessed", TimesAccessed, 0), JS_PS_END};
 
 static JSObject* NewGlobalObject(JSContext* cx, JS::RealmOptions& options,
-                                 JSPrincipals* principals) {
+                                 JSPrincipals* principals,
+                                 ShellGlobalKind kind) {
   RootedObject glob(cx,
                     JS_NewGlobalObject(cx, &global_class, principals,
                                        JS::DontFireOnNewGlobalHook, options));
   if (!glob) {
     return nullptr;
   }
 
   {
     JSAutoRealm ar(cx, glob);
 
+    if (kind == ShellGlobalKind::WindowProxy) {
+      RootedObject proxy(cx, NewShellWindowProxy(cx, glob));
+      if (!proxy) {
+        return nullptr;
+      }
+      js::SetWindowProxy(cx, glob, proxy);
+    }
+
 #ifndef LAZY_STANDARD_CLASSES
     if (!JS::InitRealmStandardClasses(cx)) {
       return nullptr;
     }
 #endif
 
     bool succeeded;
     if (!JS_SetImmutablePrototype(cx, glob, &succeeded)) {
@@ -10664,17 +10716,18 @@ static int Shell(JSContext* cx, OptionPa
   }
 
   if (op->getBoolOption("more-compartments")) {
     defaultToSameCompartment = false;
   }
 
   JS::RealmOptions options;
   SetStandardRealmOptions(options);
-  RootedObject glob(cx, NewGlobalObject(cx, options, nullptr));
+  RootedObject glob(
+      cx, NewGlobalObject(cx, options, nullptr, ShellGlobalKind::GlobalObject));
   if (!glob) {
     return 1;
   }
 
   JSAutoRealm ar(cx, glob);
 
   ShellContext* sc = GetShellContext(cx);
   int result = EXIT_SUCCESS;
@@ -11222,16 +11275,18 @@ int main(int argc, char** argv, char** e
     JS_SetGCParametersBasedOnAvailableMemory(cx, availMem);
   }
 
   JS_SetTrustedPrincipals(cx, &ShellPrincipals::fullyTrusted);
   JS_SetSecurityCallbacks(cx, &ShellPrincipals::securityCallbacks);
   JS_InitDestroyPrincipalsCallback(cx, ShellPrincipals::destroy);
   JS_SetDestroyCompartmentCallback(cx, DestroyShellCompartmentPrivate);
 
+  js::SetWindowProxyClass(cx, &ShellWindowProxyClass);
+
   JS_AddInterruptCallback(cx, ShellInterruptCallback);
 
   bufferStreamState = js_new<ExclusiveWaitableData<BufferStreamState>>(
       mutexid::BufferStreamState);
   if (!bufferStreamState) {
     return 1;
   }
   auto shutdownBufferStreams = MakeScopeExit([] {