tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

shadow-dom.js (4808B)


      1 function removeWhiteSpaceOnlyTextNodes(node) {
      2  for (var i = 0; i < node.childNodes.length; i++) {
      3    var child = node.childNodes[i];
      4    if (child.nodeType === Node.TEXT_NODE &&
      5      child.nodeValue.trim().length == 0) {
      6      node.removeChild(child);
      7      i--;
      8    } else if (
      9      child.nodeType === Node.ELEMENT_NODE ||
     10      child.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
     11      removeWhiteSpaceOnlyTextNodes(child);
     12    }
     13  }
     14  if (node.shadowRoot) {
     15    removeWhiteSpaceOnlyTextNodes(node.shadowRoot);
     16  }
     17 }
     18 
     19 function convertTemplatesToShadowRootsWithin(node) {
     20  var nodes = node.querySelectorAll('template');
     21  for (var i = 0; i < nodes.length; ++i) {
     22    var template = nodes[i];
     23    var mode = template.getAttribute('data-mode');
     24    var delegatesFocus = template.hasAttribute('data-delegatesFocus');
     25    var parent = template.parentNode;
     26    parent.removeChild(template);
     27    var shadowRoot;
     28    if (!mode) {
     29      shadowRoot = parent.attachShadow({ mode: 'open' });
     30    } else {
     31      shadowRoot =
     32        parent.attachShadow({ 'mode': mode, 'delegatesFocus': delegatesFocus });
     33    }
     34    var expose = template.getAttribute('data-expose-as');
     35    if (expose)
     36      window[expose] = shadowRoot;
     37    if (template.id)
     38      shadowRoot.id = template.id;
     39    var fragments = document.importNode(template.content, true);
     40    shadowRoot.appendChild(fragments);
     41 
     42    convertTemplatesToShadowRootsWithin(shadowRoot);
     43  }
     44 }
     45 
     46 function isShadowHost(node) {
     47  return node && node.nodeType == Node.ELEMENT_NODE && node.shadowRoot;
     48 }
     49 
     50 function isIFrameElement(element) {
     51  return element && element.nodeName == 'IFRAME';
     52 }
     53 
     54 // Returns node from shadow/iframe tree "path".
     55 function getNodeInComposedTree(path) {
     56  var ids = path.split('/');
     57  var node = document.getElementById(ids[0]);
     58  for (var i = 1; node != null && i < ids.length; ++i) {
     59    if (isIFrameElement(node))
     60      node = node.contentDocument.getElementById(ids[i]);
     61    else if (isShadowHost(node))
     62      node = node.shadowRoot.getElementById(ids[i]);
     63    else
     64      return null;
     65  }
     66  return node;
     67 }
     68 
     69 function createTestTree(node) {
     70  let ids = {};
     71 
     72  function attachShadowFromTemplate(template) {
     73    let parent = template.parentNode;
     74    parent.removeChild(template);
     75    let shadowRoot;
     76    if (template.getAttribute('data-slot-assignment') === 'manual') {
     77      shadowRoot =
     78        parent.attachShadow({
     79          mode: template.getAttribute('data-mode'),
     80          slotAssignment: 'manual'
     81        });
     82    } else {
     83      shadowRoot =
     84        parent.attachShadow({ mode: template.getAttribute('data-mode') });
     85    }
     86    let id = template.id;
     87    if (id) {
     88      shadowRoot.id = id;
     89      ids[id] = shadowRoot;
     90    }
     91    shadowRoot.appendChild(document.importNode(template.content, true));
     92    return shadowRoot;
     93  }
     94 
     95  function walk(root) {
     96    if (root.id) {
     97      ids[root.id] = root;
     98    }
     99    for (let e of Array.from(root.querySelectorAll('[id]'))) {
    100      ids[e.id] = e;
    101    }
    102    for (let e of Array.from(root.querySelectorAll('template'))) {
    103      walk(attachShadowFromTemplate(e));
    104    }
    105  }
    106 
    107  walk(node.cloneNode(true));
    108  return ids;
    109 }
    110 
    111 function dispatchEventWithLog(nodes, target, event) {
    112  function labelFor(e) {
    113    return e.id || e.tagName;
    114  }
    115 
    116  let log = [];
    117  let attachedNodes = [];
    118  for (let label in nodes) {
    119    let startingNode = nodes[label];
    120    for (let node = startingNode; node; node = node.parentNode) {
    121      if (attachedNodes.indexOf(node) >= 0)
    122        continue;
    123      let id = node.id;
    124      if (!id)
    125        continue;
    126      attachedNodes.push(node);
    127      node.addEventListener(event.type, (e) => {
    128        // Record [currentTarget, target, relatedTarget, composedPath()]
    129        log.push([
    130          id, labelFor(e.target),
    131          e.relatedTarget ? labelFor(e.relatedTarget) : null,
    132          e.composedPath().map((n) => {
    133            return labelFor(n);
    134          })
    135        ]);
    136      });
    137    }
    138  }
    139  target.dispatchEvent(event);
    140  return log;
    141 }
    142 
    143 // This function assumes that testharness.js is available.
    144 function assert_event_path_equals(actual, expected) {
    145  assert_equals(actual.length, expected.length);
    146  for (let i = 0; i < actual.length; ++i) {
    147    assert_equals(
    148      actual[i][0], expected[i][0],
    149      'currentTarget at ' + i + ' should be same');
    150    assert_equals(
    151      actual[i][1], expected[i][1], 'target at ' + i + ' should be same');
    152    assert_equals(
    153      actual[i][2], expected[i][2],
    154      'relatedTarget at ' + i + ' should be same');
    155    assert_array_equals(
    156      actual[i][3], expected[i][3],
    157      'composedPath at ' + i + ' should be same');
    158  }
    159 }
    160 
    161 function assert_background_color(path, color) {
    162  assert_equals(
    163    window.getComputedStyle(getNodeInComposedTree(path)).backgroundColor,
    164    color, 'backgroundColor for ' + path + ' should be ' + color);
    165 }