Repository: GoogleChrome/lighthouse
Pull request #7745 (2019-03-27 17:15:13)

core(main-thread-tasks): improve error messages


Patch:
diff --git a/lighthouse-core/audits/main-thread-tasks.js b/lighthouse-core/audits/main-thread-tasks.js
index 437b0bbef..1399e6248 100644
--- a/lighthouse-core/audits/main-thread-tasks.js
+++ b/lighthouse-core/audits/main-thread-tasks.js
@@ -32,10 +32,10 @@ class MainThreadTasks extends Audit {
     const tasks = await MainThreadTasksComputed.request(trace, context);
 
     const results = tasks
-      .filter(task => task.duration > 5 && task.parent)
+      // Filter to just the sizable toplevel tasks; toplevel tasks are tasks without a parent.
+      .filter(task => task.duration > 5 && !task.parent)
       .map(task => {
         return {
-          type: task.group.id,
           duration: task.duration,
           startTime: task.startTime,
         };
@@ -43,7 +43,6 @@ class MainThreadTasks extends Audit {
 
     /** @type {LH.Audit.Details.Table['headings']} */
     const headings = [
-      {key: 'type', itemType: 'text', text: 'Task Type'},
       {key: 'startTime', itemType: 'ms', granularity: 1, text: 'Start Time'},
       {key: 'duration', itemType: 'ms', granularity: 1, text: 'End Time'},
     ];
diff --git a/lighthouse-core/computed/main-thread-tasks.js b/lighthouse-core/computed/main-thread-tasks.js
index e66eab62c..8efb0f32f 100644
--- a/lighthouse-core/computed/main-thread-tasks.js
+++ b/lighthouse-core/computed/main-thread-tasks.js
@@ -74,9 +74,10 @@ class MainThreadTasks {
   /**
    * @param {LH.TraceEvent[]} mainThreadEvents
    * @param {PriorTaskData} priorTaskData
+   * @param {number} traceEndTs
    * @return {TaskNode[]}
    */
-  static _createTasksFromEvents(mainThreadEvents, priorTaskData) {
+  static _createTasksFromEvents(mainThreadEvents, priorTaskData, traceEndTs) {
     /** @type {TaskNode[]} */
     const tasks = [];
     /** @type {TaskNode|undefined} */
@@ -108,7 +109,7 @@ class MainThreadTasks {
       if (!currentTask) {
         // We can't start a task with an end event
         if (event.ph === 'E') {
-          throw new Error('Fatal trace logic error');
+          throw new Error('Fatal trace logic error - unexpected end event');
         }
 
         currentTask = MainThreadTasks._createNewTaskNode(event);
@@ -124,7 +125,8 @@ class MainThreadTasks {
         currentTask = newTask;
       } else {
         if (currentTask.event.ph !== 'B') {
-          throw new Error('Fatal trace logic error');
+          throw new Error(
+            `Fatal trace logic error - expected start event, got ${currentTask.event.ph}`);
         }
 
         // We're ending an event, update the end time and the currentTask to its parent
@@ -133,6 +135,14 @@ class MainThreadTasks {
       }
     }
 
+    // Starting from the last and bottom-most task, we finish any tasks that didn't end yet.
+    while (currentTask && !Number.isFinite(currentTask.endTime)) {
+      // The last event didn't finish before tracing stopped, use traceEnd timestamp instead.
+      currentTask.endTime = traceEndTs;
+      currentTask = currentTask.parent;
+    }
+
+    // At this point we expect all tasks to have a finite startTime and endTime.
     return tasks;
   }
 
@@ -215,12 +225,13 @@ class MainThreadTasks {
 
   /**
    * @param {LH.TraceEvent[]} traceEvents
+   * @param {number} traceEndTs
    * @return {TaskNode[]}
    */
-  static getMainThreadTasks(traceEvents) {
+  static getMainThreadTasks(traceEvents, traceEndTs) {
     const timers = new Map();
     const priorTaskData = {timers};
-    const tasks = MainThreadTasks._createTasksFromEvents(traceEvents, priorTaskData);
+    const tasks = MainThreadTasks._createTasksFromEvents(traceEvents, priorTaskData, traceEndTs);
 
     // Compute the recursive properties we couldn't compute earlier, starting at the toplevel tasks
     for (const task of tasks) {
@@ -254,8 +265,8 @@ class MainThreadTasks {
    * @return {Promise<Array<TaskNode>>} networkRecords
    */
   static async compute_(trace, context) {
-    const {mainThreadEvents} = await TraceOfTab.request(trace, context);
-    return MainThreadTasks.getMainThreadTasks(mainThreadEvents);
+    const {mainThreadEvents, timestamps} = await TraceOfTab.request(trace, context);
+    return MainThreadTasks.getMainThreadTasks(mainThreadEvents, timestamps.traceEnd);
   }
 }
 
diff --git a/lighthouse-core/gather/driver.js b/lighthouse-core/gather/driver.js
index b800c9adf..d0d8176f7 100644
--- a/lighthouse-core/gather/driver.js
+++ b/lighthouse-core/gather/driver.js
@@ -295,7 +295,7 @@ class Driver {
    * @param {string[]} parentSessionIds The list of session ids of the parents from youngest to oldest.
    */
   async _handleReceivedMessageFromTarget(event, parentSessionIds) {
-    const {sessionId, message} = event;
+    const {targetId, sessionId, message} = event;
     /** @type {LH.Protocol.RawMessage} */
     const protocolMessage = JSON.parse(message);
 
@@ -317,7 +317,7 @@ class Driver {
     }
 
     if (protocolMessage.method.startsWith('Network')) {
-      this._handleProtocolEvent(protocolMessage);
+      this._handleProtocolEvent({...protocolMessage, source: {targetId, sessionId}});
     }
   }
 
diff --git a/lighthouse-core/gather/gatherers/dobetterweb/optimized-images.js b/lighthouse-core/gather/gatherers/dobetterweb/optimized-images.js
index 0a96d98b3..a9a533cb5 100644
--- a/lighthouse-core/gather/gatherers/dobetterweb/optimized-images.js
+++ b/lighthouse-core/gather/gatherers/dobetterweb/optimized-images.js
@@ -45,7 +45,8 @@ class OptimizedImages extends Gatherer {
     /** @type {Set<string>} */
     const seenUrls = new Set();
     return networkRecords.reduce((prev, record) => {
-      if (seenUrls.has(record.url) || !record.finished) {
+      // Skip records that we've seen before, never finished, or came from OOPIFs.
+      if (seenUrls.has(record.url) || !record.finished || record.sessionId) {
         return prev;
       }
 
diff --git a/lighthouse-core/gather/gatherers/dobetterweb/response-compression.js b/lighthouse-core/gather/gatherers/dobetterweb/response-compression.js
index 235845617..ed58dcb26 100644
--- a/lighthouse-core/gather/gatherers/dobetterweb/response-compression.js
+++ b/lighthouse-core/gather/gatherers/dobetterweb/response-compression.js
@@ -40,6 +40,9 @@ class ResponseCompression extends Gatherer {
     const unoptimizedResponses = [];
 
     networkRecords.forEach(record => {
+      // Ignore records from OOPIFs
+      if (record.sessionId) return;
+
       const mimeType = record.mimeType;
       const resourceType = record.resourceType || NetworkRequest.TYPES.Other;
       const resourceSize = record.resourceSize;
diff --git a/lighthouse-core/gather/gatherers/image-elements.js b/lighthouse-core/gather/gatherers/image-elements.js
index 25e8eb10c..3a49b0f24 100644
--- a/lighthouse-core/gather/gatherers/image-elements.js
+++ b/lighthouse-core/gather/gatherers/image-elements.js
@@ -22,7 +22,7 @@ function collectImageElementInfo() {
   function getClientRect(element) {
     const clientRect = element.getBoundingClientRect();
     return {
-      // manually copy the properties because ClientRect does not JSONify
+      // Just grab the DOMRect properties we want, excluding x/y/width/height
       top: clientRect.top,
       bottom: clientRect.bottom,
       left: clientRect.left,
diff --git a/lighthouse-core/gather/gatherers/scripts.js b/lighthouse-core/gather/gatherers/scripts.js
index 537ed29c4..fa0969fb4 100644
--- a/lighthouse-core/gather/gatherers/scripts.js
+++ b/lighthouse-core/gather/gatherers/scripts.js
@@ -58,6 +58,9 @@ class Scripts extends Gatherer {
     }
 
     const scriptRecords = loadData.networkRecords
+      // Ignore records from OOPIFs
+      .filter(record => !record.sessionId)
+      // Only get the content of script requests
       .filter(record => record.resourceType === NetworkRequest.TYPES.Script);
 
     for (const record of scriptRecords) {
diff --git a/lighthouse-core/lib/dependency-graph/base-node.js b/lighthouse-core/lib/dependency-graph/base-node.js
index 308ea66ef..c3638e60c 100644
--- a/lighthouse-core/lib/dependency-graph/base-node.js
+++ b/lighthouse-core/lib/dependency-graph/base-node.js
@@ -168,96 +168,88 @@ class BaseNode {
 
   /**
    * Clones the entire graph connected to this node filtered by the optional predicate. If a node is
-   * included by the predicate, all nodes along the paths between the two will be included. If the
-   * node that was called clone is not included in the resulting filtered graph, the method will throw.
+   * included by the predicate, all nodes along the paths between the node and the root will be included. If the
+   * node this was called on is not included in the resulting filtered graph, the method will throw.
    * @param {function(Node):boolean} [predicate]
    * @return {Node}
    */
   cloneWithRelationships(predicate) {
     const rootNode = this.getRootNode();
 
-    /** @type {function(Node): boolean} */
-    let shouldIncludeNode = () => true;
-    if (predicate) {
-      const idsToInclude = new Set();
-      rootNode.traverse(node => {
-        if (predicate(node)) {
-          node.traverse(
-            node => idsToInclude.add(node.id),
-            node => node._dependencies.filter(parent => !idsToInclude.has(parent))
-          );
-        }
-      });
-
-      shouldIncludeNode = node => idsToInclude.has(node.id);
-    }
+    /** @type {Map<string, Node>} */
+    const idsToIncludedClones = new Map();
 
-    const idToNodeMap = new Map();
-    rootNode.traverse(originalNode => {
-      if (!shouldIncludeNode(originalNode)) return;
-      const clonedNode = originalNode.cloneWithoutRelationships();
-      idToNodeMap.set(clonedNode.id, clonedNode);
+    // Walk down dependents.
+    rootNode.traverse(node => {
+      if (idsToIncludedClones.has(node.id)) return;
+
+      if (predicate === undefined) {
+        // No condition for entry, so clone every node.
+        idsToIncludedClones.set(node.id, node.cloneWithoutRelationships());
+        return;
+      }
+
+      if (predicate(node)) {
+        // Node included, so walk back up dependencies, cloning nodes from here back to the root.
+        node.traverse(
+          node => idsToIncludedClones.set(node.id, node.cloneWithoutRelationships()),
+          // Dependencies already cloned have already cloned ancestors, so no need to visit again.
+          node => node._dependencies.filter(parent => !idsToIncludedClones.has(parent.id))
+        );
+      }
     });
 
+    // Copy dependencies between nodes.
     rootNode.traverse(originalNode => {
-      if (!shouldIncludeNode(originalNode)) return;
-      const clonedNode = idToNodeMap.get(originalNode.id);
+      const clonedNode = idsToIncludedClones.get(originalNode.id);
+      if (!clonedNode) return;
 
       for (const dependency of originalNode._dependencies) {
-        const clonedDependency = idToNodeMap.get(dependency.id);
+        const clonedDependency = idsToIncludedClones.get(dependency.id);
+        if (!clonedDependency) throw new Error('Dependency somehow not cloned');
         clonedNode.addDependency(clonedDependency);
       }
     });
 
-    if (!idToNodeMap.has(this.id)) throw new Error('Cloned graph missing node');
-    return idToNodeMap.get(this.id);
+    const clonedThisNode = idsToIncludedClones.get(this.id);
+    if (!clonedThisNode) throw new Error('Cloned graph missing node');
+    return clonedThisNode;
   }
 
   /**
-   * Traverses all paths in the graph, calling iterator on each node visited. Decides which nodes to
-   * visit with the getNext function.
-   * @param {function(Node, Node[]): void} iterator
-   * @param {function(Node): Node[]} getNext
+   * Traverses all connected nodes in BFS order, calling `callback` exactly once
+   * on each. `traversalPath` is the shortest (though not necessarily unique)
+   * path from `node` to the root of the iteration.
+   *
+   * The `getNextNodes` function takes a visited node and returns which nodes to
+   * visit next. It defaults to returning the node's dependents.
+   * @param {(node: Node, traversalPath: Node[]) => void} callback
+   * @param {function(Node): Node[]} [getNextNodes]
    */
-  _traversePaths(iterator, getNext) {
-    const stack = [[/** @type {Node} */(/** @type {BaseNode} */(this))]];
-    while (stack.length) {
-      /** @type {Node[]} */
-      // @ts-ignore - stack has length so it's guaranteed to have an item
-      const path = stack.shift();
-      const node = path[0];
-      iterator(node, path);
-
-      const nodesToAdd = getNext(node);
-      for (const nextNode of nodesToAdd) {
-        stack.push([nextNode].concat(path));
-      }
+  traverse(callback, getNextNodes) {
+    if (!getNextNodes) {
+      getNextNodes = node => node.getDependents();
     }
-  }
 
-  /**
-   * Traverses all connected nodes exactly once, calling iterator on each. Decides which nodes to
-   * visit with the getNext function.
-   * @param {function(Node, Node[]): void} iterator
-   * @param {function(Node): Node[]} [getNext] Defaults to returning the dependents.
-   */
-  traverse(iterator, getNext) {
-    if (!getNext) {
-      getNext = node => node.getDependents();
-    }
+    /** @type {Node[][]} */
+    // @ts-ignore - only traverses graphs of Node, so force tsc to treat `this` as one
+    const queue = [[this]];
+    const visited = new Set([this.id]);
 
-    const visited = new Set();
-    const originalGetNext = getNext;
+    while (queue.length) {
+      /** @type {Node[]} */
+      // @ts-ignore - queue has length so it's guaranteed to have an item
+      const traversalPath = queue.shift();
+      const node = traversalPath[0];
+      callback(node, traversalPath);
 
-    getNext = node => {
-      visited.add(node.id);
-      const allNodesToVisit = originalGetNext(node);
-      const nodesToVisit = allNodesToVisit.filter(nextNode => !visited.has(nextNode.id));
-      nodesToVisit.forEach(nextNode => visited.add(nextNode.id));
-      return nodesToVisit;
-    };
+      for (const nextNode of getNextNodes(node)) {
+        if (visited.has(nextNode.id)) continue;
+        visited.add(nextNode.id);
 
-    this._traversePaths(iterator, getNext);
+        queue.push([nextNode, ...traversalPath]);
+      }
+    }
   }
 
   /**
diff --git a/lighthouse-core/lib/i18n/locales/hi.json b/lighthouse-core/lib/i18n/locales/hi.json
index b3c347903..05e5912e6 100644
--- a/lighthouse-core/lib/i18n/locales/hi.json
+++ b/lighthouse-core/lib/i18n/locales/hi.json
@@ -1,6 +1,6 @@
 {
   "lighthouse-core/audits/accessibility/accesskeys.js | description": {
-    "message": "Access keys let users quickly focus a part of the page. For proper navigation, each access key must be unique. [Learn more](https://dequeuniversity.com/rules/axe/3.1/accesskeys?application=lighthouse)."
+    "message": "एक्सेस कुंजी से इस्तेमाल करने वाले पेज के किसी हिस्से पर तेज़ी से फ़ोकस कर सकते हैं. सही नेविगेशन के लिए, हर एक्सेस कुंजी सबसे अलग होनी चाहिए. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/accesskeys?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/accesskeys.js | failureTitle": {
     "message": "`[accesskey]` मान सबसे अलग नहीं हैं"
@@ -9,7 +9,7 @@
     "message": "`[accesskey]` मान सबसे अलग हैं"
   },
   "lighthouse-core/audits/accessibility/aria-allowed-attr.js | description": {
-    "message": "Each ARIA `role` supports a specific subset of `aria-*` attributes. Mismatching these invalidates the `aria-*` attributes. [Learn more](https://dequeuniversity.com/rules/axe/3.1/aria-allowed-attr?application=lighthouse)."
+    "message": "`aria-*` विशेषताओं के खास सबसेट के लिए, हर ARIA `भूमिका` काम करती है. इनमें मिलान नहीं होने से `aria-*` विशेषताएं गलत हो जाती हैं. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/aria-allowed-attr?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/aria-allowed-attr.js | failureTitle": {
     "message": "`[aria-*]` विशेषताओं का मिलान उनकी भूमिकाओं से नहीं होता है"
@@ -18,7 +18,7 @@
     "message": "`[aria-*]` विशेषताओं के मिलान उनकी भूमिकाओं से होता है"
   },
   "lighthouse-core/audits/accessibility/aria-required-attr.js | description": {
-    "message": "Some ARIA roles have required attributes that describe the state of the element to screen readers. [Learn more](https://dequeuniversity.com/rules/axe/3.1/aria-required-attr?application=lighthouse)."
+    "message": "कुछ ARIA भूमिकाओं में ऐसी विशेषताओं की ज़रूरत होती है, जो स्क्रीन रीडर को एलिमेंट की स्थिति बताती हैं. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/aria-required-attr?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/aria-required-attr.js | failureTitle": {
     "message": "`[role]` की ज़रूरी `[aria-*]` विशेषताएं नहीं हैं"
@@ -27,7 +27,7 @@
     "message": "`[role]`के पास ज़रूरी `[aria-*]` विशेषताएं हैं"
   },
   "lighthouse-core/audits/accessibility/aria-required-children.js | description": {
-    "message": "Some ARIA parent roles must contain specific child roles to perform their intended accessibility functions. [Learn more](https://dequeuniversity.com/rules/axe/3.1/aria-required-children?application=lighthouse)."
+    "message": "कुछ ARIA पैरेंट भूमिकाओं में खास चाइल्ड भूमिकाएं होनी चाहिए ताकि वे तय किए गए सुलभता फ़ंक्शन कर सकें. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/aria-required-children?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/aria-required-children.js | failureTitle": {
     "message": "`[role]` वाले एलिमेंट जिन्हें खास चिल्ड्रन `[role]` की ज़रूरत होती है, वे मिल नहीं रहे हैं."
@@ -36,7 +36,7 @@
     "message": "`[role]` वाले एलिमेंट जिन्हें खास चिल्ड्रन `[role]`की ज़रूरत होती है, वे मिल नहीं रहे हैं."
   },
   "lighthouse-core/audits/accessibility/aria-required-parent.js | description": {
-    "message": "Some ARIA child roles must be contained by specific parent roles to properly perform their intended accessibility functions. [Learn more](https://dequeuniversity.com/rules/axe/3.1/aria-required-parent?application=lighthouse)."
+    "message": "कुछ ARIA चाइल्ड रोल अपने तय सुलभता फ़ंक्शन को ठीक से करें, इसके लिए उन्हें खास पैरेंट रोल में होना चाहिए. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/aria-required-parent?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/aria-required-parent.js | failureTitle": {
     "message": "`[role]` अपने ज़रूरी अभिभावक एलिमेंट में नहीं हैं"
@@ -45,7 +45,7 @@
     "message": "`[role]` अपने ज़रूरी अभिभावक एलिमेंट में होते हैं"
   },
   "lighthouse-core/audits/accessibility/aria-roles.js | description": {
-    "message": "ARIA roles must have valid values in order to perform their intended accessibility functions. [Learn more](https://dequeuniversity.com/rules/axe/3.1/aria-roles?application=lighthouse)."
+    "message": "ARIA भूमिकाओं में सही मान होने चाहिए ताकि वे तय किए गए सुलभता फ़ंक्शन कर सकें. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/aria-roles?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/aria-roles.js | failureTitle": {
     "message": "`[role]` मान सही नहीं हैं"
@@ -54,7 +54,7 @@
     "message": "`[role]` मान सही हैं"
   },
   "lighthouse-core/audits/accessibility/aria-valid-attr-value.js | description": {
-    "message": "Assistive technologies, like screen readers, can't interpret ARIA attributes with invalid values. [Learn more](https://dequeuniversity.com/rules/axe/3.1/aria-valid-attr-value?application=lighthouse)."
+    "message": "स्क्रीन रीडर जैसी सहायक तकनीकें, गलत मानों वाली ARIA विशेषताओं को समझ नहीं सकतीं. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/aria-valid-attr-value?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/aria-valid-attr-value.js | failureTitle": {
     "message": "`[aria-*]` विशेषताओं में सही मान नहीं हैं"
@@ -63,7 +63,7 @@
     "message": "`[aria-*]` विशेषताओं में सही मान हैं"
   },
   "lighthouse-core/audits/accessibility/aria-valid-attr.js | description": {
-    "message": "Assistive technologies, like screen readers, can't interpret ARIA attributes with invalid names. [Learn more](https://dequeuniversity.com/rules/axe/3.1/aria-valid-attr?application=lighthouse)."
+    "message": "स्क्रीन रीडर जैसी सहायक तकनीकें, गलत नामों वाली ARIA विशेषताओं को समझ नहीं सकतीं. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/aria-valid-attr?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/aria-valid-attr.js | failureTitle": {
     "message": "`[aria-*]` विशेषताएं गलत हैं या उनकी वर्तनी गलत है"
@@ -72,7 +72,7 @@
     "message": "`[aria-*]` विशेषताएं मान्य हैं और उनकी वर्तनी गलत नहीं है"
   },
   "lighthouse-core/audits/accessibility/audio-caption.js | description": {
-    "message": "Captions make audio elements usable for deaf or hearing-impaired users, providing critical information such as who is talking, what they're saying, and other non-speech information. [Learn more](https://dequeuniversity.com/rules/axe/3.1/audio-caption?application=lighthouse)."
+    "message": "कैप्शन, ऑडियो एलिमेंट को ऐसे इस्तेमाल करने वाले लोगों के लिए फ़ायदेमंद बनाते हैं, जो सुन नहीं पाते हैं या जिन्हें कम सुनाई देता है. साथ ही, ये कौन बा़त कर रहा है, वे क्या बोल रहे हैं, और बोली नहीं जाने वाली दूसरी जानकारी जैसी खास जानकारी भी देते हैं. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/audio-caption?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/audio-caption.js | failureTitle": {
     "message": "`<audio>` एलिमेंट में `[kind=\"captions\"]` वाला `<track>` एलिमेंट नहीं है."
@@ -84,7 +84,7 @@
     "message": "विफल रहने वाले एलिमेंट"
   },
   "lighthouse-core/audits/accessibility/button-name.js | description": {
-    "message": "When a button doesn't have an accessible name, screen readers announce it as \"button\", making it unusable for users who rely on screen readers. [Learn more](https://dequeuniversity.com/rules/axe/3.1/button-name?application=lighthouse)."
+    "message": "बटन का एक्सेस करने लायक कोई नाम नहीं होने पर, स्क्रीन रीडर उसे \"बटन\" के रूप में बताते हैं. इस वजह से यह उन इस्तेमाल करने वालों के लिए बेकार हो जाता है, जो स्क्रीन रीडर पर भरोसा करते हैं. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/button-name?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/button-name.js | failureTitle": {
     "message": "बटन के नाम एक्सेस करने लायक नहीं हैं"
@@ -93,7 +93,7 @@
     "message": "बटनों का एक सुलभता नाम है"
   },
   "lighthouse-core/audits/accessibility/bypass.js | description": {
-    "message": "Adding ways to bypass repetitive content lets keyboard users navigate the page more efficiently. [Learn more](https://dequeuniversity.com/rules/axe/3.1/bypass?application=lighthouse)."
+    "message": "दोहराव वाली सामग्री को नज़रअंदाज करके आगे बढ़ने के तरीके जोड़ने से, कीबोर्ड इस्तेमाल करने वाले लोग पेज पर ज़्यादा अच्छे से नेविगेट कर सकते हैं. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/bypass?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/bypass.js | failureTitle": {
     "message": "पेज में 'हेडिंग', 'स्किप लिंक' या 'लैंडमार्क' क्षेत्र नहीं है"
@@ -102,7 +102,7 @@
     "message": "पेज में 'हेडिंग', 'स्किप लिंक' या 'लैंडमार्क' क्षेत्र हैं"
   },
   "lighthouse-core/audits/accessibility/color-contrast.js | description": {
-    "message": "Low-contrast text is difficult or impossible for many users to read. [Learn more](https://dequeuniversity.com/rules/axe/3.1/color-contrast?application=lighthouse)."
+    "message": "कम-कंट्रास्ट वाला टेक्स्ट पढ़ना कई इस्तेमाल करने वालों के लिए कठिन या नामुमकिन हो जाता है. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/color-contrast?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/color-contrast.js | failureTitle": {
     "message": "बैकग्राउंड और फ़ोरग्राउंड रंगों में काफ़ी कंट्रास्ट अनुपात नहीं है."
@@ -111,7 +111,7 @@
     "message": "बैकग्राउंड और फ़ोरग्राउंड के रंगों का कंट्रास्ट अनुपात काफ़ी है"
   },
   "lighthouse-core/audits/accessibility/definition-list.js | description": {
-    "message": "When definition lists are not properly marked up, screen readers may produce confusing or inaccurate output. [Learn more](https://dequeuniversity.com/rules/axe/3.1/definition-list?application=lighthouse)."
+    "message": "जब परिभाषा सूचियां ठीक से मार्क अप नहीं की जाती हैं, तो स्क्रीन रीडर उलझन भरे या गलत आउटपुट दे सकते हैं. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/definition-list?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/definition-list.js | failureTitle": {
     "message": "`<dl>` में सिर्फ़ ठीक तरह से क्रम में लगे `<dt>` और `<dd>` समूह, `<script>` या `<template>` एलिमेंट नहीं हैं."
@@ -120,7 +120,7 @@
     "message": "`<dl>` में सिर्फ़ ठीक तरह से क्रम में लगे `<dt>` और `<dd>` समूह, `<script>` या `<template>` एलिमेंट होते हैं."
   },
   "lighthouse-core/audits/accessibility/dlitem.js | description": {
-    "message": "Definition list items (`<dt>` and `<dd>`) must be wrapped in a parent `<dl>` element to ensure that screen readers can properly announce them. [Learn more](https://dequeuniversity.com/rules/axe/3.1/dlitem?application=lighthouse)."
+    "message": "परिभाषा सूची आइटम (<dt>` और `<dd>`) को पैरेंट `<dl>` एलिमेंट में रैप होना चाहिए, ताकि यह पक्का किया जा सके कि स्क्रीन रीडर उन्हें ठीक तरीके से बोल सकते हैं. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/dlitem?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/dlitem.js | failureTitle": {
     "message": "`<dl>` एलिमेंट में परिभाषा सूची के आइटम नहीं हैं"
@@ -138,7 +138,7 @@
     "message": "दस्तावेज़ में `<title>` एलिमेंट है"
   },
   "lighthouse-core/audits/accessibility/duplicate-id.js | description": {
-    "message": "The value of an id attribute must be unique to prevent other instances from being overlooked by assistive technologies. [Learn more](https://dequeuniversity.com/rules/axe/3.1/duplicate-id?application=lighthouse)."
+    "message": "सहायक तकनीकों को दूसरे इंस्टेंस अनदेखा करने से रोकने के लिए, आईडी विशेषता का मान सबसे अलग होना चाहिए. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/duplicate-id?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/duplicate-id.js | failureTitle": {
     "message": "पेज पर `[id]` विशेषताएं सबसे अलग नहीं हैं"
@@ -147,7 +147,7 @@
     "message": "पेज पर `[id]` विशेषताएं सबसे अलग हैं"
   },
   "lighthouse-core/audits/accessibility/frame-title.js | description": {
-    "message": "Screen reader users rely on frame titles to describe the contents of frames. [Learn more](https://dequeuniversity.com/rules/axe/3.1/frame-title?application=lighthouse)."
+    "message": "स्क्रीन रीडर इस्तेमाल करने वाले फ़्रेम की सामग्रियों के बारे में जानने के लिए फ़्रेम के शीर्षकों पर भरोसा करते हैं. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/frame-title?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/frame-title.js | failureTitle": {
     "message": "`<frame>` या `<iframe>` एलिमेंट का कोई शीर्षक नहीं है"
@@ -156,7 +156,7 @@
     "message": "`<frame>` या `<iframe>` एलिमेंट में शीर्षक हैं"
   },
   "lighthouse-core/audits/accessibility/html-has-lang.js | description": {
-    "message": "If a page doesn't specify a lang attribute, a screen reader assumes that the page is in the default language that the user chose when setting up the screen reader. If the page isn't actually in the default language, then the screen reader might not announce the page's text correctly. [Learn more](https://dequeuniversity.com/rules/axe/3.1/html-has-lang?application=lighthouse)."
+    "message": "अगर कोई पेज भाषा विशेषता तय नहीं करता है, तो स्क्रीन रीडर को लगता है कि पेज उस डिफ़ॉल्ट भाषा में है जो इस्तेमाल करने वाले ने स्क्रीन रीडर सेट अप करते समय चुनी थी. दरअसल, पेज अगर डिफ़ॉल्ट भाषा में नहीं होता है, तो स्क्रीन रीडर पेज का टेक्स्ट ठीक से नहीं बोल पाता है. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/html-has-lang?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/html-has-lang.js | failureTitle": {
     "message": "`<html>` एलिमेंट में एक `[lang]` विशेषता नहीं है"
@@ -165,7 +165,7 @@
     "message": "`<html>` एलिमेंट में एक `[lang]` विशेषता है"
   },
   "lighthouse-core/audits/accessibility/html-lang-valid.js | description": {
-    "message": "Specifying a valid [BCP 47 language](https://www.w3.org/International/questions/qa-choosing-language-tags#question) helps screen readers announce text properly. [Learn more](https://dequeuniversity.com/rules/axe/3.1/valid-lang?application=lighthouse)."
+    "message": "सही [BCP 47 भाषा](https://www.w3.org/International/questions/qa-choosing-language-tags#question) तय करने से स्क्रीन रीडर को टेक्स्ट ठीक से बोलने में मदद मिलती है. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/valid-lang?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/html-lang-valid.js | failureTitle": {
     "message": "`<html>` एलिमेंट के पास इसकी `[lang]` विशेषता के लिए सही मान नहीं है."
@@ -174,7 +174,7 @@
     "message": "`<html>` एलिमेंट में इसकी `[lang]` विशेषता के लिए सही मान है"
   },
   "lighthouse-core/audits/accessibility/image-alt.js | description": {
-    "message": "Informative elements should aim for short, descriptive alternate text. Decorative elements can be ignored with an empty alt attribute. [Learn more](https://dequeuniversity.com/rules/axe/3.1/image-alt?application=lighthouse)."
+    "message": "जानकारी वाले एलिमेंट का मकसद छोटा, पूरी जानकारी देने वाला वैकल्पिक टेक्स्ट होना चाहिए. सजावटी एलिमेंट को एक खाली alt विशेषता के साथ अनदेखा किया जा सकता है. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/image-alt?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/image-alt.js | failureTitle": {
     "message": "इमेज एलिमेंट में `[alt]` विशेषताएं नहीं हैं"
@@ -183,7 +183,7 @@
     "message": "इमेज एलिमेंट में `[alt]` विशेषताएं हैं"
   },
   "lighthouse-core/audits/accessibility/input-image-alt.js | description": {
-    "message": "When an image is being used as an `<input>` button, providing alternative text can help screen reader users understand the purpose of the button. [Learn more](https://dequeuniversity.com/rules/axe/3.1/input-image-alt?application=lighthouse)."
+    "message": "जब किसी इमेज का इस्तेमाल एक `<input>` बटन के रूप में हो रहा हो, तो वैकल्पिक टेक्स्ट देने से स्क्रीन रीडर इस्तेमाल करने वालों को बटन के बारे में समझने में मदद मिलती है. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/input-image-alt?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/input-image-alt.js | failureTitle": {
     "message": "`<input type=\"image\">` एलिमेंट में `[alt]` टेक्स्ट नहीं है"
@@ -192,7 +192,7 @@
     "message": "`<input type=\"image\">` एलिमेंट में `[alt]` टेक्स्ट है"
   },
   "lighthouse-core/audits/accessibility/label.js | description": {
-    "message": "Labels ensure that form controls are announced properly by assistive technologies, like screen readers. [Learn more](https://dequeuniversity.com/rules/axe/3.1/label?application=lighthouse)."
+    "message": "लेबल यह पक्का करते हैं कि स्क्रीन रीडर जैसी सहायक तकनीकें फ़ॉर्म नियंत्रणों को सही तरीके से बोलती हैं. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/label?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/label.js | failureTitle": {
     "message": "फ़ॉर्म एलिमेंट में जुड़े हुए लेबल नहीं हैं"
@@ -201,7 +201,7 @@
     "message": "फ़ॉर्म एलिमेंट में सहभागी लेबल हैं"
   },
   "lighthouse-core/audits/accessibility/layout-table.js | description": {
-    "message": "A table being used for layout purposes should not include data elements, such as the th or caption elements or the summary attribute, because this can create a confusing experience for screen reader users. [Learn more](https://dequeuniversity.com/rules/axe/3.1/layout-table?application=lighthouse)."
+    "message": "लेआउट के लिए इस्तेमाल की जाने वाली टेबल में th या कैप्शन वाले एलिमेंट जैसे डेटा एलिमेंट या सारांश विशेषता शामिल नहीं होनी चाहिए, क्योंकि इससे स्क्रीन रीडर इस्तेमाल करने वाले उलझन में पड़ सकते हैं. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/layout-table?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/layout-table.js | failureTitle": {
     "message": "प्रस्तुतिकरण `<table>` वाले तत्व `<th>`, `<caption>` या `[summary]` विशेषता के इस्तेमाल को अनदेखा नहीं कर सकते."
@@ -210,7 +210,7 @@
     "message": "प्रस्तुतिकरण `<table>` वाले एलिमेंट में `<th>`, `<caption>` या `[summary]` विशेषता के इस्तेमाल को अनदेखा किया गया है."
   },
   "lighthouse-core/audits/accessibility/link-name.js | description": {
-    "message": "Link text (and alternate text for images, when used as links) that is discernible, unique, and focusable improves the navigation experience for screen reader users. [Learn more](https://dequeuniversity.com/rules/axe/3.1/link-name?application=lighthouse)."
+    "message": "लिंक टेक्स्ट (और इमेज को लिंक की तरह इस्तेमाल किए जाने पर उनके लिए वैकल्पिक टेक्स्ट) समझने लायक, सबसे अलग, और फ़ोकस किए जा सकने वाला होना चाहिए. इससे स्क्रीन रीडर इस्तेमाल करने वालों का नेविगेशन अनुभव बेहतर बनता है. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/link-name?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/link-name.js | failureTitle": {
     "message": "लिंक का समझने लायक नहीं है"
@@ -219,7 +219,7 @@
     "message": "लिंक का समझने लायक नाम है"
   },
   "lighthouse-core/audits/accessibility/list.js | description": {
-    "message": "Screen readers have a specific way of announcing lists. Ensuring proper list structure aids screen reader output. [Learn more](https://dequeuniversity.com/rules/axe/3.1/list?application=lighthouse)."
+    "message": "स्क्रीन रीडर सूचियों को एक खास तरीके से बोलते हैं. सूची की सही बनावट पक्की करने से स्क्रीन रीडर को आउटपुट देने में मदद मिलती है. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/list?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/list.js | failureTitle": {
     "message": "सूचियों में सिर्फ़ `<li>`एलिमेंट और स्क्रिप्ट के साथ काम करने वाले एलिमेंट (`<script>` और `<template>`) ही शामिल नहीं हैं."
@@ -228,7 +228,7 @@
     "message": "सूचियों में सिर्फ़ `<li>` एलिमेंट और स्क्रिप्ट के साथ काम करने वाले एलिमेंट (`<script>` और `<template>`) ही शामिल हैं."
   },
   "lighthouse-core/audits/accessibility/listitem.js | description": {
-    "message": "Screen readers require list items (`<li>`) to be contained within a parent `<ul>` or `<ol>` to be announced properly. [Learn more](https://dequeuniversity.com/rules/axe/3.1/listitem?application=lighthouse)."
+    "message": "स्क्रीन रीडर के लिए ज़रूरी है कि उनमें पैरेंट `<ul>` या `<ol>` में सूची आइटम (`<li>`) शामिल हों, ताकि उन्हें ठीक तरह से बोला जा सके. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/listitem?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/listitem.js | failureTitle": {
     "message": "सूची वाले आइटम (`<li>`), `<ul>` या `<ol>` अभिभावक एलिमेंट में नहीं होते हैं."
@@ -237,7 +237,7 @@
     "message": "सूची वाले आइटम (`<li>`), `<ul>` या `<ol>` अभिभावक एलिमेंट में हैं"
   },
   "lighthouse-core/audits/accessibility/meta-refresh.js | description": {
-    "message": "Users do not expect a page to refresh automatically, and doing so will move focus back to the top of the page. This may create a frustrating or confusing experience. [Learn more](https://dequeuniversity.com/rules/axe/3.1/meta-refresh?application=lighthouse)."
+    "message": "इस्तेमाल करने वालों को ऐसी उम्मीद नहीं होती है कि कोई पेज अपने आप रीफ़्रेश हो जाएगा और इसके बाद फ़ोकस वापस पेज के सबसे ऊपर चला जाएगा. ऐसा होने पर वे परेशानी या उलझन में पड़ सकते हैं. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/meta-refresh?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/meta-refresh.js | failureTitle": {
     "message": "दस्तावेज़ `<meta http-equiv=\"refresh\">` का इस्तेमाल करता है"
@@ -246,7 +246,7 @@
     "message": "यह दस्तावेज़ `<meta http-equiv=\"refresh\">` का इस्तेमाल नहीं करता है"
   },
   "lighthouse-core/audits/accessibility/meta-viewport.js | description": {
-    "message": "Disabling zooming is problematic for users with low vision who rely on screen magnification to properly see the contents of a web page. [Learn more](https://dequeuniversity.com/rules/axe/3.1/meta-viewport?application=lighthouse)."
+    "message": "ज़ूम की सुविधा बंद करने से इस्तेमाल करने वाले ऐसे लोगों को परेशानी होती है, जिन्हें कम दिखाई देता है. इस्तेमाल करने वाले ये लोग स्क्रीन बड़ी करके वेब पेज की सामग्रियों को ठीक से देख पाते हैं. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/meta-viewport?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/meta-viewport.js | failureTitle": {
     "message": "`[user-scalable=\"no\"]` का इस्तेमाल `<meta name=\"viewport\">` एलिमेंट में किया जाता है या `[maximum-scale]` विशेषता पाँच से कम है."
@@ -255,7 +255,7 @@
     "message": "`[user-scalable=\"no\"]` का इस्तेमाल `<meta name=\"viewport\">` एलिमेंट में नहीं किया जा सकता और `[maximum-scale]` विशेषता पाँच से कम नहीं है."
   },
   "lighthouse-core/audits/accessibility/object-alt.js | description": {
-    "message": "Screen readers cannot translate non-text content. Adding alt text to `<object>` elements helps screen readers convey meaning to users. [Learn more](https://dequeuniversity.com/rules/axe/3.1/object-alt?application=lighthouse)."
+    "message": "स्क्रीन री़डर ऐसी सामग्री का अनुवाद नहीं कर सकते जिसमें टेक्स्ट नहीं होता है. `<object>` एलिमेंट में वैकल्पिक टेक्स्ट शामिल करने से, स्क्रीन रीडर को इस्तेमाल करने वालों को मतलब समझाने में मदद मिलती है. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/object-alt?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/object-alt.js | failureTitle": {
     "message": "`<object>` एलिमेंट में `[alt]` टेक्स्ट नहीं है"
@@ -264,7 +264,7 @@
     "message": "`<object>` एलिमेंट में `[alt]` टेक्स्ट है"
   },
   "lighthouse-core/audits/accessibility/tabindex.js | description": {
-    "message": "A value greater than 0 implies an explicit navigation ordering. Although technically valid, this often creates frustrating experiences for users who rely on assistive technologies. [Learn more](https://dequeuniversity.com/rules/axe/3.1/tabindex?application=lighthouse)."
+    "message": "0 से ज़्यादा मान साफ़ तौर पर नेविगेशन क्रमों को लागू करता है. हालांकि तकनीकी रूप से मान्य है. यह अक्सर ऐसे इस्तेमाल करने वालों को निराश करता है, जो सहायक तकनीकों पर भरोसा करते हैं. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/tabindex?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/tabindex.js | failureTitle": {
     "message": "कुछ एलिमेंट का `[tabindex]` मान 0 से ज़्यादा होता है"
@@ -273,7 +273,7 @@
     "message": "किसी भी एलिमेंट का `[tabindex]` मान 0 से ज़्यादा नहीं हो सकता"
   },
   "lighthouse-core/audits/accessibility/td-headers-attr.js | description": {
-    "message": "Screen readers have features to make navigating tables easier. Ensuring `<td>` cells using the `[headers]` attribute only refer to other cells in the same table may improve the experience for screen reader users. [Learn more](https://dequeuniversity.com/rules/axe/3.1/td-headers-attr?application=lighthouse)."
+    "message": "स्क्रीन रीडर में ऐसी सुविधाएं होती हैं जिनसे टेबल में जाना आसान हो जाता है. `[headers]` विशेषता का इस्तेमाल करने वाले `<td>` सेल सिर्फ़ उसी टेबल में दूसरे सेल का हवाला देते हैं, इसे पक्का करने पर स्क्रीन रीडर इस्तेमाल करने वालों को बेहतर अनुभव मिल सकता है. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/td-headers-attr?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/td-headers-attr.js | failureTitle": {
     "message": "`[headers]` विशेषता का इस्तेमाल करने वाले `<table>` एलिमेंट में मौजूद सेल, उसी टेबल में दूसरे सेल का हवाला देते हैं."
@@ -282,7 +282,7 @@
     "message": "`<table>` विशेषता का इस्तेमाल करने वाले `[headers]` एलिमेंट में मौजूद सेल सिर्फ़ उसी टेबल के दूसरे सेल का हवाला देते हैं."
   },
   "lighthouse-core/audits/accessibility/th-has-data-cells.js | description": {
-    "message": "Screen readers have features to make navigating tables easier. Ensuring table headers always refer to some set of cells may improve the experience for screen reader users. [Learn more](https://dequeuniversity.com/rules/axe/3.1/th-has-data-cells?application=lighthouse)."
+    "message": "स्क्रीन रीडर में ऐसी सुविधाएं होती हैं जिनसे टेबल में जाना आसान हो जाता है. अगर यह पक्का कर लिया जाए कि टेबल हेडर हमेशा सेल के कुछ सेट के बारे में बताते हैं, तो स्क्रीन रीडर इस्तेमाल करने वालों को बेहतर अनुभव मिल सकता है. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/th-has-data-cells?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/th-has-data-cells.js | failureTitle": {
     "message": "`<th>` एलिमेंट और `[role=\"columnheader\"/\"rowheader\"]` वाले एलिमेंट के पास उनके बताए हुए डेटा सेल नहीं हैं."
@@ -291,7 +291,7 @@
     "message": "`<th>` एलिमेंट और `[role=\"columnheader\"/\"rowheader\"]` वाले एलिमेंट के पास उनके बताए हुए डेटा सेल हैं."
   },
   "lighthouse-core/audits/accessibility/valid-lang.js | description": {
-    "message": "Specifying a valid [BCP 47 language](https://www.w3.org/International/questions/qa-choosing-language-tags#question) on elements helps ensure that text is pronounced correctly by a screen reader. [Learn more](https://dequeuniversity.com/rules/axe/3.1/valid-lang?application=lighthouse)."
+    "message": "एलिमेंट पर सही [BCP 47 भाषा](https://www.w3.org/International/questions/qa-choosing-language-tags#question) तय करने से यह पक्का करने में मदद मिलती है कि स्क्रीन रीडर ने टेक्स्ट को सही तरीके से बोला है. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/valid-lang?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/valid-lang.js | failureTitle": {
     "message": "`[lang]` विशेषताओं का कोई सही मान नहीं है"
@@ -300,7 +300,7 @@
     "message": "`[lang]` विशेषताओं में सही मान है"
   },
   "lighthouse-core/audits/accessibility/video-caption.js | description": {
-    "message": "When a video provides a caption it is easier for deaf and hearing impaired users to access its information. [Learn more](https://dequeuniversity.com/rules/axe/3.1/video-caption?application=lighthouse)."
+    "message": "वीडियो में कैप्शन उपलब्ध कराने पर ऐसे इस्तेमाल करने वाले लोग जानकारी को आसानी से समझ सकते हैं, जो सुन नहीं पाते हैं और जिन्हें कम सुनाई देता है. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/video-caption?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/video-caption.js | failureTitle": {
     "message": "`<video>` एलिमेंट में `[kind=\"captions\"]` वाले `<track>` नहीं हैं."
@@ -309,7 +309,7 @@
     "message": "`<video>` एलिमेंट में `[kind=\"captions\"]`वाला `<track>` एलिमेंट है"
   },
   "lighthouse-core/audits/accessibility/video-description.js | description": {
-    "message": "Audio descriptions provide relevant information for videos that dialogue cannot, such as facial expressions and scenes. [Learn more](https://dequeuniversity.com/rules/axe/3.1/video-description?application=lighthouse)."
+    "message": "ऑडियो के ब्यौरे में वीडियो से जुड़ी काम की ऐसी जानकारी दी जाती है, जो बातचीत से नहीं दी जा सकती, जैसे चेहरे के हावभाव और सीन. [ज़्यादा जानें](https://dequeuniversity.com/rules/axe/3.1/video-description?application=lighthouse)."
   },
   "lighthouse-core/audits/accessibility/video-description.js | failureTitle": {
     "message": "`<video>` एलिमेंट में `[kind=\"description\"]`वाले `<track>` एलिमेंट शामिल नहीं हैं."
@@ -327,7 +327,7 @@
     "message": "स्क्रिप्ट पार्स"
   },
   "lighthouse-core/audits/bootup-time.js | columnTotal": {
-    "message": "Total CPU Time"
+    "message": "कुल सीपीयू समय"
   },
   "lighthouse-core/audits/bootup-time.js | description": {
     "message": "JS को पार्स करने, कंपाइल करने और एक्ज़ीक्यूट करने में लगने वाला समय कम करने पर विचार करें. आप पाएंगे कि इसके ज़रिए छोटे-छोटे JS पेलोड डिलीवर करने में मदद मिलती है. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/bootup)."
@@ -381,10 +381,10 @@
     "message": "JavaScript का आकार कम करें"
   },
   "lighthouse-core/audits/byte-efficiency/unused-css-rules.js | description": {
-    "message": "Remove dead rules from stylesheets and defer the loading of CSS not used for above-the-fold content to reduce unnecessary bytes consumed by network activity. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/unused-css)."
+    "message": "स्टाइल-शीट से इस्तेमाल नहीं किए गए नियमों को हटाएं और पेज के ऊपरी हिस्से की सामग्री के लिए इस्तेमाल नहीं होने वाले CSS को लोड होने से टालें. ऐसा करके, नेटवर्क गतिविधि में खर्च होने वाले गैर-ज़रूरी बाइट कम किए जा सकते हैं. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/unused-css)."
   },
   "lighthouse-core/audits/byte-efficiency/unused-css-rules.js | title": {
-    "message": "Remove unused CSS"
+    "message": "इस्तेमाल नहीं किए गए CSS को हटाएं"
   },
   "lighthouse-core/audits/byte-efficiency/unused-javascript.js | description": {
     "message": "नेटवर्क गतिविधि में खर्च होने वाले बाइट में कमी करने के लिए इस्तेमाल नहीं किया गया JavaScript हटाएं."
@@ -477,19 +477,19 @@
     "message": "वेबफ़ॉन्ट लोड होने के दौरान सभी लेख दिखाई देते रहते हैं"
   },
   "lighthouse-core/audits/load-fast-enough-for-pwa.js | description": {
-    "message": "A fast page load over a cellular network ensures a good mobile user experience. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/fast-3g)."
+    "message": "सेल्युलर नेटवर्क पर तेज़ी से पेज लोड होने की सुविधा से मोबाइल इस्तेमाल करने वालों को अच्छा अनुभव मिलता है. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/fast-3g)."
   },
   "lighthouse-core/audits/load-fast-enough-for-pwa.js | displayValueText": {
-    "message": "Interactive at {timeInMs, number, seconds} s"
+    "message": "{timeInMs, number, seconds} सेकंड में इंटरैक्टिव"
   },
   "lighthouse-core/audits/load-fast-enough-for-pwa.js | displayValueTextWithOverride": {
-    "message": "Interactive on simulated mobile network at {timeInMs, number, seconds} s"
+    "message": "{timeInMs, number, seconds} सेकंड में सिम्युलेट किए गए मोबाइल नेटवर्क पर इंटरैक्टिव"
   },
   "lighthouse-core/audits/load-fast-enough-for-pwa.js | failureTitle": {
-    "message": "Page load is not fast enough on mobile networks"
+    "message": "मोबाइल नेटवर्क पर पेज लोड होने की गति ज़रूरत के मुताबिक तेज़ नहीं है"
   },
   "lighthouse-core/audits/load-fast-enough-for-pwa.js | title": {
-    "message": "Page load is fast enough on mobile networks"
+    "message": "मोबाइल नेटवर्क पर पेज लोड होने की गति ज़रूरत के मुताबिक तेज़ है"
   },
   "lighthouse-core/audits/mainthread-work-breakdown.js | columnCategory": {
     "message": "श्रेणी"
@@ -504,7 +504,7 @@
     "message": "मुख्य थ्रेड के काम को कम करता है"
   },
   "lighthouse-core/audits/metrics/estimated-input-latency.js | description": {
-    "message": "Estimated Input Latency is an estimate of how long your app takes to respond to user input, in milliseconds, during the busiest 5s window of page load. If your latency is higher than 50 ms, users may perceive your app as laggy. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/estimated-input-latency)."
+    "message": "अंदाजन इनपुट देरी इस बात का अनुमान होता है कि पेज लोड होने की सबसे व्यस्त 5 सेकंड की विंडो के दौरान आपके ऐप्लिकेशन को इस्तेमाल करने वाले के इनपुट का जवाब देने में, मिलीसेकंड में कितना समय लगेगा. अगर इंतज़ार का समय 60 मिलीसेकंड से ज़्यादा है, तो इस्तेमाल करने वाले आपके ऐप्लिकेशन को धीमा मान सकते हैं. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/estimated-input-latency)."
   },
   "lighthouse-core/audits/metrics/estimated-input-latency.js | title": {
     "message": "अनुमानित इनपुट प्रतीक्षा समय"
@@ -528,16 +528,16 @@
     "message": "पहला सार्थक पेंट"
   },
   "lighthouse-core/audits/metrics/interactive.js | description": {
-    "message": "Time to interactive is the amount of time it takes for the page to become fully interactive. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/consistently-interactive)."
+    "message": "इंटरैक्टिव में लगने वाला समय वह समय है जितनी देर में पेज पूरी तरह से इंटरैक्टिव हो जाता है. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/consistently-interactive)."
   },
   "lighthouse-core/audits/metrics/interactive.js | title": {
     "message": "इंटरएक्टिव में लगने वाला समय"
   },
   "lighthouse-core/audits/metrics/max-potential-fid.js | description": {
-    "message": "The potential First Input Delay that your users could experience is the duration, in milliseconds, of the longest task."
+    "message": "मिलीसेकंड में बताई जाने वाली सबसे लंबे टास्क की पहली संभावित इनपुट देरी. आपके इस्तेमाल करने वालों को यह समस्या आ सकती है."
   },
   "lighthouse-core/audits/metrics/max-potential-fid.js | title": {
-    "message": "Max Potential FID"
+    "message": "ज़्यादा से ज़्यादा संभावित एफ़आईडी"
   },
   "lighthouse-core/audits/metrics/speed-index.js | description": {
     "message": "गति इंडेक्स दिखाता है कि किसी पेज की सामग्री विज़ुअल रूप से कितनी तेज़ी से पॉपुलेट होती हैं. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/speed-index)."
@@ -546,16 +546,16 @@
     "message": "गति इंडेक्स"
   },
   "lighthouse-core/audits/network-rtt.js | description": {
-    "message": "Network round trip times (RTT) have a large impact on performance. If the RTT to an origin is high, it's an indication that servers closer to the user could improve performance. [Learn more](https://hpbn.co/primer-on-latency-and-bandwidth/)."
+    "message": "नेटवर्क के 'दोतरफ़ा यात्रा के समय' (आरटीटी) का परफ़ॉर्मेंस पर बहुत गहरा असर होता है. शुरुआत की जगह पर आरटीटी ज़्यादा होने से यह पता चलता है कि अगर सर्वर इस्तेमाल करने वालों के करीब होंगे, तो परफ़ॉर्मेंस बेहतर हो सकती है. [ज़्यादा जानें](https://hpbn.co/primer-on-latency-and-bandwidth/)."
   },
   "lighthouse-core/audits/network-rtt.js | title": {
-    "message": "Network Round Trip Times"
+    "message": "नेटवर्क का दोतरफ़ा यात्रा का समय"
   },
   "lighthouse-core/audits/network-server-latency.js | description": {
-    "message": "Server latencies can impact web performance. If the server latency of an origin is high, it's an indication the server is overloaded or has poor backend performance. [Learn more](https://hpbn.co/primer-on-web-performance/#analyzing-the-resource-waterfall)."
+    "message": "सर्वर के इंतज़ार के समय का असर वेब परफ़ॉर्मेंस पर पड़ सकता है. शुरुआत की जगह के सर्वर का इंतज़ार का समय ज़्यादा होने से यह पता चलता है कि सर्वर ओवरलोड हो गया है या उसकी बैकएंड परफ़ॉर्मेंस खराब है. [ज़्यादा जानें](https://hpbn.co/primer-on-web-performance/#analyzing-the-resource-waterfall)."
   },
   "lighthouse-core/audits/network-server-latency.js | title": {
-    "message": "Server Backend Latencies"
+    "message": "सर्वर बैकएंड के इंतज़ार का समय"
   },
   "lighthouse-core/audits/redirects.js | description": {
     "message": "'रीडायरेक्ट' पेज लोड किए जाने से पहले और ज़्यादा प्रतीक्षा जोड़ता है. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/redirects)."
@@ -564,160 +564,160 @@
     "message": "एक से ज़्यादा पेज रीडायरेक्ट करने से बचें"
   },
   "lighthouse-core/audits/seo/canonical.js | description": {
-    "message": "Canonical links suggest which URL to show in search results. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/canonical)."
+    "message": "कैननिकल लिंक, खोज नतीजों में दिखाए जाने वाले यूआरएल के बारे में बताते हैं. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/canonical)."
   },
   "lighthouse-core/audits/seo/canonical.js | explanationConflict": {
-    "message": "Multiple conflicting URLs ({urlList})"
+    "message": "कई विरोधी यूआरएल हैं ({urlList})"
   },
   "lighthouse-core/audits/seo/canonical.js | explanationDifferentDomain": {
-    "message": "Points to a different domain ({url})"
+    "message": "किसी अलग डोमेन पर ले जाता है ({url})"
   },
   "lighthouse-core/audits/seo/canonical.js | explanationInvalid": {
-    "message": "Invalid URL ({url})"
+    "message": "गलत यूआरएल ({url})"
   },
   "lighthouse-core/audits/seo/canonical.js | explanationPointsElsewhere": {
-    "message": "Points to another `hreflang` location ({url})"
+    "message": "किसी दूसरी `hreflang` जगह पर ले जाता है ({url})"
   },
   "lighthouse-core/audits/seo/canonical.js | explanationRelative": {
-    "message": "Relative URL ({url})"
+    "message": "मिलता-जुलता यूआरएल ({url})"
   },
   "lighthouse-core/audits/seo/canonical.js | explanationRoot": {
-    "message": "Points to the domain's root URL (the homepage), instead of an equivalent page of content"
+    "message": "मिलती-जुलती सामग्री वाले पेज पर ले जाने के बजाय डोमेन के रूट यूआरएल (होम पेज) पर ले जाता है"
   },
   "lighthouse-core/audits/seo/canonical.js | failureTitle": {
-    "message": "Document does not have a valid `rel=canonical`"
+    "message": "दस्तावेज़ में सही `rel=canonical` नहीं है"
   },
   "lighthouse-core/audits/seo/canonical.js | title": {
-    "message": "Document has a valid `rel=canonical`"
+    "message": "दस्तावेज़ में सही `rel=canonical` है"
   },
   "lighthouse-core/audits/seo/font-size.js | description": {
-    "message": "Font sizes less than 12px are too small to be legible and require mobile visitors to “pinch to zoom” in order to read. Strive to have >60% of page text ≥12px. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/font-sizes)."
+    "message": "पढ़ने के नज़रिए से 12px से कम आकार वाले फ़ॉन्ट काफ़ी छोटे होते हैं. ऐसा होने पर, मोबाइल से वेबसाइट पर आने वालों को पढ़ने के लिए “पिंच करके ज़ूम करना” होगा. पेज का 60% से ज़्यादा टेक्स्ट 12px का या उससे बड़े आकार में रखने की कोशिश करें. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/font-sizes)."
   },
   "lighthouse-core/audits/seo/font-size.js | displayValue": {
-    "message": "{decimalProportion, number, extendedPercent} legible text"
+    "message": "{decimalProportion, number, extendedPercent} टेक्स्ट पढ़ने लायक है"
   },
   "lighthouse-core/audits/seo/font-size.js | explanation": {
-    "message": "{decimalProportion, number, extendedPercent} of text is too small."
+    "message": "{decimalProportion, number, extendedPercent} टेक्स्ट काफ़ी छोटा है."
   },
   "lighthouse-core/audits/seo/font-size.js | explanationViewport": {
-    "message": "Text is illegible because there's no viewport meta tag optimized for mobile screens."
+    "message": "मोबाइल स्क्रीन के लिए कोई व्यूपोर्ट मेटा टैग ऑप्टिमाइज़ नहीं किए जाने की वजह से टेक्स्ट पढ़ने लायक नहीं है."
   },
   "lighthouse-core/audits/seo/font-size.js | explanationWithDisclaimer": {
-    "message": "{decimalProportion, number, extendedPercent} of text is too small (based on {decimalProportionVisited, number, extendedPercent} sample)."
+    "message": "{decimalProportion, number, extendedPercent} टेक्स्ट काफ़ी छोटा है ({decimalProportionVisited, number, extendedPercent} नमूने के आधार पर)."
   },
   "lighthouse-core/audits/seo/font-size.js | failureTitle": {
-    "message": "Document doesn't use legible font sizes"
+    "message": "दस्तावेज़ पढ़ने लायक फ़ॉन्ट आकारों का इस्तेमाल नहीं करता है"
   },
   "lighthouse-core/audits/seo/font-size.js | title": {
-    "message": "Document uses legible font sizes"
+    "message": "दस्तावेज़ पढ़ने लायक फ़ॉन्ट आकारों का इस्तेमाल करता है"
   },
   "lighthouse-core/audits/seo/hreflang.js | description": {
-    "message": "hreflang links tell search engines what version of a page they should list in search results for a given language or region. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/hreflang)."
+    "message": "hreflang लिंक से सर्च इंजन को यह पता चलता है कि किसी खास भाषा या क्षेत्र के लिए पेज के किस वर्शन को खोज नतीजों में रखना चाहिए. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/hreflang)."
   },
   "lighthouse-core/audits/seo/hreflang.js | failureTitle": {
-    "message": "Document doesn't have a valid `hreflang`"
+    "message": "दस्तावेज़ में सही `hreflang` नहीं है"
   },
   "lighthouse-core/audits/seo/hreflang.js | title": {
-    "message": "Document has a valid `hreflang`"
+    "message": "दस्तावेज़ में सही `hreflang` है"
   },
   "lighthouse-core/audits/seo/http-status-code.js | description": {
-    "message": "Pages with unsuccessful HTTP status codes may not be indexed properly. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/successful-http-code)."
+    "message": "ऐसे पेज ठीक से इंडेक्स नहीं किए जा सकते जिनमें काम नहीं करने वाले एचटीटीपी स्थिति कोड होते हैं. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/successful-http-code)."
   },
   "lighthouse-core/audits/seo/http-status-code.js | failureTitle": {
-    "message": "Page has unsuccessful HTTP status code"
+    "message": "पेज में काम नहीं करने वाला एचटीटीपी स्थिति कोड है"
   },
   "lighthouse-core/audits/seo/http-status-code.js | title": {
-    "message": "Page has successful HTTP status code"
+    "message": "पेज में काम करने वाला एचटीटीपी स्थिति कोड है"
   },
   "lighthouse-core/audits/seo/is-crawlable.js | description": {
-    "message": "Search engines are unable to include your pages in search results if they don't have permission to crawl them. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/indexing)."
+    "message": "अगर सर्च इंजन के पास आपके पेज क्रॉल करने की मंज़ूरी नहीं होती है, तो वे खोज नतीजों में आपके पेज शामिल नहीं कर पाएंगे. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/indexing)."
   },
   "lighthouse-core/audits/seo/is-crawlable.js | failureTitle": {
-    "message": "Page is blocked from indexing"
+    "message": "पेज को इंडेक्स करने से ब्लॉक किया गया है"
   },
   "lighthouse-core/audits/seo/is-crawlable.js | title": {
-    "message": "Page isn’t blocked from indexing"
+    "message": "पेज को इंडेक्स करने से ब्लॉक नहीं किया गया है"
   },
   "lighthouse-core/audits/seo/link-text.js | description": {
-    "message": "Descriptive link text helps search engines understand your content. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/descriptive-link-text)."
+    "message": "लिंक की पूरी जानकारी देने वाले टेक्स्ट की मदद से सर्च इंजन आपकी सामग्री के बारे में समझ पाते हैं. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/descriptive-link-text)."
   },
   "lighthouse-core/audits/seo/link-text.js | displayValue": {
-    "message": "{itemCount, plural,\n    =1 {1 link found}\n    other {# links found}\n    }"
+    "message": "{itemCount,plural, =1{1 लिंक मिला}one{# लिंक मिले}other{# लिंक मिले}}"
   },
   "lighthouse-core/audits/seo/link-text.js | failureTitle": {
-    "message": "Links do not have descriptive text"
+    "message": "लिंक में पूरी जानकारी देने वाला टेक्स्ट नहीं है"
   },
   "lighthouse-core/audits/seo/link-text.js | title": {
-    "message": "Links have descriptive text"
+    "message": "लिंक में पूरी जानकारी देने वाला टेक्स्ट है"
   },
   "lighthouse-core/audits/seo/manual/structured-data.js | description": {
-    "message": "Run the [Structured Data Testing Tool](https://search.google.com/structured-data/testing-tool/) and the [Structured Data Linter](http://linter.structured-data.org/) to validate structured data. [Learn more](https://developers.google.com/search/docs/guides/mark-up-content)."
+    "message": "स्ट्रक्चर्ड डेटा की पुष्टि करने के लिए [स्ट्रक्चर्ड डेटा टेस्टिंग टूल](https://search.google.com/structured-data/testing-tool/) और [स्ट्रक्चर्ड डेटा लिंटर](http://linter.structured-data.org/) चलाएं. [ज़्यादा जानें](https://developers.google.com/search/docs/guides/mark-up-content)."
   },
   "lighthouse-core/audits/seo/manual/structured-data.js | title": {
-    "message": "Structured data is valid"
+    "message": "स्ट्रक्चर्ड डेटा सही है"
   },
   "lighthouse-core/audits/seo/meta-description.js | description": {
-    "message": "Meta descriptions may be included in search results to concisely summarize page content. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/description)."
+    "message": "खोज नतीजों में संक्षिप्त विवरण शामिल हो सकते हैं ताकि पेज सामग्री के बारे में थोड़े शब्दों में खास जानकारी दी जा सके. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/description)."
   },
   "lighthouse-core/audits/seo/meta-description.js | explanation": {
-    "message": "Description text is empty."
+    "message": "ब्यौरे का टेक्स्ट खाली है."
   },
   "lighthouse-core/audits/seo/meta-description.js | failureTitle": {
-    "message": "Document does not have a meta description"
+    "message": "दस्तावेज़ में संक्षिप्त विवरण नहीं है"
   },
   "lighthouse-core/audits/seo/meta-description.js | title": {
-    "message": "Document has a meta description"
+    "message": "दस्तावेज़ में संक्षिप्त विवरण है"
   },
   "lighthouse-core/audits/seo/plugins.js | description": {
-    "message": "Search engines can't index plugin content, and many devices restrict plugins or don't support them. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/plugins)."
+    "message": "सर्च इंजन प्लग इन की सामग्री इंडेक्स नहीं कर सकते. साथ ही, कई डिवाइस प्लग इन पर पाबंदी लगाते हैं या वे उन पर काम नहीं करते हैं. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/plugins)."
   },
   "lighthouse-core/audits/seo/plugins.js | failureTitle": {
-    "message": "Document uses plugins"
+    "message": "दस्तावेज़ प्लग इन का इस्तेमाल करता है"
   },
   "lighthouse-core/audits/seo/plugins.js | title": {
-    "message": "Document avoids plugins"
+    "message": "दस्तावेज़ प्लग इन से बचता है"
   },
   "lighthouse-core/audits/seo/robots-txt.js | description": {
-    "message": "If your robots.txt file is malformed, crawlers may not be able to understand how you want your website to be crawled or indexed."
+    "message": "अगर आपकी robots.txt फ़ाइल सही नहीं है, तो क्रॉलर यह नहीं समझ पाएंगे कि आप अपनी वेबसाइट को किस तरह क्रॉल या इंडेक्स करना चाहते हैं."
   },
   "lighthouse-core/audits/seo/robots-txt.js | displayValueHttpBadCode": {
-    "message": "request for robots.txt returned HTTP status: {statusCode}"
+    "message": "robots.txt के अनुरोध ने यह एचटीटीपी स्थिति लौटाई है: {statusCode}"
   },
   "lighthouse-core/audits/seo/robots-txt.js | displayValueValidationError": {
-    "message": "{itemCount, plural,\n    =1 {1 error found}\n    other {# errors found}\n    }"
+    "message": "{itemCount,plural, =1{1 गड़बड़ी मिली}one{# गड़बड़ियां मिलीं}other{# गड़बड़ियां मिलीं}}"
   },
   "lighthouse-core/audits/seo/robots-txt.js | explanation": {
-    "message": "Lighthouse was unable to download a robots.txt file"
+    "message": "Lighthouse, robots.txt फ़ाइल डाउनलोड नहीं कर सका"
   },
   "lighthouse-core/audits/seo/robots-txt.js | failureTitle": {
-    "message": "robots.txt is not valid"
+    "message": "robots.txt सही नहीं है"
   },
   "lighthouse-core/audits/seo/robots-txt.js | title": {
-    "message": "robots.txt is valid"
+    "message": "robots.txt सही है"
   },
   "lighthouse-core/audits/seo/tap-targets.js | description": {
-    "message": "Interactive elements like buttons and links should be large enough (48x48px), and have enough space around them, to be easy enough to tap without overlapping onto other elements. [Learn more](https://developers.google.com/web/fundamentals/accessibility/accessible-styles#multi-device_responsive_design)."
+    "message": "बटन और लिंक जैसे इंटरैक्टिव एलिमेंट ज़रूरत के मुताबिक आकार वाले (48x48px) होने चाहिए और इनके आसपास जगह होनी चाहिए. ऐसा करने से, दूसरे एलिमेंट ओवरलैप किए बिना आसानी से उन पर टैप किया जा सकेगा. [ज़्यादा जानें](https://developers.google.com/web/fundamentals/accessibility/accessible-styles#multi-device_responsive_design)."
   },
   "lighthouse-core/audits/seo/tap-targets.js | displayValue": {
-    "message": "{decimalProportion, number, percent} appropriately sized tap targets"
+    "message": "टैप की जाने वाली {decimalProportion, number, percent} जगहें सही आकार में हैं"
   },
   "lighthouse-core/audits/seo/tap-targets.js | explanationViewportMetaNotOptimized": {
-    "message": "Tap targets are too small because there's no viewport meta tag optimized for mobile screens"
+    "message": "मोबाइल स्क्रीन के लिए कोई व्यूपोर्ट मेटा टैग ऑप्टिमाइज़ नहीं किए जाने की वजह से टैप की जाने वाली जगहें काफ़ी छोटी हैं"
   },
   "lighthouse-core/audits/seo/tap-targets.js | failureTitle": {
-    "message": "Tap targets are not sized appropriately"
+    "message": "टैप की जाने वाली जगहें सही आकार में नहीं हैं"
   },
   "lighthouse-core/audits/seo/tap-targets.js | overlappingTargetHeader": {
-    "message": "Overlapping Target"
+    "message": "ओवरलैप करने वाली जगह"
   },
   "lighthouse-core/audits/seo/tap-targets.js | sizeHeader": {
-    "message": "Size"
+    "message": "आकार"
   },
   "lighthouse-core/audits/seo/tap-targets.js | tapTargetHeader": {
-    "message": "Tap Target"
+    "message": "टैप की जाने वाली जगह"
   },
   "lighthouse-core/audits/seo/tap-targets.js | title": {
-    "message": "Tap targets are sized appropriately"
+    "message": "टैप की जाने वाली जगहें सही आकार में हैं"
   },
   "lighthouse-core/audits/time-to-first-byte.js | description": {
     "message": "पहली बाइट का समय, उस समय की पहचान करता है जब आपका सर्वर कोई जवाब भेजता है. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/ttfb)."
@@ -762,7 +762,7 @@
     "message": "ज़रूरी मूल से प्री-कनेक्ट करें"
   },
   "lighthouse-core/audits/uses-rel-preload.js | crossoriginWarning": {
-    "message": "A preload <link> was found for \"{preloadURL}\" but was not used by the browser. Check that you are using the `crossorigin` attribute properly."
+    "message": "\"{preloadURL}\" के लिए पहले से लोड <link> मिला, लेकिन ब्राउज़र ने इस्तेमाल नहीं किया था. यह जाँचें कि आप `crossorigin` विशेषता का इस्तेमाल ठीक से कर रहे हैं."
   },
   "lighthouse-core/audits/uses-rel-preload.js | description": {
     "message": "ऐसे संसाधन फ़ेच करने को प्राथमिकता देने के लिए <link rel=preload> का इस्तेमाल करने पर विचार करें जिनका फ़िलहाल पेज लोड में बाद में अनुरोध किया गया है. [ज़्यादा जानें](https://developers.google.com/web/tools/lighthouse/audits/preload)."
@@ -777,55 +777,55 @@
     "message": "ARIA"
   },
   "lighthouse-core/config/default-config.js | a11yAudioVideoGroupDescription": {
-    "message": "These are opportunities to provide alternative content for audio and video. This may improve the experience for users with hearing or vision impairments."
+    "message": "ये अवसर ऑडियो और वीडियो के लिए वैकल्पिक सामग्री मुहैया कराते हैं. इससे ऐसे इस्तेमाल करने वालों को बेहतर सुविधा मिल सकती है जो ठीक से सुन या देख नहीं पाते हैं."
   },
   "lighthouse-core/config/default-config.js | a11yAudioVideoGroupTitle": {
-    "message": "Audio and video"
+    "message": "ऑडियो और वीडियो"
   },
   "lighthouse-core/config/default-config.js | a11yBestPracticesGroupDescription": {
-    "message": "These items highlight common accessibility best practices."
+    "message": "ये आइटम सुलभता के सबसे अच्छे सामान्य तरीके हाइलाइट करते हैं."
   },
   "lighthouse-core/config/default-config.js | a11yBestPracticesGroupTitle": {
-    "message": "Best practices"
+    "message": "सबसे अच्छे तरीके"
   },
   "lighthouse-core/config/default-config.js | a11yCategoryDescription": {
-    "message": "These checks highlight opportunities to [improve the accessibility of your web app](https://developers.google.com/web/fundamentals/accessibility). Only a subset of accessibility issues can be automatically detected so manual testing is also encouraged."
+    "message": "ये जाँच [आपके वेब ऐप्लिकेशन की सुलभता को बेहतर बनाने](https://developers.google.com/web/fundamentals/accessibility) के अवसर हाइलाइट करती हैं. सिर्फ़ कुछ चुनिंदा सुलभता समस्याओं का अपने आप पता लगाया जा सकता है, इसलिए आपको खुद ही टेस्ट करने का भी सुझाव दिया जाता है."
   },
   "lighthouse-core/config/default-config.js | a11yCategoryManualDescription": {
-    "message": "These items address areas which an automated testing tool cannot cover. Learn more in our guide on [conducting an accessibility review](https://developers.google.com/web/fundamentals/accessibility/how-to-review)."
+    "message": "ये आइटम ऐसे मामलों में भी काम करते हैं जहां अपने आप काम करने वाला टेस्टिंग टूल नाकाम रहता है. हमारी मार्गदर्शिका में जाकर [सुलभता समीक्षा करने](https://developers.google.com/web/fundamentals/accessibility/how-to-review) के बारे में ज़्यादा जानें."
   },
   "lighthouse-core/config/default-config.js | a11yCategoryTitle": {
-    "message": "Accessibility"
+    "message": "सुलभता"
   },
   "lighthouse-core/config/default-config.js | a11yColorContrastGroupDescription": {
     "message": "आपकी सामग्री को पढ़ने में आसान बनाने के अवसर मौजूद हैं."
   },
   "lighthouse-core/config/default-config.js | a11yColorContrastGroupTitle": {
-    "message": "Contrast"
+    "message": "कंट्रास्ट"
   },
   "lighthouse-core/config/default-config.js | a11yLanguageGroupDescription": {
     "message": "इन अवसरों की मदद से कई भाषाओं में उपयोगकर्ताओं के ज़रिए आपकी सामग्री के प्रस्तुतिकरण को बेहतर बनाया जा सकता है."
   },
   "lighthouse-core/config/default-config.js | a11yLanguageGroupTitle": {
-    "message": "Internationalization and localization"
+    "message": "अंतरराष्ट्रीय और स्थानीय भाषा के अनुसार"
   },
   "lighthouse-core/config/default-config.js | a11yNamesLabelsGroupDescription": {
     "message": "इन अवसरों से आपके ऐप्लिकेशन में नियंत्रणों के सीमेंटिक (शब्दार्थ विज्ञान) को बेहतर बनाया जा सकता है. इससे उपयोगकर्ता का स्क्रीन रीडर जैसी सहायक तकनीक का अनुभव बेहतर हो सकता है."
   },
   "lighthouse-core/config/default-config.js | a11yNamesLabelsGroupTitle": {
-    "message": "Names and labels"
+    "message": "नाम और लेबल"
   },
   "lighthouse-core/config/default-config.js | a11yNavigationGroupDescription": {
-    "message": "These are opportunities to improve keyboard navigation in your application."
+    "message": "ये अवसर आपके ऐप्लिकेशन में कीबोर्ड नेविगेशन को बेहतर बनाते हैं."
   },
   "lighthouse-core/config/default-config.js | a11yNavigationGroupTitle": {
-    "message": "Navigation"
+    "message": "नेविगेशन"
   },
   "lighthouse-core/config/default-config.js | a11yTablesListsVideoGroupDescription": {
-    "message": "These are opportunities to to improve the experience of reading tabular or list data using assistive technology, like a screen reader."
+    "message": "ये अवसर स्क्रीन रीडर जैसी सहायक तकनीक का इस्तेमाल करके टेबल या सूची का डेटा पढ़ने की सुविधा बेहतर बनाते हैं."
   },
   "lighthouse-core/config/default-config.js | a11yTablesListsVideoGroupTitle": {
-    "message": "Tables and lists"
+    "message": "टेबल और सूचियां"
   },
   "lighthouse-core/config/default-config.js | diagnosticsGroupDescription": {
     "message": "आपके ऐप्लिकेशन के परफ़ॉर्मेंस के बारे में ज़्यादा जानकारी."
@@ -867,10 +867,10 @@
     "message": "PWA ऑप्टिमाइज़ किया गया"
   },
   "lighthouse-core/config/default-config.js | seoCategoryDescription": {
-    "message": "These checks ensure that your page is optimized for search engine results ranking. There are additional factors Lighthouse does not check that may affect your search ranking. [Learn more](https://support.google.com/webmasters/answer/35769)."
+    "message": "ये जाँच पक्का करती हैं कि आपका पेज सर्च इंजन के नतीजे रैंक करने के लिए ऑप्टिमाइज़ किया हुआ है. दूसरी वजहों से Lighthouse जाँच नहीं करता है. इसका असर आपकी खोज की रैंकिंग पर पड़ सकता है. [ज़्यादा जानें](https://support.google.com/webmasters/answer/35769)."
   },
   "lighthouse-core/config/default-config.js | seoCategoryManualDescription": {
-    "message": "Run these additional validators on your site to check additional SEO best practices."
+    "message": "SEO के दूसरे सबसे अच्छे तरीके देखने के लिए अपनी साइट पर पुष्टि करने वाले ये और भी टूल चलाएं."
   },
   "lighthouse-core/config/default-config.js | seoCategoryTitle": {
     "message": "SEO"
@@ -924,7 +924,7 @@
     "message": "Lighthouse उस यूआरएल को भरोसेमंद रूप से लोड नहीं कर सका जिसका आपने अनुरोध किया था क्योंकि पेज ने काम करना बंद कर दिया था."
   },
   "lighthouse-core/lib/lh-error.js | pageLoadFailedInsecure": {
-    "message": "The URL you have provided does not have valid security credentials. {securityMessages}"
+    "message": "आपके दिए गए यूआरएल के लिए सही सुरक्षा क्रेडेंशियल नहीं हैं. {securityMessages}"
   },
   "lighthouse-core/lib/lh-error.js | pageLoadFailedWithDetails": {
     "message": "Lighthouse आपका अनुरोध किया गया पेज ठीक से लोड नहीं कर सका. पक्का करें कि आप सही यूआरएल को टेस्ट कर रहे हैं और सर्वर सभी अनुरोधों के लिए ठीक से काम कर रहा है. (जानकारी: {errorDetails})"
@@ -984,10 +984,10 @@
     "message": "स्कोर स्केल:"
   },
   "lighthouse-core/report/html/renderer/util.js | snippetCollapseButtonLabel": {
-    "message": "Collapse snippet"
+    "message": "स्निपेट को छोटा करें"
   },
   "lighthouse-core/report/html/renderer/util.js | snippetExpandButtonLabel": {
-    "message": "Expand snippet"
+    "message": "स्निपेट को बड़ा करें"
   },
   "lighthouse-core/report/html/renderer/util.js | toplevelWarningsMessage": {
     "message": "कुछ समस्याएं आने के कारण Lighthouse के इस रन पर असर पड़ा है:"
@@ -996,7 +996,7 @@
     "message": "मान अनुमानित हैं और इनमें अंतर हो सकता है."
   },
   "lighthouse-core/report/html/renderer/util.js | warningAuditsGroupTitle": {
-    "message": "Passed audits but with warnings"
+    "message": "चेतावनियों के साथ पास हुए ऑडिट"
   },
   "lighthouse-core/report/html/renderer/util.js | warningHeader": {
     "message": "चेतावनियां: "
diff --git a/lighthouse-core/lib/network-recorder.js b/lighthouse-core/lib/network-recorder.js
index f0f05e64a..070a3357f 100644
--- a/lighthouse-core/lib/network-recorder.js
+++ b/lighthouse-core/lib/network-recorder.js
@@ -196,10 +196,11 @@ class NetworkRecorder extends EventEmitter {
   // DevTools SDK network layer.
 
   /**
-   * @param {LH.Crdp.Network.RequestWillBeSentEvent} data
+   * @param {{params: LH.Crdp.Network.RequestWillBeSentEvent, source?: LH.Protocol.RawSource}} event
    */
-  onRequestWillBeSent(data) {
-    const originalRequest = this._findRealRequest(data.requestId);
+  onRequestWillBeSent(event) {
+    const data = event.params;
+    const originalRequest = this._findRealRequestAndSetSource(data.requestId, event.source);
     // This is a simple new request, create the NetworkRequest object and finish.
     if (!originalRequest) {
       const request = new NetworkRequest();
@@ -235,57 +236,63 @@ class NetworkRecorder extends EventEmitter {
   }
 
   /**
-   * @param {LH.Crdp.Network.RequestServedFromCacheEvent} data
+   * @param {{params: LH.Crdp.Network.RequestServedFromCacheEvent, source?: LH.Protocol.RawSource}} event
    */
-  onRequestServedFromCache(data) {
-    const request = this._findRealRequest(data.requestId);
+  onRequestServedFromCache(event) {
+    const data = event.params;
+    const request = this._findRealRequestAndSetSource(data.requestId, event.source);
     if (!request) return;
     request.onRequestServedFromCache();
   }
 
   /**
-   * @param {LH.Crdp.Network.ResponseReceivedEvent} data
+   * @param {{params: LH.Crdp.Network.ResponseReceivedEvent, source?: LH.Protocol.RawSource}} event
    */
-  onResponseReceived(data) {
-    const request = this._findRealRequest(data.requestId);
+  onResponseReceived(event) {
+    const data = event.params;
+    const request = this._findRealRequestAndSetSource(data.requestId, event.source);
     if (!request) return;
     request.onResponseReceived(data);
   }
 
   /**
-   * @param {LH.Crdp.Network.DataReceivedEvent} data
+   * @param {{params: LH.Crdp.Network.DataReceivedEvent, source?: LH.Protocol.RawSource}} event
    */
-  onDataReceived(data) {
-    const request = this._findRealRequest(data.requestId);
+  onDataReceived(event) {
+    const data = event.params;
+    const request = this._findRealRequestAndSetSource(data.requestId, event.source);
     if (!request) return;
     request.onDataReceived(data);
   }
 
   /**
-   * @param {LH.Crdp.Network.LoadingFinishedEvent} data
+   * @param {{params: LH.Crdp.Network.LoadingFinishedEvent, source?: LH.Protocol.RawSource}} event
    */
-  onLoadingFinished(data) {
-    const request = this._findRealRequest(data.requestId);
+  onLoadingFinished(event) {
+    const data = event.params;
+    const request = this._findRealRequestAndSetSource(data.requestId, event.source);
     if (!request) return;
     request.onLoadingFinished(data);
     this.onRequestFinished(request);
   }
 
   /**
-   * @param {LH.Crdp.Network.LoadingFailedEvent} data
+   * @param {{params: LH.Crdp.Network.LoadingFailedEvent, source?: LH.Protocol.RawSource}} event
    */
-  onLoadingFailed(data) {
-    const request = this._findRealRequest(data.requestId);
+  onLoadingFailed(event) {
+    const data = event.params;
+    const request = this._findRealRequestAndSetSource(data.requestId, event.source);
     if (!request) return;
     request.onLoadingFailed(data);
     this.onRequestFinished(request);
   }
 
   /**
-   * @param {LH.Crdp.Network.ResourceChangedPriorityEvent} data
+   * @param {{params: LH.Crdp.Network.ResourceChangedPriorityEvent, source?: LH.Protocol.RawSource}} event
    */
-  onResourceChangedPriority(data) {
-    const request = this._findRealRequest(data.requestId);
+  onResourceChangedPriority(event) {
+    const data = event.params;
+    const request = this._findRealRequestAndSetSource(data.requestId, event.source);
     if (!request) return;
     request.onResourceChangedPriority(data);
   }
@@ -296,13 +303,13 @@ class NetworkRecorder extends EventEmitter {
    */
   dispatch(event) {
     switch (event.method) {
-      case 'Network.requestWillBeSent': return this.onRequestWillBeSent(event.params);
-      case 'Network.requestServedFromCache': return this.onRequestServedFromCache(event.params);
-      case 'Network.responseReceived': return this.onResponseReceived(event.params);
-      case 'Network.dataReceived': return this.onDataReceived(event.params);
-      case 'Network.loadingFinished': return this.onLoadingFinished(event.params);
-      case 'Network.loadingFailed': return this.onLoadingFailed(event.params);
-      case 'Network.resourceChangedPriority': return this.onResourceChangedPriority(event.params);
+      case 'Network.requestWillBeSent': return this.onRequestWillBeSent(event);
+      case 'Network.requestServedFromCache': return this.onRequestServedFromCache(event);
+      case 'Network.responseReceived': return this.onResponseReceived(event);
+      case 'Network.dataReceived': return this.onDataReceived(event);
+      case 'Network.loadingFinished': return this.onLoadingFinished(event);
+      case 'Network.loadingFailed': return this.onLoadingFailed(event);
+      case 'Network.resourceChangedPriority': return this.onResourceChangedPriority(event);
       default: return;
     }
   }
@@ -314,9 +321,10 @@ class NetworkRecorder extends EventEmitter {
    * message is referring.
    *
    * @param {string} requestId
+   * @param {LH.Protocol.RawSource|undefined} source
    * @return {NetworkRequest|undefined}
    */
-  _findRealRequest(requestId) {
+  _findRealRequestAndSetSource(requestId, source) {
     let request = this._recordsById.get(requestId);
     if (!request || !request.isValid) return undefined;
 
@@ -324,6 +332,7 @@ class NetworkRecorder extends EventEmitter {
       request = request.redirectDestination;
     }
 
+    request.setSource(source);
     return request;
   }
 
diff --git a/lighthouse-core/lib/network-request.js b/lighthouse-core/lib/network-request.js
index 687924e0b..bfd64351e 100644
--- a/lighthouse-core/lib/network-request.js
+++ b/lighthouse-core/lib/network-request.js
@@ -103,6 +103,18 @@ module.exports = class NetworkRequest {
     this.fetchedViaServiceWorker = false;
     /** @type {string|undefined} */
     this.frameId = '';
+    /**
+     * @type {string|undefined}
+     * Only set for OOPIFs. This is the targetId of the protocol target from which this
+     * request came. Undefined means it came from the root.
+     */
+    this.targetId = undefined;
+    /**
+     * @type {string|undefined}
+     * Only set for OOPIFs. This is the sessionId of the protocol connection on which this
+     * request was discovered. Undefined means it came from the root.
+     */
+    this.sessionId = undefined;
     this.isLinkPreload = false;
   }
 
@@ -233,6 +245,19 @@ module.exports = class NetworkRequest {
     this._updateResponseReceivedTimeIfNecessary();
   }
 
+  /**
+   * @param {LH.Protocol.RawSource|undefined} source
+   */
+  setSource(source) {
+    if (source) {
+      this.targetId = source.targetId;
+      this.sessionId = source.sessionId;
+    } else {
+      this.targetId = undefined;
+      this.sessionId = undefined;
+    }
+  }
+
   /**
    * @param {LH.Crdp.Network.Response} response
    * @param {number} timestamp
diff --git a/lighthouse-core/runner.js b/lighthouse-core/runner.js
index b021b81cb..4f0ea5985 100644
--- a/lighthouse-core/runner.js
+++ b/lighthouse-core/runner.js
@@ -179,7 +179,16 @@ class Runner {
       ...timingEntriesFromRunner,
       // As entries can share a name, dedupe based on the startTime timestamp
     ].map(entry => /** @type {[number, PerformanceEntry]} */ ([entry.startTime, entry]));
-    const timingEntries = Array.from(new Map(timingEntriesKeyValues).values());
+    const timingEntries = Array.from(new Map(timingEntriesKeyValues).values())
+    // Truncate timestamps to hundredths of a millisecond saves ~4KB. No need for microsecond
+    // resolution.
+    .map(entry => {
+      return /** @type {PerformanceEntry} */ ({
+        ...entry,
+        duration: parseFloat(entry.duration.toFixed(2)),
+        startTime: parseFloat(entry.startTime.toFixed(2)),
+      });
+    });
     const runnerEntry = timingEntries.find(e => e.name === 'lh:runner:run');
     return {entries: timingEntries, total: runnerEntry && runnerEntry.duration || 0};
   }
diff --git a/proto/sample_v2_round_trip.json b/proto/sample_v2_round_trip.json
index 3bc2761a1..ed7cd2fe6 100644
--- a/proto/sample_v2_round_trip.json
+++ b/proto/sample_v2_round_trip.json
@@ -1248,11 +1248,6 @@
             "description": "Lists the toplevel main thread tasks that executed during page load.", 
             "details": {
                 "headings": [
-                    {
-                        "itemType": "text", 
-                        "key": "type", 
-                        "text": "Task Type"
-                    }, 
                     {
                         "granularity": 1.0, 
                         "itemType": "ms", 
@@ -1268,199 +1263,68 @@
                 ], 
                 "items": [
                     {
-                        "duration": 26.823, 
-                        "startTime": 604.125, 
-                        "type": "other"
-                    }, 
-                    {
-                        "duration": 23.175, 
-                        "startTime": 606.246, 
-                        "type": "other"
-                    }, 
-                    {
-                        "duration": 5.89, 
-                        "startTime": 631.458, 
-                        "type": "other"
-                    }, 
-                    {
-                        "duration": 18.85, 
-                        "startTime": 638.562, 
-                        "type": "other"
-                    }, 
-                    {
-                        "duration": 16.58, 
-                        "startTime": 638.577, 
-                        "type": "parseHTML"
-                    }, 
-                    {
-                        "duration": 12.522, 
-                        "startTime": 638.586, 
-                        "type": "parseHTML"
-                    }, 
-                    {
-                        "duration": 5.897, 
-                        "startTime": 668.051, 
-                        "type": "other"
-                    }, 
-                    {
-                        "duration": 5.046, 
-                        "startTime": 668.09, 
-                        "type": "other"
-                    }, 
-                    {
-                        "duration": 5.516, 
-                        "startTime": 2643.396, 
-                        "type": "other"
-                    }, 
-                    {
-                        "duration": 979.165, 
-                        "startTime": 2857.942, 
-                        "type": "other"
-                    }, 
-                    {
-                        "duration": 5.803, 
-                        "startTime": 2858.62, 
-                        "type": "scriptEvaluation"
-                    }, 
-                    {
-                        "duration": 970.698, 
-                        "startTime": 2865.033, 
-                        "type": "parseHTML"
-                    }, 
-                    {
-                        "duration": 19.463, 
-                        "startTime": 2866.527, 
-                        "type": "parseHTML"
-                    }, 
-                    {
-                        "duration": 19.21, 
-                        "startTime": 2866.571, 
-                        "type": "scriptEvaluation"
-                    }, 
-                    {
-                        "duration": 945.898, 
-                        "startTime": 2889.816, 
-                        "type": "parseHTML"
-                    }, 
-                    {
-                        "duration": 944.888, 
-                        "startTime": 2890.118, 
-                        "type": "scriptEvaluation"
-                    }, 
-                    {
-                        "duration": 9.047, 
-                        "startTime": 3023.783, 
-                        "type": "scriptEvaluation"
-                    }, 
-                    {
-                        "duration": 5.97, 
-                        "startTime": 3023.842, 
-                        "type": "garbageCollection"
-                    }, 
-                    {
-                        "duration": 706.85, 
-                        "startTime": 3070.433, 
-                        "type": "scriptEvaluation"
-                    }, 
-                    {
-                        "duration": 123.208, 
-                        "startTime": 3840.307, 
-                        "type": "other"
-                    }, 
-                    {
-                        "duration": 123.116, 
-                        "startTime": 3840.313, 
-                        "type": "other"
-                    }, 
-                    {
-                        "duration": 30.142, 
-                        "startTime": 3840.667, 
-                        "type": "styleLayout"
-                    }, 
-                    {
-                        "duration": 87.039, 
-                        "startTime": 3870.824, 
-                        "type": "other"
-                    }, 
-                    {
-                        "duration": 86.959, 
-                        "startTime": 3870.844, 
-                        "type": "styleLayout"
+                        "duration": 5.272, 
+                        "startTime": 598.738
                     }, 
                     {
-                        "duration": 85.493, 
-                        "startTime": 3871.238, 
-                        "type": "styleLayout"
+                        "duration": 26.926, 
+                        "startTime": 604.035
                     }, 
                     {
-                        "duration": 94.261, 
-                        "startTime": 3996.555, 
-                        "type": "other"
+                        "duration": 6.681, 
+                        "startTime": 630.994
                     }, 
                     {
-                        "duration": 90.663, 
-                        "startTime": 3997.797, 
-                        "type": "scriptEvaluation"
+                        "duration": 19.718, 
+                        "startTime": 637.706
                     }, 
                     {
-                        "duration": 5.709, 
-                        "startTime": 4019.279, 
-                        "type": "garbageCollection"
+                        "duration": 7.812, 
+                        "startTime": 666.147
                     }, 
                     {
-                        "duration": 5.151, 
-                        "startTime": 4098.541, 
-                        "type": "other"
+                        "duration": 10.087, 
+                        "startTime": 2639.59
                     }, 
                     {
-                        "duration": 122.537, 
-                        "startTime": 4808.624, 
-                        "type": "other"
+                        "duration": 983.081, 
+                        "startTime": 2854.095
                     }, 
                     {
-                        "duration": 82.741, 
-                        "startTime": 4810.0, 
-                        "type": "scriptEvaluation"
+                        "duration": 123.752, 
+                        "startTime": 3839.839
                     }, 
                     {
-                        "duration": 36.919, 
-                        "startTime": 4893.397, 
-                        "type": "parseHTML"
+                        "duration": 5.338, 
+                        "startTime": 3980.383
                     }, 
                     {
-                        "duration": 36.866, 
-                        "startTime": 4893.44, 
-                        "type": "parseHTML"
+                        "duration": 6.574, 
+                        "startTime": 3986.1
                     }, 
                     {
-                        "duration": 10.58, 
-                        "startTime": 4894.119, 
-                        "type": "scriptEvaluation"
+                        "duration": 96.198, 
+                        "startTime": 3996.134
                     }, 
                     {
-                        "duration": 9.353, 
-                        "startTime": 4894.151, 
-                        "type": "scriptEvaluation"
+                        "duration": 9.23, 
+                        "startTime": 4098.379
                     }, 
                     {
-                        "duration": 22.297, 
-                        "startTime": 4905.447, 
-                        "type": "parseHTML"
+                        "duration": 5.463, 
+                        "startTime": 4784.783
                     }, 
                     {
-                        "duration": 6.849, 
-                        "startTime": 8603.435, 
-                        "type": "other"
+                        "duration": 127.282, 
+                        "startTime": 4808.602
                     }, 
                     {
-                        "duration": 7.076, 
-                        "startTime": 10219.463, 
-                        "type": "other"
+                        "duration": 6.885, 
+                        "startTime": 8603.411
                     }, 
                     {
-                        "duration": 6.983, 
-                        "startTime": 10219.523, 
-                        "type": "scriptEvaluation"
+                        "duration": 7.258, 
+                        "startTime": 10219.292
                     }
                 ], 
                 "type": "table"
diff --git a/types/protocol.d.ts b/types/protocol.d.ts
index e80525c24..61c3ec580 100644
--- a/types/protocol.d.ts
+++ b/types/protocol.d.ts
@@ -12,6 +12,11 @@ declare global {
      */
     export type RawEventMessage = RawEventMessageRecord[keyof RawEventMessageRecord];
 
+    export interface RawSource {
+      targetId?: string;
+      sessionId: string;
+    }
+
     /**
      * Raw (over the wire) message format of all possible Crdp command responses.
      */
@@ -65,6 +70,8 @@ type RawEventMessageRecord = {
     method: K,
     // Drop [] for `undefined` (so a JS value is valid).
     params: LH.CrdpEvents[K] extends [] ? undefined: LH.CrdpEvents[K][number]
+    // If the source is not set, it means the event was from the root target.
+    source?: LH.Protocol.RawSource
   };
 }
 


Test patch:
diff --git a/lighthouse-cli/test/smokehouse/perf/expectations.js b/lighthouse-cli/test/smokehouse/perf/expectations.js
index fc8fcf297..3cb71b073 100644
--- a/lighthouse-cli/test/smokehouse/perf/expectations.js
+++ b/lighthouse-cli/test/smokehouse/perf/expectations.js
@@ -69,7 +69,6 @@ module.exports = [
     audits: {
       'font-display': {
         score: 0,
-        rawValue: false,
         details: {
           items: {
             length: 2,
diff --git a/lighthouse-cli/test/smokehouse/seo/expectations.js b/lighthouse-cli/test/smokehouse/seo/expectations.js
index 6fe1913f9..cf16ab81a 100644
--- a/lighthouse-cli/test/smokehouse/seo/expectations.js
+++ b/lighthouse-cli/test/smokehouse/seo/expectations.js
@@ -53,7 +53,7 @@ module.exports = [
         score: 1,
       },
       'font-size': {
-        rawValue: true,
+        score: 1,
         details: {
           items: {
             length: 6,
@@ -76,7 +76,7 @@ module.exports = [
         score: 1,
       },
       'robots-txt': {
-        rawValue: true,
+        score: null,
         scoreDisplayMode: 'notApplicable',
       },
     },
@@ -98,7 +98,7 @@ module.exports = [
         score: 1,
       },
       'font-size': {
-        rawValue: false,
+        score: 0,
         explanation:
           'Text is illegible because there\'s no viewport meta tag optimized for mobile screens.',
       },
diff --git a/lighthouse-core/test/audits/main-thread-tasks-test.js b/lighthouse-core/test/audits/main-thread-tasks-test.js
index fe34b94f2..9840afe62 100644
--- a/lighthouse-core/test/audits/main-thread-tasks-test.js
+++ b/lighthouse-core/test/audits/main-thread-tasks-test.js
@@ -15,7 +15,7 @@ describe('Main thread tasks audit', () => {
   it('should work', async () => {
     const artifacts = {traces: {defaultPass: acceptableTrace}};
     const result = await MainThreadTasks.audit(artifacts, {computedCache: new Map()});
-    expect(result.details.items).toHaveLength(76);
+    expect(result.details.items).toHaveLength(39);
 
     for (const item of result.details.items) {
       expect(Number.isFinite(item.startTime)).toBeTruthy();
diff --git a/lighthouse-core/test/computed/main-thread-tasks-test.js b/lighthouse-core/test/computed/main-thread-tasks-test.js
index e3babd95e..7dbca9dd7 100644
--- a/lighthouse-core/test/computed/main-thread-tasks-test.js
+++ b/lighthouse-core/test/computed/main-thread-tasks-test.js
@@ -143,4 +143,56 @@ describe('MainResource computed artifact', () => {
     assert.deepStrictEqual(taskC.attributableURLs, ['urlB.1', 'urlB.2', 'urlC']);
     assert.deepStrictEqual(taskD.attributableURLs, ['urlB.1', 'urlB.2', 'urlC', 'urlD']);
   });
+
+  it('should handle the last trace event not ending', async () => {
+    /*
+    An artistic rendering of the below trace:
+    █████████████████████████████TaskA████████████|
+          ████████████████TaskB███████████████████|
+                                            █TaskC|
+                                                  ^ trace abruptly ended
+    */
+    const traceEvents = [
+      ...boilerplateTrace,
+      // These events would normally be accompanied by an 'E' event
+      {ph: 'B', name: 'TaskA', pid, tid, ts: baseTs, args},
+      {ph: 'B', name: 'TaskB', pid, tid, ts: baseTs + 5e3, args},
+      {ph: 'B', name: 'TaskC', pid, tid, ts: baseTs + 100e3, args},
+    ];
+
+    traceEvents.forEach(evt => Object.assign(evt, {cat: 'devtools.timeline'}));
+
+    const context = {computedCache: new Map()};
+    const tasks = await MainThreadTasks.request({traceEvents}, context);
+    expect(tasks).toHaveLength(3);
+
+    const taskA = tasks.find(task => task.event.name === 'TaskA');
+    const taskB = tasks.find(task => task.event.name === 'TaskB');
+    const taskC = tasks.find(task => task.event.name === 'TaskC');
+    expect(taskA).toEqual({
+      parent: undefined,
+      attributableURLs: [],
+
+      children: [taskB],
+      event: traceEvents[3],
+      startTime: 0,
+      endTime: 100,
+      duration: 100,
+      selfTime: 5,
+      group: taskGroups.other,
+    });
+
+    expect(taskB).toEqual({
+      parent: taskA,
+      attributableURLs: [],
+
+      children: [taskC],
+      event: traceEvents[4],
+      startTime: 5,
+      endTime: 100,
+      duration: 95,
+      selfTime: 95,
+      group: taskGroups.other,
+    });
+  });
 });
diff --git a/lighthouse-core/test/gather/gatherers/dobetterweb/optimized-images-test.js b/lighthouse-core/test/gather/gatherers/dobetterweb/optimized-images-test.js
index 7af9f3e10..ed1efec97 100644
--- a/lighthouse-core/test/gather/gatherers/dobetterweb/optimized-images-test.js
+++ b/lighthouse-core/test/gather/gatherers/dobetterweb/optimized-images-test.js
@@ -68,6 +68,16 @@ const traceData = {
       resourceType: 'Image',
       finished: true,
     },
+    {
+      requestId: '1',
+      url: 'http://gmail.com/image.jpg',
+      mimeType: 'image/jpeg',
+      resourceSize: 15000,
+      transferSize: 20000,
+      resourceType: 'Image',
+      finished: true,
+      sessionId: 'oopif', // ignore for being an oopif
+    },
     {
       requestId: '1',
       url: 'data: image/jpeg ; base64 ,SgVcAT32587935321...',
diff --git a/lighthouse-core/test/gather/gatherers/dobetterweb/response-compression-test.js b/lighthouse-core/test/gather/gatherers/dobetterweb/response-compression-test.js
index 954dd908e..d885acf95 100644
--- a/lighthouse-core/test/gather/gatherers/dobetterweb/response-compression-test.js
+++ b/lighthouse-core/test/gather/gatherers/dobetterweb/response-compression-test.js
@@ -55,6 +55,19 @@ const traceData = {
       content: '1234567',
       finished: true,
     },
+    {
+      url: 'http://google.com/index.json',
+      _statusCode: 200,
+      mimeType: 'application/json',
+      requestId: 27,
+      resourceSize: 7,
+      transferSize: 8,
+      resourceType: 'XHR',
+      responseHeaders: [],
+      content: '1234567',
+      finished: true,
+      sessionId: 'oopif', // ignore for being from oopif
+    },
     {
       url: 'http://google.com/index.json',
       _statusCode: 304, // ignore for being a cache not modified response
diff --git a/lighthouse-core/test/lib/dependency-graph/base-node-test.js b/lighthouse-core/test/lib/dependency-graph/base-node-test.js
index 9696caae4..97a3043f7 100644
--- a/lighthouse-core/test/lib/dependency-graph/base-node-test.js
+++ b/lighthouse-core/test/lib/dependency-graph/base-node-test.js
@@ -207,6 +207,15 @@ describe('DependencyGraph/Node', () => {
       assert.equal(clonedIdMap.get('G'), undefined);
       assert.equal(clonedIdMap.get('H'), undefined);
     });
+
+    it('should throw if original node is not in cloned graph', () => {
+      const graph = createComplexGraph();
+      assert.throws(
+        // clone from root to nodeB, but called on nodeD
+        _ => graph.nodeD.cloneWithRelationships(node => node.id === 'B'),
+        /^Error: Cloned graph missing node$/
+      );
+    });
   });
 
   describe('.traverse', () => {
@@ -218,6 +227,26 @@ describe('DependencyGraph/Node', () => {
       assert.deepEqual(ids, ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']);
     });
 
+    it('should include a shortest traversal path to every dependent node', () => {
+      const graph = createComplexGraph();
+      const paths = [];
+      graph.nodeA.traverse((node, traversalPath) => {
+        assert.strictEqual(node.id, traversalPath[0].id);
+        paths.push(traversalPath.map(node => node.id));
+      });
+
+      assert.deepStrictEqual(paths, [
+        ['A'],
+        ['B', 'A'],
+        ['C', 'A'],
+        ['D', 'B', 'A'],
+        ['E', 'D', 'B', 'A'],
+        ['F', 'E', 'D', 'B', 'A'],
+        ['G', 'E', 'D', 'B', 'A'],
+        ['H', 'G', 'E', 'D', 'B', 'A'],
+      ]);
+    });
+
     it('should respect getNext', () => {
       const graph = createComplexGraph();
       const ids = [];
diff --git a/lighthouse-core/test/lib/network-recorder-test.js b/lighthouse-core/test/lib/network-recorder-test.js
index 671ad53a8..1c47eea76 100644
--- a/lighthouse-core/test/lib/network-recorder-test.js
+++ b/lighthouse-core/test/lib/network-recorder-test.js
@@ -180,6 +180,36 @@ describe('network recorder', function() {
     });
   });
 
+  it('should set the source of network records', () => {
+    const devtoolsLogs = networkRecordsToDevtoolsLog([
+      {url: 'http://example.com'},
+      {url: 'http://iframe.com'},
+      {url: 'http://other-iframe.com'},
+    ]);
+
+    const requestId1 = devtoolsLogs.find(
+      log => log.params.request && log.params.request.url === 'http://iframe.com'
+    ).params.requestId;
+    const requestId2 = devtoolsLogs.find(
+      log => log.params.request && log.params.request.url === 'http://other-iframe.com'
+    ).params.requestId;
+
+    for (const log of devtoolsLogs) {
+      if (log.params.requestId === requestId1) log.source = {sessionId: '1', targetId: 'a'};
+
+      if (log.params.requestId === requestId2 && log.method === 'Network.loadingFinished') {
+        log.source = {sessionId: '2', targetId: 'b'};
+      }
+    }
+
+    const records = NetworkRecorder.recordsFromLogs(devtoolsLogs);
+    expect(records).toMatchObject([
+      {url: 'http://example.com', sessionId: undefined, targetId: undefined},
+      {url: 'http://iframe.com', sessionId: '1', targetId: 'a'},
+      {url: 'http://other-iframe.com', sessionId: '2', targetId: 'b'},
+    ]);
+  });
+
   describe('#findNetworkQuietPeriods', () => {
     function record(data) {
       const url = data.url || 'https://example.com';
diff --git a/lighthouse-core/test/results/sample_v2.json b/lighthouse-core/test/results/sample_v2.json
index 746d3435c..7f803ce0e 100644
--- a/lighthouse-core/test/results/sample_v2.json
+++ b/lighthouse-core/test/results/sample_v2.json
@@ -1150,15 +1150,10 @@
       "description": "Lists the toplevel main thread tasks that executed during page load.",
       "score": null,
       "scoreDisplayMode": "informative",
-      "rawValue": 39,
+      "rawValue": 16,
       "details": {
         "type": "table",
         "headings": [
-          {
-            "key": "type",
-            "itemType": "text",
-            "text": "Task Type"
-          },
           {
             "key": "startTime",
             "itemType": "ms",
@@ -1174,199 +1169,68 @@
         ],
         "items": [
           {
-            "type": "other",
-            "duration": 26.823,
-            "startTime": 604.125
-          },
-          {
-            "type": "other",
-            "duration": 23.175,
-            "startTime": 606.246
-          },
-          {
-            "type": "other",
-            "duration": 5.89,
-            "startTime": 631.458
-          },
-          {
-            "type": "other",
-            "duration": 18.85,
-            "startTime": 638.562
-          },
-          {
-            "type": "parseHTML",
-            "duration": 16.58,
-            "startTime": 638.577
-          },
-          {
-            "type": "parseHTML",
-            "duration": 12.522,
-            "startTime": 638.586
-          },
-          {
-            "type": "other",
-            "duration": 5.897,
-            "startTime": 668.051
-          },
-          {
-            "type": "other",
-            "duration": 5.046,
-            "startTime": 668.09
-          },
-          {
-            "type": "other",
-            "duration": 5.516,
-            "startTime": 2643.396
-          },
-          {
-            "type": "other",
-            "duration": 979.165,
-            "startTime": 2857.942
-          },
-          {
-            "type": "scriptEvaluation",
-            "duration": 5.803,
-            "startTime": 2858.62
-          },
-          {
-            "type": "parseHTML",
-            "duration": 970.698,
-            "startTime": 2865.033
-          },
-          {
-            "type": "parseHTML",
-            "duration": 19.463,
-            "startTime": 2866.527
-          },
-          {
-            "type": "scriptEvaluation",
-            "duration": 19.21,
-            "startTime": 2866.571
-          },
-          {
-            "type": "parseHTML",
-            "duration": 945.898,
-            "startTime": 2889.816
-          },
-          {
-            "type": "scriptEvaluation",
-            "duration": 944.888,
-            "startTime": 2890.118
-          },
-          {
-            "type": "scriptEvaluation",
-            "duration": 9.047,
-            "startTime": 3023.783
-          },
-          {
-            "type": "garbageCollection",
-            "duration": 5.97,
-            "startTime": 3023.842
-          },
-          {
-            "type": "scriptEvaluation",
-            "duration": 706.85,
-            "startTime": 3070.433
-          },
-          {
-            "type": "other",
-            "duration": 123.208,
-            "startTime": 3840.307
-          },
-          {
-            "type": "other",
-            "duration": 123.116,
-            "startTime": 3840.313
-          },
-          {
-            "type": "styleLayout",
-            "duration": 30.142,
-            "startTime": 3840.667
-          },
-          {
-            "type": "other",
-            "duration": 87.039,
-            "startTime": 3870.824
-          },
-          {
-            "type": "styleLayout",
-            "duration": 86.959,
-            "startTime": 3870.844
+            "duration": 5.272,
+            "startTime": 598.738
           },
           {
-            "type": "styleLayout",
-            "duration": 85.493,
-            "startTime": 3871.238
+            "duration": 26.926,
+            "startTime": 604.035
           },
           {
-            "type": "other",
-            "duration": 94.261,
-            "startTime": 3996.555
+            "duration": 6.681,
+            "startTime": 630.994
           },
           {
-            "type": "scriptEvaluation",
-            "duration": 90.663,
-            "startTime": 3997.797
+            "duration": 19.718,
+            "startTime": 637.706
           },
           {
-            "type": "garbageCollection",
-            "duration": 5.709,
-            "startTime": 4019.279
+            "duration": 7.812,
+            "startTime": 666.147
           },
           {
-            "type": "other",
-            "duration": 5.151,
-            "startTime": 4098.541
+            "duration": 10.087,
+            "startTime": 2639.59
           },
           {
-            "type": "other",
-            "duration": 122.537,
-            "startTime": 4808.624
+            "duration": 983.081,
+            "startTime": 2854.095
           },
           {
-            "type": "scriptEvaluation",
-            "duration": 82.741,
-            "startTime": 4810
+            "duration": 123.752,
+            "startTime": 3839.839
           },
           {
-            "type": "parseHTML",
-            "duration": 36.919,
-            "startTime": 4893.397
+            "duration": 5.338,
+            "startTime": 3980.383
           },
           {
-            "type": "parseHTML",
-            "duration": 36.866,
-            "startTime": 4893.44
+            "duration": 6.574,
+            "startTime": 3986.1
           },
           {
-            "type": "scriptEvaluation",
-            "duration": 10.58,
-            "startTime": 4894.119
+            "duration": 96.198,
+            "startTime": 3996.134
           },
           {
-            "type": "scriptEvaluation",
-            "duration": 9.353,
-            "startTime": 4894.151
+            "duration": 9.23,
+            "startTime": 4098.379
           },
           {
-            "type": "parseHTML",
-            "duration": 22.297,
-            "startTime": 4905.447
+            "duration": 5.463,
+            "startTime": 4784.783
           },
           {
-            "type": "other",
-            "duration": 6.849,
-            "startTime": 8603.435
+            "duration": 127.282,
+            "startTime": 4808.602
           },
           {
-            "type": "other",
-            "duration": 7.076,
-            "startTime": 10219.463
+            "duration": 6.885,
+            "startTime": 8603.411
           },
           {
-            "type": "scriptEvaluation",
-            "duration": 6.983,
-            "startTime": 10219.523
+            "duration": 7.258,
+            "startTime": 10219.292
           }
         ]
       }