DevTools: Shadow DOM v0 polyfill

This polyfills Shadow DOM v0 on top of Shadow DOM v1 for old devtools
front ends.

Bug: 685385
Change-Id: Ia2a79a86012cbec2fc7cff3cb36fe4c9b631a381
Reviewed-on: https://chromium-review.googlesource.com/c/1366325
Reviewed-by: Dmitry Gozman <[email protected]>
Commit-Queue: Joel Einbinder <[email protected]>
Cr-Original-Commit-Position: refs/heads/master@{#614893}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: d828088b0546192c7cb08fd8404ead6422e39cdf
diff --git a/front_end/devtools_compatibility.js b/front_end/devtools_compatibility.js
index 1319616..788ebe8 100644
--- a/front_end/devtools_compatibility.js
+++ b/front_end/devtools_compatibility.js
@@ -1172,6 +1172,52 @@
 
     /** @type {!Array<string>} */
     const styleRules = [];
+    // Shadow DOM V0 polyfill
+    if (majorVersion <= 73 && !Element.prototype.createShadowRoot) {
+      Element.prototype.createShadowRoot = function() {
+        try {
+          return this.attachShadow({mode: 'open'});
+        } catch (e) {
+          // some elements we use to add shadow roots can no
+          // longer have shadow roots.
+          const fakeShadowHost = document.createElement('span');
+          this.appendChild(fakeShadowHost);
+          fakeShadowHost.className = 'fake-shadow-host';
+          return fakeShadowHost.createShadowRoot();
+        }
+      };
+
+      const origAdd = DOMTokenList.prototype.add;
+      DOMTokenList.prototype.add = function(...tokens) {
+        if (tokens[0].startsWith('insertion-point') || tokens[0].startsWith('tabbed-pane-header'))
+          this._myElement.slot = '.' + tokens[0];
+        return origAdd.apply(this, tokens);
+      };
+
+      const origCreateElement = Document.prototype.createElement;
+      Document.prototype.createElement = function(tagName, ...rest) {
+        if (tagName === 'content')
+          tagName = 'slot';
+        const element = origCreateElement.call(this, tagName, ...rest);
+        element.classList._myElement = element;
+        return element;
+      };
+
+      Object.defineProperty(HTMLSlotElement.prototype, 'select', {
+        async set(selector) {
+          this.name = selector;
+        }
+      });
+      const origCreateElementWithClass = Document.prototype.createElementWithClass;
+      Document.prototype.createElementWithClass = function(tagName, className, ...rest) {
+        if (tagName !== 'button' || (className !== 'soft-dropdown' && className !== 'dropdown-button'))
+          return origCreateElementWithClass.call(this, tagName, className, ...rest);
+        const element = origCreateElementWithClass.call(this, 'div', className, ...rest);
+        element.tabIndex = 0;
+        element.role = 'button';
+        return element;
+      };
+    }
 
     if (majorVersion <= 66) {
       /** @type {(!function(number, number):Element|undefined)} */