blob: ee81aa4adb097aada14ab54b3586871b396a44b9 [file] [log] [blame]
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import HTMLParser
import json
import os
import string
from telemetry.web_components import web_components_project
from tvcm import generate
class WebComponent(object):
"""An HTML-based viewer of data, built out of telemetry components.
A WebComponent is used to visualize complex data that is produced from a
telemtry benchmark. A WebComponent is a Javascript class that can be
data-bound, plus python-side bindings that let us write HTML files that
instantiate that class and bind it to specific data.
The primary job of the python side of a WebComponent is to implement the
WriteDataToFileAsJson. The data written here is passed to the
data_binding_property of the JS-side class.
The primary job of the javascript side of a WebComponent is visualization: it
takes the data from python and renders a UI for displaying that data in some
manner.
"""
def __init__(self, tvcm_module_name, js_class_name, data_binding_property):
self._tvcm_module_name = tvcm_module_name
self._js_class_name = js_class_name
self._data_binding_property = data_binding_property
self._data_to_view = None
@property
def data_to_view(self):
return self._data_to_view
@data_to_view.setter
def data_to_view(self, data_to_view):
self._data_to_view = data_to_view
def WriteDataToFileAsJson(self, f):
raise NotImplementedError()
def GetDependentModuleNames(self):
return [self._tvcm_module_name]
def WriteWebComponentToFile(self, f):
project = web_components_project.WebComponentsProject()
load_sequence = project.CalcLoadSequenceForModuleNames(
self.GetDependentModuleNames())
with open(os.path.join(os.path.dirname(__file__),
'web_component_bootstrap.js')) as bfile:
bootstrap_js_template = string.Template(bfile.read())
bootstrap_js = bootstrap_js_template.substitute(
js_class_name=self._js_class_name,
data_binding_property=self._data_binding_property)
bootstrap_script = generate.ExtraScript(text_content=bootstrap_js)
class WebComponentDataScript(generate.ExtraScript):
def __init__(self, results_component):
super(WebComponentDataScript, self).__init__()
self._results_component = results_component
def WriteToFile(self, output_file):
output_file.write('<script id="telemetry-web-component-data" ' +
'type="application/json">\n')
self._results_component.WriteDataToFileAsJson(output_file)
output_file.write('</script>\n')
generate.GenerateStandaloneHTMLToFile(
f, load_sequence,
title='Telemetry results',
extra_scripts=[bootstrap_script, WebComponentDataScript(self)])
@staticmethod
def ReadDataObjectFromWebComponentFile(f):
"""Reads the data inside a file written with WriteWebComponentToFile
Returns None if the data wasn't found, the JSON.parse'd object on success.
Raises exception if the HTML file was corrupt.
"""
class MyHTMLParser(HTMLParser.HTMLParser):
def __init__(self):
HTMLParser.HTMLParser.__init__(self)
self._got_data_tag = False
self._in_data_tag = False
self._data_records = []
def handle_starttag(self, tag, attrs):
if tag != 'script':
return
id_attr = dict(attrs).get('id', None)
if id_attr == 'telemetry-web-component-data':
assert not self._got_data_tag
self._got_data_tag = True
self._in_data_tag = True
def handle_endtag(self, tag):
self._in_data_tag = False
def handle_data(self, data):
if self._in_data_tag:
self._data_records.append(data)
@property
def data(self):
if not self._got_data_tag:
raise Exception('Missing <script> with #telemetry-web-component-data')
if self._in_data_tag:
raise Exception('Missing </script> on #telemetry-web-component-data')
return json.loads(''.join(self._data_records))
parser = MyHTMLParser()
for line in f:
parser.feed(line)
return parser.data