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