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