blink: Support blocking rendering until Document parsing.

Add a `blocking` attribute to the HTML element so authors can choose to
block rendering until the complete Document has been parsed.

Design:
https://docs.google.com/document/d/1MrWyy1VVa3WxTtG-uJhcPNohrWFApHmY4PRbgKKd2hg/edit?pli=1

Bug: 1470393
Change-Id: I30eb8e71cf36dcfd4355d0a1bd76f32d5a8ff061
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4753436
Auto-Submit: Khushal Sagar <khushalsagar@chromium.org>
Reviewed-by: Mason Freed <masonf@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1182742}
diff --git a/html/dom/render-blocking/document-render-blocking-partial.tentative.html b/html/dom/render-blocking/document-render-blocking-partial.tentative.html
new file mode 100644
index 0000000..89ab05e
--- /dev/null
+++ b/html/dom/render-blocking/document-render-blocking-partial.tentative.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+  <html blocking=render>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="support/utils.js"></script>
+  <title>`blocking=render` defers frames until the attribute is set</title>
+  <script>
+    assert_implements(document.documentElement.blocking, "no blocking attribute");
+
+    promise_test(() => {
+      return new Promise((resolve, reject) => {
+        requestAnimationFrame(() => {
+          if (document.getElementById("last"))
+            reject();
+          else
+            resolve();
+        });
+      });
+    }, "blocking defers frames until removed");
+  </script>
+  </head>
+  <body>
+    <div id="first"></div>
+    <script>
+      jankMany(100, 10);
+      document.documentElement.blocking="";
+    </script>
+    <div id="second"></div>
+    <script>
+      jankMany(100, 10);
+    </script>
+    <div id="last"></div>
+  </body>
+</html>
diff --git a/html/dom/render-blocking/document-render-blocking.tentative.html b/html/dom/render-blocking/document-render-blocking.tentative.html
new file mode 100644
index 0000000..909029b
--- /dev/null
+++ b/html/dom/render-blocking/document-render-blocking.tentative.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+  <html blocking=render>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="support/utils.js"></script>
+  <title>`blocking=render` defers frames until complete document parsed</title>
+  <script>
+    assert_implements(document.documentElement.blocking, "no blocking attribute");
+
+    promise_test(() => {
+      return new Promise((resolve, reject) => {
+        requestAnimationFrame(() => {
+          if (document.getElementById("last"))
+            resolve();
+          else
+            reject();
+        });
+      });
+    }, "blocking defers frames until full parsing");
+  </script>
+  </head>
+  <body>
+    <div id="first"></div>
+    <script>
+      jankMany(100, 10);
+    </script>
+    <div id="second"></div>
+    <script>
+      jankMany(100, 10);
+    </script>
+    <div id="last"></div>
+  </body>
+</html>
diff --git a/html/dom/render-blocking/support/utils.js b/html/dom/render-blocking/support/utils.js
new file mode 100644
index 0000000..9a890ab
--- /dev/null
+++ b/html/dom/render-blocking/support/utils.js
@@ -0,0 +1,10 @@
+function jank(ms) {
+  let start = performance.now();
+  while (performance.now() < start + ms);
+}
+
+function jankMany(ms, times) {
+  for (let i = 0; i < times; i++) {
+    jank(ms);
+  }
+}