1 # Copyright 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 from collections
import defaultdict
6 from itertools
import starmap
7 from telemetry
.core
import exceptions
8 from telemetry
.core
import util
9 from telemetry
.page
import action_runner
10 from telemetry
.page
import page_test
11 from telemetry
.value
import scalar
13 from measurements
import timeline_controller
16 class BlinkStyle(page_test
.PageTest
):
18 super(BlinkStyle
, self
).__init
__()
19 self
._controller
= None
21 def WillNavigateToPage(self
, page
, tab
):
22 self
._controller
= timeline_controller
.TimelineController()
23 self
._controller
.trace_categories
= 'blink_style,blink.console'
24 self
._controller
.SetUp(page
, tab
)
25 self
._controller
.Start(tab
)
27 def DidRunPage(self
, platform
):
29 self
._controller
.CleanUp(platform
)
31 def ValidateAndMeasurePage(self
, page
, tab
, results
):
32 runner
= action_runner
.ActionRunner(tab
)
33 with runner
.CreateInteraction('wait-for-quiescence'):
34 tab
.ExecuteJavaScript('console.time("");')
36 util
.WaitFor(tab
.HasReachedQuiescence
, 15)
37 except exceptions
.TimeoutException
:
38 # Some sites never reach quiesence. As this benchmark normalizes/
39 # categories results, it shouldn't be necessary to reach the same
43 tab
.ExecuteJavaScript('''
44 for (var i = 0; i < 11; i++) {
45 var cold = i % 2 == 0;
46 var name = "update_style";
47 if (cold) name += "_cold";
49 // Occasionally documents will break the APIs we need
51 // On cold runs, force a new StyleResolver
53 var style = document.createElement("style");
54 document.head.appendChild(style);
57 // Invalidate style for the whole document
58 document.documentElement.lang += "z";
59 // Force a style update (but not layout)
60 getComputedStyle(document.documentElement).color;
62 console.timeEnd(name);
65 self
._controller
.Stop(tab
, results
)
66 renderer
= self
._controller
.model
.GetRendererThreadFromTabId(tab
.id)
67 markers
= [event
for event
in renderer
.async_slices
68 if event
.name
.startswith('update_style')
69 and event
.category
== 'blink.console']
72 assert len(markers
) == 10
75 if event
.has_thread_timestamps
:
76 return event
.thread_duration
80 for marker
in markers
:
81 for event
in renderer
.all_slices
:
82 if (event
.name
== 'Document::updateStyle'
83 and event
.start
>= marker
.start
84 and event
.end
<= marker
.end
):
85 access_count
= event
.args
.get('resolverAccessCount')
86 if access_count
is None:
87 # absent in earlier versions
91 if access_count
>= min_access_count
:
92 result
= 1000 * (duration(event
) / access_count
)
93 results
.AddValue(scalar
.ScalarValue(
94 page
, marker
.name
, 'ms/1000 elements', result
))
96 class ParserEvent(object):
97 def __init__(self
, summary_event
, tokenize_event
, parse_event
):
98 min_sheet_length
= 1000
100 enormous_token_threshold
= 100
101 large_token_threshold
= 5
103 self
.mode
= summary_event
.args
.get('mode')
104 self
.length
= summary_event
.args
.get('length')
105 self
.tokens
= summary_event
.args
.get('tokenCount')
106 self
.tokenize_duration
= duration(tokenize_event
)
107 self
.parse_duration
= duration(parse_event
)
108 self
.chars_per_token
= 0
110 self
.chars_per_token
= self
.length
/ float(self
.tokens
)
111 if self
.mode
== ua_sheet_mode
or self
.length
< min_sheet_length
:
112 self
.category
= 'ignored'
113 elif self
.chars_per_token
> enormous_token_threshold
:
114 self
.category
= 'enormous_tokens'
115 elif self
.chars_per_token
> large_token_threshold
:
116 self
.category
= 'large_tokens'
118 self
.category
= 'regular'
120 parser_events
= [event
for event
in renderer
.all_slices
121 if event
.name
== 'CSSParserImpl::parseStyleSheet'
122 or event
.name
== 'CSSParserImpl::parseStyleSheet.tokenize'
123 or event
.name
== 'CSSParserImpl::parseStyleSheet.parse']
125 merged_events
= starmap(ParserEvent
, zip(*[iter(parser_events
)]*3))
127 events_by_category
= defaultdict(list)
128 for event
in merged_events
:
129 if event
.category
!= 'ignored':
130 events_by_category
[event
.category
].append(event
)
132 for category
, events
in events_by_category
.items():
133 parse_duration
= sum(event
.parse_duration
for event
in events
)
134 tokenize_duration
= sum(event
.tokenize_duration
for event
in events
)
135 tokens
= sum(event
.tokens
for event
in events
)
136 length
= sum(event
.length
for event
in events
)
139 scalar
.ScalarValue(page
, ('parse_css_%s' % category
),
140 'tokens/s', 1000 / (parse_duration
/ tokens
)))
143 scalar
.ScalarValue(page
, ('tokenize_css_%s' % category
),
144 'char/s', 1000 / (tokenize_duration
/ length
)))