1<!DOCTYPE html> 2<!-- 3Copyright (c) 2015 The Chromium Authors. All rights reserved. 4Use of this source code is governed by a BSD-style license that can be 5found in the LICENSE file. 6--> 7 8<link rel="import" href="/tracing/model/user_model/load_expectation.html"> 9 10<script> 11'use strict'; 12 13tr.exportTo('tr.importer', function() { 14 // This global instant event marks the start of a navigation. 15 var NAVIGATION_START = 'NavigationTiming navigationStart'; 16 17 // This render-process instant event marks the first contentful paint in a 18 // main frame. 19 var FIRST_CONTENTFUL_PAINT_TITLE = 'firstContentfulPaint'; 20 21 function getAllFrameEvents(modelHelper) { 22 var frameEvents = []; 23 frameEvents.push.apply(frameEvents, 24 modelHelper.browserHelper.getFrameEventsInRange( 25 tr.model.helpers.IMPL_FRAMETIME_TYPE, modelHelper.model.bounds)); 26 27 tr.b.iterItems(modelHelper.rendererHelpers, function(pid, renderer) { 28 frameEvents.push.apply(frameEvents, renderer.getFrameEventsInRange( 29 tr.model.helpers.IMPL_FRAMETIME_TYPE, modelHelper.model.bounds)); 30 }); 31 return frameEvents.sort(tr.importer.compareEvents); 32 } 33 34 // If a thread contains a typical initialization slice, then the first event 35 // on that thread is a startup event. 36 function getStartupEvents(modelHelper) { 37 function isStartupSlice(slice) { 38 return slice.title === 'BrowserMainLoop::CreateThreads'; 39 } 40 var events = modelHelper.browserHelper.getAllAsyncSlicesMatching( 41 isStartupSlice); 42 var deduper = new tr.model.EventSet(); 43 events.forEach(function(event) { 44 var sliceGroup = event.parentContainer.sliceGroup; 45 var slice = sliceGroup && sliceGroup.findFirstSlice(); 46 if (slice) 47 deduper.push(slice); 48 }); 49 return deduper.toArray(); 50 } 51 52 // Match every event in |openingEvents| to the first following event from 53 // |closingEvents| and return an array containing a load interaction record 54 // for each pair. 55 function findLoadExpectationsInternal( 56 modelHelper, subtypeName, openingEvents, closingEvents) { 57 var loads = []; 58 openingEvents.forEach(function(openingEvent) { 59 closingEvents.forEach(function(closingEvent) { 60 // Ignore opening event that already have a closing event. 61 if (openingEvent.closingEvent) 62 return; 63 64 // Ignore closing events that already belong to an opening event. 65 if (closingEvent.openingEvent) 66 return; 67 68 // Ignore closing events before |openingEvent|. 69 if (closingEvent.start <= openingEvent.start) 70 return; 71 72 // Ignore events from different threads. 73 if (openingEvent.parentContainer.parent.pid !== 74 closingEvent.parentContainer.parent.pid) 75 return; 76 77 // This is the first closing event for this opening event, record it. 78 openingEvent.closingEvent = closingEvent; 79 closingEvent.openingEvent = openingEvent; 80 var lir = new tr.model.um.LoadExpectation( 81 modelHelper.model, subtypeName, openingEvent.start, 82 closingEvent.end - openingEvent.start); 83 lir.associatedEvents.push(openingEvent); 84 lir.associatedEvents.push(closingEvent); 85 loads.push(lir); 86 }); 87 }); 88 return loads; 89 } 90 91 function findRenderLoadExpectations(modelHelper) { 92 var events = []; 93 modelHelper.model.iterateAllEvents(function(event) { 94 if ((event.title === NAVIGATION_START) || 95 (event.title === FIRST_CONTENTFUL_PAINT_TITLE)) 96 events.push(event); 97 }); 98 events.sort(tr.importer.compareEvents); 99 100 var loads = []; 101 var startEvent = undefined; 102 events.forEach(function(event) { 103 if (event.title === NAVIGATION_START) { 104 startEvent = event; 105 } else if (event.title === FIRST_CONTENTFUL_PAINT_TITLE) { 106 if (startEvent) { 107 loads.push(new tr.model.um.LoadExpectation( 108 modelHelper.model, tr.model.um.LOAD_SUBTYPE_NAMES.SUCCESSFUL, 109 startEvent.start, event.start - startEvent.start)); 110 startEvent = undefined; 111 } 112 } 113 }); 114 115 // If the trace ended between navigation start and first contentful paint, 116 // then make a LoadExpectation that ends at the end of the trace. 117 if (startEvent) { 118 loads.push(new tr.model.um.LoadExpectation( 119 modelHelper.model, tr.model.um.LOAD_SUBTYPE_NAMES.SUCCESSFUL, 120 startEvent.start, modelHelper.model.bounds.max - startEvent.start)); 121 } 122 123 return loads; 124 } 125 126 // Match up RenderFrameImpl events with frame render events. 127 function findLoadExpectations(modelHelper) { 128 var loads = []; 129 130 var commitLoadEvents = 131 modelHelper.browserHelper.getCommitProvisionalLoadEventsInRange( 132 modelHelper.model.bounds); 133 134 // Attach frame events to every startup events. 135 var startupEvents = getStartupEvents(modelHelper); 136 var frameEvents = getAllFrameEvents(modelHelper); 137 var startupLoads = findLoadExpectationsInternal( 138 modelHelper, tr.model.um.LOAD_SUBTYPE_NAMES.STARTUP, 139 startupEvents, frameEvents); 140 loads.push.apply(loads, startupLoads); 141 142 loads.push.apply(loads, findRenderLoadExpectations(modelHelper)); 143 144 return loads; 145 } 146 147 return { 148 findLoadExpectations: findLoadExpectations 149 }; 150}); 151</script> 152