dawn/node: Fetch and use node 16.13

Fixed: dawn:1329
Change-Id: Ifbc97961f1cafa804908d24d61da72ce0f59dae0
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/85061
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/.gitignore b/.gitignore
index b22f66c..1ab427f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,7 @@
 /third_party/jsoncpp
 /third_party/llvm-build
 /third_party/markupsafe
+/third_party/node
 /third_party/node-addon-api
 /third_party/node-api-headers
 /third_party/swiftshader
diff --git a/DEPS b/DEPS
index 1c4c98a..478a3ea 100644
--- a/DEPS
+++ b/DEPS
@@ -19,6 +19,11 @@
   'dawn_gn_version': 'git_revision:bd99dbf98cbdefe18a4128189665c5761263bcfb',
   'dawn_go_version': 'version:1.16',
 
+  'node_darwin_arm64_sha': '31859fc1fa0994a95f44f09c367d6ff63607cfde',
+  'node_darwin_x64_sha': '16dfd094763b71988933a31735f9dea966f9abd6',
+  'node_linux_x64_sha': 'ab9544e24e752d3d17f335fb7b2055062e582d11',
+  'node_win_x64_sha': '5ef847033c517c499f56f9d136d159b663bab717',
+
   # GN variable required by //testing that will be output in the gclient_args.gni
   'generate_location_tags': False,
 }
@@ -158,7 +163,7 @@
     'condition': 'build_with_chromium',
   },
 
-  # Dependencies required to build Dawn NodeJS bindings
+  # Dependencies required to build / run Dawn NodeJS bindings
   'third_party/node-api-headers': {
     'url': '{github_git}/nodejs/node-api-headers.git@d68505e4055ecb630e14c26c32e5c2c65e179bba',
     'condition': 'dawn_node',
@@ -320,6 +325,60 @@
                 'tools/cmake-win32/',
     ],
   },
+
+  # Node binaries, when dawn_node is enabled
+  {
+    'name': 'node_linux64',
+    'pattern': '.',
+    'condition': 'dawn_node and host_os == "linux"',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--extract',
+                '--no_auth',
+                '--bucket', 'chromium-nodejs/16.13.0',
+                Var('node_linux_x64_sha'),
+                '-o', 'third_party/node/node-linux-x64.tar.gz',
+    ],
+  },
+  {
+    'name': 'node_mac',
+    'pattern': '.',
+    'condition': 'dawn_node and host_os == "mac"',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--extract',
+                '--no_auth',
+                '--bucket', 'chromium-nodejs/16.13.0',
+                Var('node_darwin_x64_sha'),
+                '-o', 'third_party/node/node-darwin-x64.tar.gz',
+    ],
+  },
+  {
+    'name': 'node_mac_arm64',
+    'pattern': '.',
+    'condition': 'dawn_node and host_os == "mac"',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--extract',
+                '--no_auth',
+                '--bucket', 'chromium-nodejs/16.13.0',
+                Var('node_darwin_arm64_sha'),
+                '-o', 'third_party/node/node-darwin-arm64.tar.gz',
+    ],
+  },
+  {
+    'name': 'node_win',
+    'pattern': '.',
+    'condition': 'dawn_node and host_os == "win"',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--no_auth',
+                '--bucket', 'chromium-nodejs/16.13.0',
+                Var('node_win_x64_sha'),
+                '-o', 'third_party/node/node.exe',
+    ],
+  },
+
 ]
 
 recursedeps = [
diff --git a/src/dawn/node/tools/src/cmd/run-cts/main.go b/src/dawn/node/tools/src/cmd/run-cts/main.go
index bafb13c..2ac7ddf 100644
--- a/src/dawn/node/tools/src/cmd/run-cts/main.go
+++ b/src/dawn/node/tools/src/cmd/run-cts/main.go
@@ -135,7 +135,7 @@
 	var flags dawnNodeFlags
 	flag.StringVar(&dawnNode, "dawn-node", "", "path to dawn.node module")
 	flag.StringVar(&cts, "cts", "", "root directory of WebGPU CTS")
-	flag.StringVar(&node, "node", "", "path to node executable")
+	flag.StringVar(&node, "node", defaultNodePath(), "path to node executable")
 	flag.StringVar(&npx, "npx", "", "path to npx executable")
 	flag.StringVar(&resultsPath, "output", "", "path to write test results file")
 	flag.StringVar(&expectationsPath, "expect", "", "path to expectations file")
@@ -188,11 +188,7 @@
 
 	// Find node
 	if node == "" {
-		var err error
-		node, err = exec.LookPath("node")
-		if err != nil {
-			return fmt.Errorf("add node to PATH or specify with --node")
-		}
+		return fmt.Errorf("cannot find path to node. Specify with --node")
 	}
 	// Find npx
 	if npx == "" {
@@ -1073,3 +1069,44 @@
 
 	return nil
 }
+
+// defaultNodePath looks for the node binary, first in dawn's third_party
+// directory, falling back to PATH. This is used as the default for the --node
+// command line flag.
+func defaultNodePath() string {
+	if dir := thisDir(); dir != "" {
+		node := filepath.Join(dir, "../../../../../../../third_party/node")
+		if info, err := os.Stat(node); err == nil && info.IsDir() {
+			path := ""
+			switch fmt.Sprintf("%v/%v", runtime.GOOS, runtime.GOARCH) { // See `go tool dist list`
+			case "darwin/amd64":
+				path = filepath.Join(node, "node-darwin-x64/bin/node")
+			case "darwin/arm64":
+				path = filepath.Join(node, "node-darwin-arm64/bin/node")
+			case "linux/amd64":
+				path = filepath.Join(node, "node-linux-x64/bin/node")
+			case "windows/amd64":
+				path = filepath.Join(node, "node.exe")
+			}
+			if _, err := os.Stat(path); err == nil {
+				return path
+			}
+		}
+	}
+
+	if path, err := exec.LookPath("node"); err == nil {
+		return path
+	}
+
+	return ""
+}
+
+// thisDir returns the path to the directory that holds the .go file of the
+// caller function
+func thisDir() string {
+	_, file, _, ok := runtime.Caller(1)
+	if !ok {
+		return ""
+	}
+	return filepath.Dir(file)
+}