364 lines
11 KiB
Python
364 lines
11 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 Element.tag('div').style;
|
|
style.cssText = css;
|
|
return style;
|
|
}
|
|
|
|
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_(unwrap_jso(this), propertyName) != null;
|
|
$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()
|