1# Copyright (c) 2014 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.
4
5import os
6import sys
7import subprocess
8import tempfile
9import StringIO
10
11from tvcm import html_generation_controller
12
13
14html_warning_message = """
15
16
17<!--
18WARNING: This file is auto generated.
19
20         Do not edit directly.
21-->
22"""
23
24js_warning_message = """
25// Copyright (c) 2014 The Chromium Authors. All rights reserved.
26// Use of this source code is governed by a BSD-style license that can be
27// found in the LICENSE file.
28
29/* WARNING: This file is auto generated.
30 *
31 * Do not edit directly.
32 */
33"""
34
35css_warning_message = """
36/* Copyright (c) 2014 The Chromium Authors. All rights reserved.
37 * Use of this source code is governed by a BSD-style license that can be
38 * found in the LICENSE file. */
39
40/* WARNING: This file is auto-generated.
41 *
42 * Do not edit directly.
43 */
44"""
45
46
47def _AssertIsUTF8(f):
48  if isinstance(f, StringIO.StringIO):
49    return
50  assert f.encoding == 'utf-8'
51
52
53def _MinifyJS(input_js):
54  tvcm_path = os.path.abspath(os.path.join(
55      os.path.dirname(__file__), '..'))
56  rjsmin_path = os.path.abspath(
57      os.path.join(tvcm_path, 'third_party', 'rjsmin', 'rjsmin.py'))
58
59  with tempfile.NamedTemporaryFile() as _:
60    args = [
61        'python',
62        rjsmin_path
63    ]
64    p = subprocess.Popen(args,
65                         stdin=subprocess.PIPE,
66                         stdout=subprocess.PIPE,
67                         stderr=subprocess.PIPE)
68    res = p.communicate(input=input_js)
69    errorcode = p.wait()
70    if errorcode != 0:
71      sys.stderr.write('rJSmin exited with error code %d' % errorcode)
72      sys.stderr.write(res[1])
73      raise Exception('Failed to minify, omgah')
74    return res[0]
75
76
77def GenerateJS(load_sequence,
78               use_include_tags_for_scripts=False,
79               dir_for_include_tag_root=None,
80               minify=False,
81               report_sizes=False):
82  f = StringIO.StringIO()
83  GenerateJSToFile(f,
84                   load_sequence,
85                   use_include_tags_for_scripts,
86                   dir_for_include_tag_root,
87                   minify=minify,
88                   report_sizes=report_sizes)
89
90  return f.getvalue()
91
92
93def pt_parts(self):
94  sl = ['unicode and 8-bit string parts of above page template']
95  for x in self.buflist:
96    if type(x) == type(''):
97      maxcode = 0
98      for c in x:
99          maxcode = max(ord(c), maxcode)
100    # show only unicode objects and non-ascii strings
101    if type(x) == type('') and maxcode > 127:
102      t = '****NonAsciiStr: '
103    elif type(x) == type(u''):
104      t = '*****UnicodeStr: '
105    else:
106      t = None
107    if t:
108      sl.append(t + repr(x))
109  s = '\n'.join(sl)
110  return s
111
112
113def GenerateJSToFile(f,
114                     load_sequence,
115                     use_include_tags_for_scripts=False,
116                     dir_for_include_tag_root=None,
117                     minify=False,
118                     report_sizes=False):
119  _AssertIsUTF8(f)
120  if use_include_tags_for_scripts and dir_for_include_tag_root is None:
121    raise Exception('Must provide dir_for_include_tag_root')
122
123  f.write(js_warning_message)
124  f.write('\n')
125
126  loader = load_sequence[0].loader
127
128  polymer_script = loader.LoadRawScript('components/polymer/polymer.min.js')
129  f.write(polymer_script.contents)
130
131  if not minify:
132    flatten_to_file = f
133  else:
134    flatten_to_file = StringIO.StringIO()
135
136  for module in load_sequence:
137    module.AppendJSContentsToFile(flatten_to_file,
138                                  use_include_tags_for_scripts,
139                                  dir_for_include_tag_root)
140  if minify:
141    js = flatten_to_file.getvalue()
142    minified_js = _MinifyJS(js)
143    f.write(minified_js)
144    f.write('\n')
145
146  if report_sizes:
147    for module in load_sequence:
148      s = StringIO.StringIO()
149      module.AppendJSContentsToFile(s,
150                                    use_include_tags_for_scripts,
151                                    dir_for_include_tag_root)
152
153      # Add minified size info.
154      js = s.getvalue()
155      min_js_size = str(len(_MinifyJS(js)))
156
157      # Print names for this module. Some domain-specific simplifciations
158      # are included to make pivoting more obvious.
159      parts = module.name.split('.')
160      if parts[:2] == ['base', 'ui']:
161        parts = ['base_ui'] + parts[2:]
162      if parts[:2] == ['tracing', 'importer']:
163        parts = ['importer'] + parts[2:]
164      tln = parts[0]
165      sln = '.'.join(parts[:2])
166
167      # Ouptut
168      print '%i\t%s\t%s\t%s\t%s' % (len(js), min_js_size, module.name, tln, sln)
169      sys.stdout.flush()
170
171
172class ExtraScript(object):
173  def __init__(self, script_id=None, text_content=None, content_type=None):
174    if script_id is not None:
175      assert script_id[0] != '#'
176    self.script_id = script_id
177    self.text_content = text_content
178    self.content_type = content_type
179
180  def WriteToFile(self, output_file):
181    _AssertIsUTF8(output_file)
182    attrs = []
183    if self.script_id:
184      attrs.append('id="%s"' % self.script_id)
185    if self.content_type:
186      attrs.append('content-type="%s"' % self.content_type)
187
188    if len(attrs) > 0:
189      output_file.write('<script %s>\n' % ' '.join(attrs))
190    else:
191      output_file.write('<script>\n')
192    if self.text_content:
193      output_file.write(self.text_content)
194    output_file.write('</script>\n')
195
196
197def _MinifyCSS(css_text):
198  tvcm_path = os.path.abspath(os.path.join(
199      os.path.dirname(__file__), '..'))
200  rcssmin_path = os.path.abspath(
201      os.path.join(tvcm_path, 'third_party', 'rcssmin', 'rcssmin.py'))
202
203  with tempfile.NamedTemporaryFile() as _:
204    rcssmin_args = ['python', rcssmin_path]
205    p = subprocess.Popen(rcssmin_args,
206                         stdin=subprocess.PIPE,
207                         stdout=subprocess.PIPE,
208                         stderr=subprocess.PIPE)
209    res = p.communicate(input=css_text)
210    errorcode = p.wait()
211    if errorcode != 0:
212      sys.stderr.write('rCSSmin exited with error code %d' % errorcode)
213      sys.stderr.write(res[1])
214      raise Exception('Failed to generate css for %s.' % css_text)
215    return res[0]
216
217
218def GenerateStandaloneHTMLAsString(*args, **kwargs):
219  f = StringIO.StringIO()
220  GenerateStandaloneHTMLToFile(f, *args, **kwargs)
221  return f.getvalue()
222
223
224def GenerateStandaloneHTMLToFile(output_file,
225                                 load_sequence,
226                                 title=None,
227                                 flattened_js_url=None,
228                                 extra_scripts=None,
229                                 minify=False,
230                                 report_sizes=False,
231                                 output_html_head_and_body=True):
232  _AssertIsUTF8(output_file)
233  extra_scripts = extra_scripts or []
234
235  if output_html_head_and_body:
236    output_file.write("""<!DOCTYPE HTML>
237<html>
238  <head i18n-values="dir:textdirection;">
239  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
240    """)
241    if title:
242      output_file.write("""  <title>%s</title>
243  """ % title)
244  else:
245    assert title is None
246
247  loader = load_sequence[0].loader
248
249  written_style_sheets = set()
250
251  class HTMLGenerationController(
252      html_generation_controller.HTMLGenerationController):
253
254    def __init__(self, module):
255      self.module = module
256
257    def GetHTMLForStylesheetHRef(self, href):
258      resource = self.module.HRefToResource(
259          href, '<link rel="stylesheet" href="%s">' % href)
260      style_sheet = loader.LoadStyleSheet(resource.name)
261
262      if style_sheet in written_style_sheets:
263        return None
264      written_style_sheets.add(style_sheet)
265
266      text = style_sheet.contents_with_inlined_images
267      if minify:
268        text = _MinifyCSS(text)
269      return '<style>\n%s\n</style>' % text
270
271  for module in load_sequence:
272    ctl = HTMLGenerationController(module)
273    module.AppendHTMLContentsToFile(output_file, ctl, minify=minify)
274
275  if flattened_js_url:
276    output_file.write('<script src="%s"></script>\n' % flattened_js_url)
277  else:
278    output_file.write('<script>\n')
279    x = GenerateJS(load_sequence, minify=minify, report_sizes=report_sizes)
280    output_file.write(x)
281    output_file.write('</script>\n')
282
283  for extra_script in extra_scripts:
284    extra_script.WriteToFile(output_file)
285
286  if output_html_head_and_body:
287    output_file.write("""</head>
288  <body>
289  </body>
290</html>
291""")
292