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 itertools
import starmap
6 from collections
import defaultdict
7 from telemetry
.core
import util
8 from telemetry
.core
import exceptions
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 CleanUpAfterPage(self
, page
, tab
):
29 self
._controller
.CleanUp(tab
)
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 'console.time("style-update");'
45 # Occasionally documents will break the APIs we need.
47 # Invalidate style for the whole document.
48 ' document && (document.documentElement.lang += "z");'
49 # Force a style update (but not layout).
50 ' getComputedStyle(document.documentElement).color;'
52 'console.timeEnd("style-update");'
55 self
._controller
.Stop(tab
, results
)
56 renderer
= self
._controller
.model
.GetRendererThreadFromTabId(tab
.id)
57 markers
= [event
for event
in renderer
.async_slices
58 if event
.name
== 'style-update'
59 and event
.category
== 'blink.console']
60 assert len(markers
) == 1
64 if event
.has_thread_timestamps
:
65 return event
.thread_duration
69 for event
in renderer
.all_slices
:
70 if (event
.name
== 'Document::updateStyle'
71 and event
.start
>= marker
.start
72 and event
.end
<= marker
.end
):
73 access_count
= event
.args
.get('resolverAccessCount')
74 if access_count
is None:
75 # absent in earlier versions
79 if access_count
>= min_access_count
:
80 result
= 1000 * (duration(event
) / access_count
)
81 results
.AddValue(scalar
.ScalarValue(
82 page
, 'update_style', 'ms/1000 elements', result
))
84 class ParserEvent(object):
85 def __init__(self
, summary_event
, tokenize_event
, parse_event
):
86 min_sheet_length
= 1000
88 enormous_token_threshold
= 100
89 large_token_threshold
= 5
91 self
.mode
= summary_event
.args
.get('mode')
92 self
.length
= summary_event
.args
.get('length')
93 self
.tokens
= summary_event
.args
.get('tokenCount')
94 self
.tokenize_duration
= duration(tokenize_event
)
95 self
.parse_duration
= duration(parse_event
)
96 self
.chars_per_token
= 0
98 self
.chars_per_token
= self
.length
/ float(self
.tokens
)
99 if self
.mode
== ua_sheet_mode
or self
.length
< min_sheet_length
:
100 self
.category
= 'ignored'
101 elif self
.chars_per_token
> enormous_token_threshold
:
102 self
.category
= 'enormous_tokens'
103 elif self
.chars_per_token
> large_token_threshold
:
104 self
.category
= 'large_tokens'
106 self
.category
= 'regular'
108 parser_events
= [event
for event
in renderer
.all_slices
109 if event
.name
== 'CSSParserImpl::parseStyleSheet'
110 or event
.name
== 'CSSParserImpl::parseStyleSheet.tokenize'
111 or event
.name
== 'CSSParserImpl::parseStyleSheet.parse']
113 merged_events
= starmap(ParserEvent
, zip(*[iter(parser_events
)]*3))
115 events_by_category
= defaultdict(list)
116 for event
in merged_events
:
117 if event
.category
!= 'ignored':
118 events_by_category
[event
.category
].append(event
)
120 for category
, events
in events_by_category
.items():
121 parse_duration
= sum(event
.parse_duration
for event
in events
)
122 tokenize_duration
= sum(event
.tokenize_duration
for event
in events
)
123 tokens
= sum(event
.tokens
for event
in events
)
124 length
= sum(event
.length
for event
in events
)
127 scalar
.ScalarValue(page
, ('parse_css_%s' % category
),
128 'tokens/s', 1000 / (parse_duration
/ tokens
)))
131 scalar
.ScalarValue(page
, ('tokenize_css_%s' % category
),
132 'char/s', 1000 / (tokenize_duration
/ length
)))