@@ -13,10 +13,11 @@ import {
1313import { WebDriverExtension , PerfLogFeatures } from '../web_driver_extension' ;
1414import { WebDriverAdapter } from '../web_driver_adapter' ;
1515import { Promise } from 'angular2/src/core/facade/async' ;
16+ import { Options } from '../common_options' ;
1617
1718/**
1819 * Set the following 'traceCategories' to collect metrics in Chrome:
19- * 'v8,blink.console,disabled-by-default-devtools.timeline'
20+ * 'v8,blink.console,disabled-by-default-devtools.timeline,devtools.timeline '
2021 *
2122 * In order to collect the frame rate related metrics, add 'benchmark'
2223 * to the list above.
@@ -25,7 +26,27 @@ export class ChromeDriverExtension extends WebDriverExtension {
2526 // TODO(tbosch): use static values when our transpiler supports them
2627 static get BINDINGS ( ) : Binding [ ] { return _BINDINGS ; }
2728
28- constructor ( private _driver : WebDriverAdapter ) { super ( ) ; }
29+ private _majorChromeVersion : number ;
30+
31+ constructor ( private _driver : WebDriverAdapter , userAgent : string ) {
32+ super ( ) ;
33+ this . _majorChromeVersion = this . _parseChromeVersion ( userAgent ) ;
34+ }
35+
36+ private _parseChromeVersion ( userAgent : string ) : number {
37+ if ( isBlank ( userAgent ) ) {
38+ return - 1 ;
39+ }
40+ var v = StringWrapper . split ( userAgent , / C h r o m ( e | i u m ) \/ / g) [ 2 ] ;
41+ if ( isBlank ( v ) ) {
42+ return - 1 ;
43+ }
44+ v = StringWrapper . split ( v , / \. / g) [ 0 ] ;
45+ if ( isBlank ( v ) ) {
46+ return - 1 ;
47+ }
48+ return NumberWrapper . parseInt ( v , 10 ) ;
49+ }
2950
3051 gc ( ) { return this . _driver . executeScript ( 'window.gc()' ) ; }
3152
@@ -63,81 +84,138 @@ export class ChromeDriverExtension extends WebDriverExtension {
6384 } ) ;
6485 }
6586
66- _convertPerfRecordsToEvents ( chromeEvents : Array < StringMap < string , any > > ,
67- normalizedEvents : Array < StringMap < string , any > > = null ) {
87+ private _convertPerfRecordsToEvents ( chromeEvents : Array < StringMap < string , any > > ,
88+ normalizedEvents : Array < StringMap < string , any > > = null ) {
6889 if ( isBlank ( normalizedEvents ) ) {
6990 normalizedEvents = [ ] ;
7091 }
7192 var majorGCPids = { } ;
7293 chromeEvents . forEach ( ( event ) => {
73- var cat = event [ 'cat' ] ;
94+ var categories = this . _parseCategories ( event [ 'cat' ] ) ;
7495 var name = event [ 'name' ] ;
75- var args = event [ 'args' ] ;
76- var pid = event [ 'pid' ] ;
77- var ph = event [ 'ph' ] ;
78- if ( StringWrapper . equals ( cat , 'disabled-by-default-devtools.timeline' ) ) {
79- if ( StringWrapper . equals ( name , 'FunctionCall' ) &&
80- ( isBlank ( args ) || isBlank ( args [ 'data' ] ) ||
81- ! StringWrapper . equals ( args [ 'data' ] [ 'scriptName' ] , 'InjectedScript' ) ) ) {
82- normalizedEvents . push ( normalizeEvent ( event , { 'name' : 'script' } ) ) ;
83-
84- } else if ( StringWrapper . equals ( name , 'RecalculateStyles' ) ||
85- StringWrapper . equals ( name , 'Layout' ) ||
86- StringWrapper . equals ( name , 'UpdateLayerTree' ) ||
87- StringWrapper . equals ( name , 'Paint' ) || StringWrapper . equals ( name , 'Rasterize' ) ||
88- StringWrapper . equals ( name , 'CompositeLayers' ) ) {
89- normalizedEvents . push ( normalizeEvent ( event , { 'name' : 'render' } ) ) ;
90-
91- } else if ( StringWrapper . equals ( name , 'GCEvent' ) ) {
92- var normArgs = {
93- 'usedHeapSize' : isPresent ( args [ 'usedHeapSizeAfter' ] ) ? args [ 'usedHeapSizeAfter' ] :
94- args [ 'usedHeapSizeBefore' ]
95- } ;
96- if ( StringWrapper . equals ( event [ 'ph' ] , 'E' ) ) {
97- normArgs [ 'majorGc' ] = isPresent ( majorGCPids [ pid ] ) && majorGCPids [ pid ] ;
98- }
99- majorGCPids [ pid ] = false ;
100- normalizedEvents . push ( normalizeEvent ( event , { 'name' : 'gc' , 'args' : normArgs } ) ) ;
101- }
102-
103- } else if ( StringWrapper . equals ( cat , 'blink.console' ) ) {
96+ if ( this . _isEvent ( categories , name , [ 'blink.console' ] ) ) {
10497 normalizedEvents . push ( normalizeEvent ( event , { 'name' : name } ) ) ;
105-
106- } else if ( StringWrapper . equals ( cat , 'v8' ) ) {
107- if ( StringWrapper . equals ( name , 'majorGC' ) ) {
108- if ( StringWrapper . equals ( ph , 'B' ) ) {
109- majorGCPids [ pid ] = true ;
110- }
111- }
112-
113- } else if ( StringWrapper . equals ( cat , 'benchmark' ) ) {
98+ } else if ( this . _isEvent ( categories , name , [ 'benchmark' ] ,
99+ 'BenchmarkInstrumentation::ImplThreadRenderingStats' ) ) {
114100 // TODO(goderbauer): Instead of BenchmarkInstrumentation::ImplThreadRenderingStats the
115101 // following events should be used (if available) for more accurate measurments:
116102 // 1st choice: vsync_before - ground truth on Android
117103 // 2nd choice: BenchmarkInstrumentation::DisplayRenderingStats - available on systems with
118104 // new surfaces framework (not broadly enabled yet)
119105 // 3rd choice: BenchmarkInstrumentation::ImplThreadRenderingStats - fallback event that is
120106 // allways available if something is rendered
121- if ( StringWrapper . equals ( name , 'BenchmarkInstrumentation::ImplThreadRenderingStats' ) ) {
122- var frameCount = event [ 'args' ] [ 'data' ] [ 'frame_count' ] ;
123- if ( frameCount > 1 ) {
124- throw new BaseException ( 'multi-frame render stats not supported' ) ;
125- }
126- if ( frameCount == 1 ) {
127- normalizedEvents . push ( normalizeEvent ( event , { 'name' : 'frame' } ) ) ;
128- }
107+ var frameCount = event [ 'args' ] [ 'data' ] [ 'frame_count' ] ;
108+ if ( frameCount > 1 ) {
109+ throw new BaseException ( 'multi-frame render stats not supported' ) ;
129110 }
111+ if ( frameCount == 1 ) {
112+ normalizedEvents . push ( normalizeEvent ( event , { 'name' : 'frame' } ) ) ;
113+ }
114+ } else if ( this . _isEvent ( categories , name , [ 'disabled-by-default-devtools.timeline' ] ,
115+ 'Rasterize' ) ||
116+ this . _isEvent ( categories , name , [ 'disabled-by-default-devtools.timeline' ] ,
117+ 'CompositeLayers' ) ) {
118+ normalizedEvents . push ( normalizeEvent ( event , { 'name' : 'render' } ) ) ;
119+ } else if ( this . _majorChromeVersion < 45 ) {
120+ var normalizedEvent = this . _processAsPreChrome45Event ( event , categories , majorGCPids ) ;
121+ if ( normalizedEvent != null ) normalizedEvents . push ( normalizedEvent ) ;
122+ } else {
123+ var normalizedEvent = this . _processAsPostChrome44Event ( event , categories ) ;
124+ if ( normalizedEvent != null ) normalizedEvents . push ( normalizedEvent ) ;
130125 }
131126 } ) ;
132127 return normalizedEvents ;
133128 }
134129
130+ private _processAsPreChrome45Event ( event , categories , majorGCPids ) {
131+ var name = event [ 'name' ] ;
132+ var args = event [ 'args' ] ;
133+ var pid = event [ 'pid' ] ;
134+ var ph = event [ 'ph' ] ;
135+ if ( this . _isEvent ( categories , name , [ 'disabled-by-default-devtools.timeline' ] ,
136+ 'FunctionCall' ) &&
137+ ( isBlank ( args ) || isBlank ( args [ 'data' ] ) ||
138+ ! StringWrapper . equals ( args [ 'data' ] [ 'scriptName' ] , 'InjectedScript' ) ) ) {
139+ return normalizeEvent ( event , { 'name' : 'script' } ) ;
140+ } else if ( this . _isEvent ( categories , name , [ 'disabled-by-default-devtools.timeline' ] ,
141+ 'RecalculateStyles' ) ||
142+ this . _isEvent ( categories , name , [ 'disabled-by-default-devtools.timeline' ] ,
143+ 'Layout' ) ||
144+ this . _isEvent ( categories , name , [ 'disabled-by-default-devtools.timeline' ] ,
145+ 'UpdateLayerTree' ) ||
146+ this . _isEvent ( categories , name , [ 'disabled-by-default-devtools.timeline' ] ,
147+ 'Paint' ) ) {
148+ return normalizeEvent ( event , { 'name' : 'render' } ) ;
149+ } else if ( this . _isEvent ( categories , name , [ 'disabled-by-default-devtools.timeline' ] ,
150+ 'GCEvent' ) ) {
151+ var normArgs = {
152+ 'usedHeapSize' : isPresent ( args [ 'usedHeapSizeAfter' ] ) ? args [ 'usedHeapSizeAfter' ] :
153+ args [ 'usedHeapSizeBefore' ]
154+ } ;
155+ if ( StringWrapper . equals ( ph , 'E' ) ) {
156+ normArgs [ 'majorGc' ] = isPresent ( majorGCPids [ pid ] ) && majorGCPids [ pid ] ;
157+ }
158+ majorGCPids [ pid ] = false ;
159+ return normalizeEvent ( event , { 'name' : 'gc' , 'args' : normArgs } ) ;
160+ } else if ( this . _isEvent ( categories , name , [ 'v8' ] , 'majorGC' ) &&
161+ StringWrapper . equals ( ph , 'B' ) ) {
162+ majorGCPids [ pid ] = true ;
163+ }
164+ return null ; // nothing useful in this event
165+ }
166+
167+ private _processAsPostChrome44Event ( event , categories ) {
168+ var name = event [ 'name' ] ;
169+ var args = event [ 'args' ] ;
170+ if ( this . _isEvent ( categories , name , [ 'devtools.timeline' , 'v8' ] , 'MajorGC' ) ) {
171+ var normArgs = {
172+ 'majorGc' : true ,
173+ 'usedHeapSize' : isPresent ( args [ 'usedHeapSizeAfter' ] ) ? args [ 'usedHeapSizeAfter' ] :
174+ args [ 'usedHeapSizeBefore' ]
175+ } ;
176+ return normalizeEvent ( event , { 'name' : 'gc' , 'args' : normArgs } ) ;
177+ } else if ( this . _isEvent ( categories , name , [ 'devtools.timeline' , 'v8' ] , 'MinorGC' ) ) {
178+ var normArgs = {
179+ 'majorGc' : false ,
180+ 'usedHeapSize' : isPresent ( args [ 'usedHeapSizeAfter' ] ) ? args [ 'usedHeapSizeAfter' ] :
181+ args [ 'usedHeapSizeBefore' ]
182+ } ;
183+ return normalizeEvent ( event , { 'name' : 'gc' , 'args' : normArgs } ) ;
184+ } else if ( this . _isEvent ( categories , name , [ 'devtools.timeline' , 'v8' ] , 'FunctionCall' ) &&
185+ ( isBlank ( args ) || isBlank ( args [ 'data' ] ) ||
186+ ! StringWrapper . equals ( args [ 'data' ] [ 'scriptName' ] , 'InjectedScript' ) ) ) {
187+ return normalizeEvent ( event , { 'name' : 'script' } ) ;
188+ } else if ( this . _isEvent ( categories , name , [ 'devtools.timeline' , 'blink' ] ,
189+ 'UpdateLayoutTree' ) ) {
190+ return normalizeEvent ( event , { 'name' : 'render' } ) ;
191+ } else if ( this . _isEvent ( categories , name , [ 'devtools.timeline' ] , 'UpdateLayerTree' ) ||
192+ this . _isEvent ( categories , name , [ 'devtools.timeline' ] , 'Layout' ) ||
193+ this . _isEvent ( categories , name , [ 'devtools.timeline' ] , 'Paint' ) ) {
194+ return normalizeEvent ( event , { 'name' : 'render' } ) ;
195+ }
196+ return null ; // nothing useful in this event
197+ }
198+
199+ private _parseCategories ( categories : string ) : string [ ] {
200+ return StringWrapper . split ( categories , / , / g) ;
201+ }
202+
203+ private _isEvent ( eventCategories : string [ ] , eventName : string , expectedCategories : string [ ] ,
204+ expectedName : string = null ) : boolean {
205+ var hasCategories = ListWrapper . reduce ( expectedCategories , ( value , cat ) => {
206+ return value && ListWrapper . contains ( eventCategories , cat ) ;
207+ } , true ) ;
208+ return isBlank ( expectedName ) ? hasCategories :
209+ hasCategories && StringWrapper . equals ( eventName , expectedName ) ;
210+ }
211+
135212 perfLogFeatures ( ) : PerfLogFeatures {
136213 return new PerfLogFeatures ( { render : true , gc : true , frameCapture : true } ) ;
137214 }
138215
139216 supports ( capabilities : StringMap < string , any > ) : boolean {
140- return StringWrapper . equals ( capabilities [ 'browserName' ] . toLowerCase ( ) , 'chrome' ) ;
217+ return this . _majorChromeVersion != - 1 &&
218+ StringWrapper . equals ( capabilities [ 'browserName' ] . toLowerCase ( ) , 'chrome' ) ;
141219 }
142220}
143221
@@ -164,5 +242,6 @@ function normalizeEvent(chromeEvent: StringMap<string, any>, data: StringMap<str
164242
165243var _BINDINGS = [
166244 bind ( ChromeDriverExtension )
167- . toFactory ( ( driver ) => new ChromeDriverExtension ( driver ) , [ WebDriverAdapter ] )
245+ . toFactory ( ( driver , userAgent ) => new ChromeDriverExtension ( driver , userAgent ) ,
246+ [ WebDriverAdapter , Options . USER_AGENT ] )
168247] ;
0 commit comments