b7b12b564a
The body of new DivElement() is marked as returning a DivElement, instead of inferred to return Element.
The more precise receiver type allows svg polyfills to be excluded, allowsing the HtmlElement methods to be identified as the single target and inlined.
This tends to improve the code generated for idioms like
new DivElement()..classes.add('foo')
var e1 = new DivElement();
e1.children.add(e2);
BUG=
R=alanknight@google.com
Review-Url: https://codereview.chromium.org/2705213003 .
369 lines
12 KiB
Python
369 lines
12 KiB
Python
#!/usr/bin/python
|
|
#
|
|
# Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
|
# for details. All rights reserved. Use of this source code is governed by a
|
|
# BSD-style license that can be found in the LICENSE file.
|
|
|
|
"""Generates CSSStyleDeclaration template file from css property definitions
|
|
defined in WebKit."""
|
|
|
|
import tempfile, os, re
|
|
|
|
COMMENT_LINE_PREFIX = ' * '
|
|
# TODO(efortuna): Pull from DEPS so that we have latest css *in sync* with our
|
|
# Dartium. Then remove the checked in CSSPropertyNames.in.
|
|
SOURCE_PATH = 'CSSPropertyNames.in'
|
|
#SOURCE_PATH = 'Source/WebCore/css/CSSPropertyNames.in'
|
|
TEMPLATE_FILE = '../templates/html/impl/impl_CSSStyleDeclaration.darttemplate'
|
|
|
|
# These are the properties that are supported on all Dart project supported
|
|
# browsers as camelCased names on the CssStyleDeclaration.
|
|
BROWSER_PATHS = [
|
|
'cssProperties.CSS21.txt', # Remove when we have samples from all browsers.
|
|
'cssProperties.ie9.txt',
|
|
'cssProperties.ie10.txt',
|
|
'cssProperties.ie11.txt',
|
|
'cssProperties.ff36.txt',
|
|
'cssProperties.chrome40.txt',
|
|
'cssProperties.safari-7.1.3.txt',
|
|
'cssProperties.mobileSafari-8.2.txt',
|
|
'cssProperties.iPad4Air.onGoogleSites.txt',
|
|
]
|
|
|
|
# Supported annotations for any specific CSS properties.
|
|
annotated = {
|
|
'transition': '''@SupportedBrowser(SupportedBrowser.CHROME)
|
|
@SupportedBrowser(SupportedBrowser.FIREFOX)
|
|
@SupportedBrowser(SupportedBrowser.IE, '10')
|
|
@SupportedBrowser(SupportedBrowser.SAFARI)'''
|
|
}
|
|
|
|
class Error:
|
|
def __init__(self, message):
|
|
self.message = message
|
|
def __repr__(self):
|
|
return self.message
|
|
|
|
def camelCaseName(name):
|
|
"""Convert a CSS property name to a lowerCamelCase name."""
|
|
name = name.replace('-webkit-', '')
|
|
words = []
|
|
for word in name.split('-'):
|
|
if words:
|
|
words.append(word.title())
|
|
else:
|
|
words.append(word)
|
|
return ''.join(words)
|
|
|
|
def dashifyName(camelName):
|
|
def fix(match):
|
|
return '-' + match.group(0).lower()
|
|
return re.sub(r'[A-Z]', fix, camelName)
|
|
|
|
def isCommentLine(line):
|
|
return line.strip() == '' or line.startswith('#') or line.startswith('//')
|
|
|
|
def readCssProperties(filename):
|
|
data = open(filename).readlines()
|
|
data = sorted([d.strip() for d in set(data) if not isCommentLine(d)])
|
|
return data
|
|
|
|
def GenerateCssTemplateFile():
|
|
data = open(SOURCE_PATH).readlines()
|
|
|
|
# filter CSSPropertyNames.in to only the properties
|
|
# TODO(efortuna): do we also want CSSPropertyNames.in?
|
|
data = [d.strip() for d in data
|
|
if not isCommentLine(d)
|
|
and not '=' in d]
|
|
|
|
browser_props = [readCssProperties(file) for file in BROWSER_PATHS]
|
|
universal_properties = reduce(
|
|
lambda a, b: set(a).intersection(b), browser_props)
|
|
universal_properties = universal_properties.difference(['cssText'])
|
|
universal_properties = universal_properties.intersection(
|
|
map(camelCaseName, data))
|
|
|
|
class_file = open(TEMPLATE_FILE, 'w')
|
|
|
|
class_file.write("""
|
|
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
// WARNING: DO NOT EDIT THIS TEMPLATE FILE.
|
|
// The template file was generated by scripts/css_code_generator.py
|
|
|
|
// Source of CSS properties:
|
|
// %s
|
|
|
|
part of $LIBRARYNAME;
|
|
""" % SOURCE_PATH)
|
|
|
|
|
|
class_file.write("""
|
|
$(ANNOTATIONS)$(NATIVESPEC)$(CLASS_MODIFIERS)class $CLASSNAME $EXTENDS with
|
|
$(CLASSNAME)Base $IMPLEMENTS {
|
|
factory $CLASSNAME() => new CssStyleDeclaration.css('');
|
|
|
|
factory $CLASSNAME.css(String css) {
|
|
final style = new DivElement().style;
|
|
style.cssText = css;
|
|
return style;
|
|
}
|
|
|
|
/// Returns the value of the property if the provided *CSS* property
|
|
/// name is supported on this element and if the value is set. Otherwise
|
|
/// returns an empty string.
|
|
///
|
|
/// Please note the property name uses camelCase, not-hyphens.
|
|
String getPropertyValue(String propertyName) {
|
|
var propValue = _getPropertyValueHelper(propertyName);
|
|
return propValue != null ? propValue : '';
|
|
}
|
|
|
|
String _getPropertyValueHelper(String propertyName) {
|
|
if (_supportsProperty(_camelCase(propertyName))) {
|
|
return _getPropertyValue(propertyName);
|
|
} else {
|
|
return _getPropertyValue(Device.cssPrefix + propertyName);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns true if the provided *CSS* property name is supported on this
|
|
* element.
|
|
*
|
|
* Please note the property name camelCase, not-hyphens. This
|
|
* method returns true if the property is accessible via an unprefixed _or_
|
|
* prefixed property.
|
|
*/
|
|
bool supportsProperty(String propertyName) {
|
|
return _supportsProperty(propertyName) ||
|
|
_supportsProperty(_camelCase(Device.cssPrefix + propertyName));
|
|
}
|
|
|
|
bool _supportsProperty(String propertyName) {
|
|
$if DART2JS
|
|
return JS('bool', '# in #', propertyName, this);
|
|
$else
|
|
// You can't just check the value of a property, because there is no way
|
|
// to distinguish between property not being present in the browser and
|
|
// not having a value at all. (Ultimately we'll want the native method to
|
|
// return null if the property doesn't exist and empty string if it's
|
|
// defined but just doesn't have a value.
|
|
return _hasProperty(propertyName);
|
|
$endif
|
|
}
|
|
|
|
$if DARTIUM
|
|
bool _hasProperty(String propertyName) =>
|
|
$if JSINTEROP
|
|
_blink.BlinkCSSStyleDeclaration.instance.$__propertyQuery___Callback_1_(this, propertyName);
|
|
$else
|
|
_blink.BlinkCSSStyleDeclaration.$__propertyQuery___Callback_1(this, propertyName);
|
|
$endif
|
|
$endif
|
|
|
|
@DomName('CSSStyleDeclaration.setProperty')
|
|
void setProperty(String propertyName, String value, [String priority]) {
|
|
return _setPropertyHelper(_browserPropertyName(propertyName),
|
|
value, priority);
|
|
}
|
|
|
|
String _browserPropertyName(String propertyName) {
|
|
String name = _readCache(propertyName);
|
|
if (name is String) return name;
|
|
if (_supportsProperty(_camelCase(propertyName))) {
|
|
name = propertyName;
|
|
} else {
|
|
name = Device.cssPrefix + propertyName;
|
|
}
|
|
_writeCache(propertyName, name);
|
|
return name;
|
|
}
|
|
|
|
$if DART2JS
|
|
static final _propertyCache = JS('', '{}');
|
|
static String _readCache(String key) =>
|
|
JS('String|Null', '#[#]', _propertyCache, key);
|
|
static void _writeCache(String key, String value) {
|
|
JS('void', '#[#] = #', _propertyCache, key, value);
|
|
}
|
|
$else
|
|
static String _readCache(String key) => null;
|
|
static void _writeCache(String key, value) {}
|
|
$endif
|
|
|
|
static String _camelCase(String hyphenated) {
|
|
$if DART2JS
|
|
var replacedMs = JS('String', r'#.replace(/^-ms-/, "ms-")', hyphenated);
|
|
|
|
var fToUpper = const JS_CONST(
|
|
r'function(_, letter) { return letter.toUpperCase(); }');
|
|
return JS('String', r'#.replace(/-([\da-z])/ig, #)', replacedMs, fToUpper);
|
|
$else
|
|
// The "ms" prefix is always lowercased.
|
|
return hyphenated.replaceFirst(new RegExp('^-ms-'), 'ms-').replaceAllMapped(
|
|
new RegExp('-([a-z]+)', caseSensitive: false),
|
|
(match) => match[0][1].toUpperCase() + match[0].substring(2));
|
|
$endif
|
|
}
|
|
|
|
$if DART2JS
|
|
void _setPropertyHelper(String propertyName, String value, [String priority]) {
|
|
if (value == null) value = '';
|
|
if (priority == null) priority = '';
|
|
JS('void', '#.setProperty(#, #, #)', this, propertyName, value, priority);
|
|
}
|
|
|
|
/**
|
|
* Checks to see if CSS Transitions are supported.
|
|
*/
|
|
static bool get supportsTransitions {
|
|
return document.body.style.supportsProperty('transition');
|
|
}
|
|
$else
|
|
void _setPropertyHelper(String propertyName, String value, [String priority]) {
|
|
if (priority == null) {
|
|
priority = '';
|
|
}
|
|
_setProperty(propertyName, value, priority);
|
|
}
|
|
|
|
/**
|
|
* Checks to see if CSS Transitions are supported.
|
|
*/
|
|
static bool get supportsTransitions => true;
|
|
$endif
|
|
$!MEMBERS
|
|
$if DART2JS
|
|
""")
|
|
|
|
for camelName in sorted(universal_properties):
|
|
property = dashifyName(camelName)
|
|
class_file.write("""
|
|
/** Gets the value of "%s" */
|
|
String get %s => this._%s;
|
|
|
|
/** Sets the value of "%s" */
|
|
set %s(String value) {
|
|
_%s = value == null ? '' : value;
|
|
}
|
|
@Returns('String')
|
|
@JSName('%s')
|
|
String _%s;
|
|
""" % (property, camelName, camelName,
|
|
property, camelName, camelName,
|
|
camelName, camelName))
|
|
|
|
class_file.write("""
|
|
$endif
|
|
}
|
|
|
|
class _CssStyleDeclarationSet extends Object with CssStyleDeclarationBase {
|
|
final Iterable<Element> _elementIterable;
|
|
Iterable<CssStyleDeclaration> _elementCssStyleDeclarationSetIterable;
|
|
|
|
_CssStyleDeclarationSet(this._elementIterable) {
|
|
_elementCssStyleDeclarationSetIterable = new List.from(
|
|
_elementIterable).map((e) => e.style);
|
|
}
|
|
|
|
String getPropertyValue(String propertyName) =>
|
|
_elementCssStyleDeclarationSetIterable.first.getPropertyValue(
|
|
propertyName);
|
|
|
|
void setProperty(String propertyName, String value, [String priority]) {
|
|
_elementCssStyleDeclarationSetIterable.forEach((e) =>
|
|
e.setProperty(propertyName, value, priority));
|
|
}
|
|
|
|
""")
|
|
|
|
class_file.write("""
|
|
$if DART2JS
|
|
void _setAll(String propertyName, String value) {
|
|
value = value == null ? '' : value;
|
|
for (Element element in _elementIterable) {
|
|
JS('void', '#.style[#] = #', element, propertyName, value);
|
|
}
|
|
}
|
|
""")
|
|
|
|
|
|
for camelName in sorted(universal_properties):
|
|
property = dashifyName(camelName)
|
|
class_file.write("""
|
|
/** Sets the value of "%s" */
|
|
set %s(String value) {
|
|
_setAll('%s', value);
|
|
}
|
|
""" % (property, camelName, camelName))
|
|
|
|
class_file.write("""
|
|
$endif
|
|
|
|
// Important note: CssStyleDeclarationSet does NOT implement every method
|
|
// available in CssStyleDeclaration. Some of the methods don't make so much
|
|
// sense in terms of having a resonable value to return when you're
|
|
// considering a list of Elements. You will need to manually add any of the
|
|
// items in the MEMBERS set if you want that functionality.
|
|
}
|
|
|
|
$if DART2JS
|
|
abstract class CssStyleDeclarationBase {
|
|
String getPropertyValue(String propertyName);
|
|
void setProperty(String propertyName, String value, [String priority]);
|
|
$else
|
|
$if JSINTEROP
|
|
class CssStyleDeclarationBase {
|
|
String getPropertyValue(String propertyName) =>
|
|
throw new StateError('getProperty not overridden in dart:html');
|
|
void setProperty(String propertyName, String value, [String priority]) =>
|
|
throw new StateError('setProperty not overridden in dart:html');
|
|
$else
|
|
abstract class CssStyleDeclarationBase {
|
|
String getPropertyValue(String propertyName);
|
|
void setProperty(String propertyName, String value, [String priority]);
|
|
$endif
|
|
$endif
|
|
""")
|
|
|
|
class_lines = [];
|
|
|
|
seen = set()
|
|
for prop in sorted(data, key=camelCaseName):
|
|
camel_case_name = camelCaseName(prop)
|
|
upper_camel_case_name = camel_case_name[0].upper() + camel_case_name[1:];
|
|
css_name = prop.replace('-webkit-', '')
|
|
base_css_name = prop.replace('-webkit-', '')
|
|
|
|
if base_css_name in seen or base_css_name.startswith('-internal'):
|
|
continue
|
|
seen.add(base_css_name)
|
|
|
|
comment = ' /** %s the value of "' + base_css_name + '" */'
|
|
class_lines.append('\n');
|
|
class_lines.append(comment % 'Gets')
|
|
if base_css_name in annotated:
|
|
class_lines.append(annotated[base_css_name])
|
|
class_lines.append("""
|
|
String get %s =>
|
|
getPropertyValue('%s');
|
|
|
|
""" % (camel_case_name, css_name))
|
|
|
|
class_lines.append(comment % 'Sets')
|
|
if base_css_name in annotated:
|
|
class_lines.append(annotated[base_css_name])
|
|
class_lines.append("""
|
|
set %s(String value) {
|
|
setProperty('%s', value, '');
|
|
}
|
|
""" % (camel_case_name, css_name))
|
|
|
|
class_file.write(''.join(class_lines));
|
|
class_file.write('}\n')
|
|
class_file.close()
|