blob: db71ab911d7516d2d44ba1aa715fc20e1a4ef33d [file] [log] [blame]
# Copyright (c) 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 base64
import codecs
import httplib
import optparse
import json
import os
import re
import sys
import subprocess
import tempfile
import urllib
import StringIO
from tvcm import js_utils
from tvcm import module as module_module
from tvcm import html_generation_controller
srcdir = os.path.abspath(os.path.join(os.path.dirname(__file__),
"..", "..", "..", "src"))
html_warning_message = """
<!--
WARNING: This file is auto generated.
Do not edit directly.
-->
"""
js_warning_message = """
// Copyright (c) 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.
/* WARNING: This file is auto generated.
*
* Do not edit directly.
*/
"""
css_warning_message = """
/* Copyright (c) 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. */
/* WARNING: This file is auto-generated.
*
* Do not edit directly.
*/
"""
def _AssertIsUTF8(f):
if isinstance(f, StringIO.StringIO):
return
assert f.encoding == 'utf-8'
def CanMinify():
return _HasLocalClosureCompiler()
_HasLocalClosureCompilerResult = None
def _HasLocalClosureCompiler():
global _HasLocalClosureCompilerResult
if _HasLocalClosureCompilerResult != None:
return _HasLocalClosureCompilerResult
try:
_MinifyJSLocally('console.log("test");')
_HasLocalClosureCompilerResult = True
except:
_HasLocalClosureCompilerResult = False
return _HasLocalClosureCompilerResult
def _MinifyJS(input_js):
if _HasLocalClosureCompiler():
return _MinifyJSLocally(input_js)
return _MinifyJSUsingClosureService(input_js)
def _MinifyJSLocally(input_js):
with open(os.devnull, 'w') as devnull:
with tempfile.NamedTemporaryFile() as f:
args = [
'closure-compiler',
'--compilation_level', 'SIMPLE_OPTIMIZATIONS',
'--language_in', 'ECMASCRIPT5',
'--warning_level', 'QUIET'
]
p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=devnull)
res = p.communicate(input=input_js)
if p.wait() != 0:
raise Exception('Failed to minify, omgah')
return res[0]
def _MinifyJSUsingClosureService(input_js):
# Define the parameters for the POST request and encode them in
# a URL-safe format.
params = urllib.urlencode([
('js_code', input_js),
('language', 'ECMASCRIPT5'),
('compilation_level', 'SIMPLE_OPTIMIZATIONS'),
('output_format', 'json'),
('output_info', 'errors'),
('output_info', 'compiled_code'),
])
# Always use the following value for the Content-type header.
headers = { "Content-type": "application/x-www-form-urlencoded" }
conn = httplib.HTTPConnection('closure-compiler.appspot.com')
conn.request('POST', '/compile', params, headers)
response = conn.getresponse()
data = response.read()
conn.close
if response.status != 200:
raise Exception("error returned from JS compile service: %d" % response.status)
result = json.loads(data)
if 'errors' in result:
errors = []
for e in result['errors']:
filenum = int(e['file'][6:])
filename = 'flat_script.js'
lineno = e['lineno']
charno = e['charno']
err = e['error']
errors.append('%s:%d:%d: %s' % (filename, lineno, charno, err))
raise Exception('Failed to generate %s. Reason: %s' % (output_js_file,
'\n'.join(errors)))
if 'compiledCode' not in result:
raise Exception('Got %s' % repr(result))
return result['compiledCode']
def GenerateJS(load_sequence,
use_include_tags_for_scripts=False,
dir_for_include_tag_root=None,
minify=False,
report_sizes=False):
f = StringIO.StringIO()
GenerateJSToFile(f,
load_sequence,
use_include_tags_for_scripts,
dir_for_include_tag_root,
minify=minify,
report_sizes=report_sizes)
return f.getvalue()
def pt_parts(self):
sl = ['unicode and 8-bit string parts of above page template']
for x in self.buflist:
if type(x) == type(''):
maxcode = 0
for c in x:
maxcode = max(ord(c), maxcode)
# show only unicode objects and non-ascii strings
if type(x) == type('') and maxcode > 127:
t = '****NonAsciiStr: '
elif type(x) == type(u''):
t = '*****UnicodeStr: '
else:
t = None
if t:
sl.append(t + repr(x))
s = '\n'.join(sl)
return s
def GenerateJSToFile(f,
load_sequence,
use_include_tags_for_scripts=False,
dir_for_include_tag_root=None,
minify=False,
report_sizes=False):
_AssertIsUTF8(f)
if use_include_tags_for_scripts and dir_for_include_tag_root == None:
raise Exception('Must provide dir_for_include_tag_root')
f.write(js_warning_message)
f.write('\n')
loader = load_sequence[0].loader
polymer_script = loader.LoadRawScript('components/polymer/polymer.min.js')
f.write(polymer_script.contents)
f.write('\n')
f.write("window._TV_IS_COMPILED = true;\n")
if not minify:
flatten_to_file = f
else:
flatten_to_file = StringIO.StringIO()
for module in load_sequence:
module.AppendJSContentsToFile(flatten_to_file,
use_include_tags_for_scripts,
dir_for_include_tag_root)
if minify:
js = flatten_to_file.getvalue()
minified_js = _MinifyJS(js)
f.write(minified_js)
f.write('\n')
if report_sizes:
for module in load_sequence:
s = StringIO.StringIO()
module.AppendJSContentsToFile(s,
use_include_tags_for_scripts,
dir_for_include_tag_root)
# Sizing. Can only show minified sizes if closure-compiler is local.
js = s.getvalue()
if _HasLocalClosureCompiler():
min_js_size = str(len(_MinifyJS(js)))
else:
min_js_size = '-'
# Print names for this module. Some domain-specific simplifciations
# are included to make pivoting more obvious.
parts = module.name.split('.')
if parts[:2] == ['base', 'ui']:
parts = ['base_ui'] + parts[2:]
if parts[:2] == ['tracing', 'importer']:
parts = ['importer'] + parts[2:]
tln = parts[0]
sln = '.'.join(parts[:2])
# Ouptut
print '%i\t%s\t%s\t%s\t%s' % (len(js), min_js_size, module.name, tln, sln)
sys.stdout.flush()
class ExtraScript(object):
def __init__(self, script_id=None, text_content=None, content_type=None):
if script_id != None:
assert script_id[0] != '#'
self.script_id = script_id
self.text_content = text_content
self.content_type = content_type
def WriteToFile(self, output_file):
_AssertIsUTF8(output_file)
attrs = []
if self.script_id:
attrs.append('id="%s"' % self.script_id)
if self.content_type:
attrs.append('content-type="%s"' % self.content_type)
if len(attrs) > 0:
output_file.write('<script %s>\n' % ' '.join(attrs))
else:
output_file.write('<script>\n')
if self.text_content:
output_file.write(self.text_content)
output_file.write('</script>\n')
_have_printed_yui_missing_warning = False
def _MinifyCSS(css_text):
with tempfile.NamedTemporaryFile() as f:
yuic_args = ['yui-compressor', '--type', 'css', '-o', f.name]
try:
p = subprocess.Popen(yuic_args, stdin=subprocess.PIPE)
except OSError:
global _have_printed_yui_missing_warning
if not _have_printed_yui_missing_warning:
_have_printed_yui_missing_warning = True
sys.stderr.write(
'yui-compressor missing. CSS minification will be skipped.\n')
return css_text
p.communicate(input=css_text)
if p.wait() != 0:
raise Exception('Failed to generate %s.' % output_css_file)
with codecs.open(f.name, mode='r', encoding='utf-8') as f2:
return f2.read()
def GenerateStandaloneHTMLAsString(*args, **kwargs):
f = StringIO.StringIO()
GenerateStandaloneHTMLToFile(f, *args, **kwargs)
return f.getvalue()
def GenerateStandaloneHTMLToFile(output_file,
load_sequence,
title=None,
flattened_js_url=None,
extra_scripts=None,
minify=False,
report_sizes=False,
output_html_head_and_body=True):
_AssertIsUTF8(output_file)
extra_scripts = extra_scripts or []
if output_html_head_and_body:
output_file.write("""<!DOCTYPE HTML>
<html>
<head i18n-values="dir:textdirection;">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
""")
if title:
output_file.write(""" <title>%s</title>
""" % title)
else:
assert title == None
loader = load_sequence[0].loader
written_style_sheets = set()
class HTMLGenerationController(html_generation_controller.HTMLGenerationController):
def __init__(self, module):
self.module = module
def GetHTMLForStylesheetHRef(self, href):
resource = self.module.HRefToResource(
href, '<link rel="stylesheet" href="%s">' % href)
style_sheet = loader.LoadStyleSheet(resource.name)
if style_sheet in written_style_sheets:
return None
written_style_sheets.add(style_sheet)
text = style_sheet.contents_with_inlined_images
if minify:
text = _MinifyCSS(text)
return "<style>\n%s\n</style>" % text
for module in load_sequence:
ctl = HTMLGenerationController(module)
module.AppendHTMLContentsToFile(output_file, ctl, minify=minify)
if flattened_js_url:
output_file.write('<script src="%s"></script>\n' % flattened_js_url)
else:
output_file.write('<script>\n')
x = GenerateJS(load_sequence, minify=minify, report_sizes=report_sizes)
output_file.write(x)
output_file.write('</script>\n')
for extra_script in extra_scripts:
extra_script.WriteToFile(output_file)
if output_html_head_and_body:
output_file.write("""</head>
<body>
</body>
</html>
""")