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 py_vulcanize 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 2015 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 2015 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  py_vulcanize_path = os.path.abspath(os.path.join(
55      os.path.dirname(__file__), '..'))
56  rjsmin_path = os.path.abspath(
57      os.path.join(py_vulcanize_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 GenerateJSToFile(f,
94                     load_sequence,
95                     use_include_tags_for_scripts=False,
96                     dir_for_include_tag_root=None,
97                     minify=False,
98                     report_sizes=False):
99  _AssertIsUTF8(f)
100  if use_include_tags_for_scripts and dir_for_include_tag_root is None:
101    raise Exception('Must provide dir_for_include_tag_root')
102
103  f.write(js_warning_message)
104  f.write('\n')
105
106  loader = load_sequence[0].loader
107
108  polymer_script = loader.LoadRawScript('components/polymer/polymer.min.js')
109  f.write(polymer_script.contents)
110
111  if not minify:
112    flatten_to_file = f
113  else:
114    flatten_to_file = StringIO.StringIO()
115
116  for module in load_sequence:
117    module.AppendJSContentsToFile(flatten_to_file,
118                                  use_include_tags_for_scripts,
119                                  dir_for_include_tag_root)
120  if minify:
121    js = flatten_to_file.getvalue()
122    minified_js = _MinifyJS(js)
123    f.write(minified_js)
124    f.write('\n')
125
126  if report_sizes:
127    for module in load_sequence:
128      s = StringIO.StringIO()
129      module.AppendJSContentsToFile(s,
130                                    use_include_tags_for_scripts,
131                                    dir_for_include_tag_root)
132
133      # Add minified size info.
134      js = s.getvalue()
135      min_js_size = str(len(_MinifyJS(js)))
136
137      # Print names for this module. Some domain-specific simplifications
138      # are included to make pivoting more obvious.
139      parts = module.name.split('.')
140      if parts[:2] == ['base', 'ui']:
141        parts = ['base_ui'] + parts[2:]
142      if parts[:2] == ['tracing', 'importer']:
143        parts = ['importer'] + parts[2:]
144      tln = parts[0]
145      sln = '.'.join(parts[:2])
146
147      # Output
148      print '%i\t%s\t%s\t%s\t%s' % (len(js), min_js_size, module.name, tln, sln)
149      sys.stdout.flush()
150
151
152class ExtraScript(object):
153
154  def __init__(self, script_id=None, text_content=None, content_type=None):
155    if script_id is not None:
156      assert script_id[0] != '#'
157    self.script_id = script_id
158    self.text_content = text_content
159    self.content_type = content_type
160
161  def WriteToFile(self, output_file):
162    _AssertIsUTF8(output_file)
163    attrs = []
164    if self.script_id:
165      attrs.append('id="%s"' % self.script_id)
166    if self.content_type:
167      attrs.append('content-type="%s"' % self.content_type)
168
169    if len(attrs) > 0:
170      output_file.write('<script %s>\n' % ' '.join(attrs))
171    else:
172      output_file.write('<script>\n')
173    if self.text_content:
174      output_file.write(self.text_content)
175    output_file.write('</script>\n')
176
177
178def _MinifyCSS(css_text):
179  py_vulcanize_path = os.path.abspath(os.path.join(
180      os.path.dirname(__file__), '..'))
181  rcssmin_path = os.path.abspath(
182      os.path.join(py_vulcanize_path, 'third_party', 'rcssmin', 'rcssmin.py'))
183
184  with tempfile.NamedTemporaryFile() as _:
185    rcssmin_args = ['python', rcssmin_path]
186    p = subprocess.Popen(rcssmin_args,
187                         stdin=subprocess.PIPE,
188                         stdout=subprocess.PIPE,
189                         stderr=subprocess.PIPE)
190    res = p.communicate(input=css_text)
191    errorcode = p.wait()
192    if errorcode != 0:
193      sys.stderr.write('rCSSmin exited with error code %d' % errorcode)
194      sys.stderr.write(res[1])
195      raise Exception('Failed to generate css for %s.' % css_text)
196    return res[0]
197
198
199def GenerateStandaloneHTMLAsString(*args, **kwargs):
200  f = StringIO.StringIO()
201  GenerateStandaloneHTMLToFile(f, *args, **kwargs)
202  return f.getvalue()
203
204
205def GenerateStandaloneHTMLToFile(output_file,
206                                 load_sequence,
207                                 title=None,
208                                 flattened_js_url=None,
209                                 extra_scripts=None,
210                                 minify=False,
211                                 report_sizes=False,
212                                 output_html_head_and_body=True):
213  """Writes a HTML file with the content of all modules in a load sequence.
214
215  The load_sequence is a list of (HTML or JS) Module objects; the order that
216  they're inserted into the file depends on their type and position in the load
217  sequence.
218  """
219  _AssertIsUTF8(output_file)
220  extra_scripts = extra_scripts or []
221
222  if output_html_head_and_body:
223    output_file.write(
224        '<!DOCTYPE html>\n'
225        '<html>\n'
226        '  <head i18n-values="dir:textdirection;">\n'
227        '  <meta http-equiv="Content-Type" content="text/html;'
228        'charset=utf-8">\n')
229    if title:
230      output_file.write('  <title>%s</title>\n  ' % title)
231  else:
232    assert title is None
233
234  loader = load_sequence[0].loader
235
236  written_style_sheets = set()
237
238  class HTMLGenerationController(
239      html_generation_controller.HTMLGenerationController):
240
241    def __init__(self, module):
242      self.module = module
243
244    def GetHTMLForStylesheetHRef(self, href):
245      resource = self.module.HRefToResource(
246          href, '<link rel="stylesheet" href="%s">' % href)
247      style_sheet = loader.LoadStyleSheet(resource.name)
248
249      if style_sheet in written_style_sheets:
250        return None
251      written_style_sheets.add(style_sheet)
252
253      text = style_sheet.contents_with_inlined_images
254      if minify:
255        text = _MinifyCSS(text)
256      return '<style>\n%s\n</style>' % text
257
258  for module in load_sequence:
259    controller = HTMLGenerationController(module)
260    module.AppendHTMLContentsToFile(output_file, controller, minify=minify)
261
262  if flattened_js_url:
263    output_file.write('<script src="%s"></script>\n' % flattened_js_url)
264  else:
265    output_file.write('<script>\n')
266    js = GenerateJS(load_sequence, minify=minify, report_sizes=report_sizes)
267    output_file.write(js)
268    output_file.write('</script>\n')
269
270  for extra_script in extra_scripts:
271    extra_script.WriteToFile(output_file)
272
273  if output_html_head_and_body:
274    output_file.write('</head>\n  <body>\n  </body>\n</html>\n')
275