1#!/usr/bin/env python 2 3# Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved. 4# 5# Redistribution and use in source and binary forms, with or without 6# modification, are permitted provided that the following conditions 7# are met: 8# 9# 1. Redistributions of source code must retain the above 10# copyright notice, this list of conditions and the following 11# disclaimer. 12# 2. Redistributions in binary form must reproduce the above 13# copyright notice, this list of conditions and the following 14# disclaimer in the documentation and/or other materials 15# provided with the distribution. 16# 17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY 18# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE 21# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 22# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 26# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 27# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28# SUCH DAMAGE. 29 30import logging 31import re 32 33from webkitpy.common.host import Host 34from webkitpy.common.webkit_finder import WebKitFinder 35from HTMLParser import HTMLParser 36 37 38_log = logging.getLogger(__name__) 39 40 41def convert_for_webkit(new_path, filename, host=Host()): 42 """ Converts a file's |contents| so it will function correctly in its |new_path| in Webkit. 43 44 Returns the list of modified properties and the modified text if the file was modifed, None otherwise.""" 45 contents = host.filesystem.read_binary_file(filename) 46 converter = _W3CTestConverter(new_path, filename, host) 47 if filename.endswith('.css'): 48 return converter.add_webkit_prefix_to_unprefixed_properties(contents) 49 else: 50 converter.feed(contents) 51 converter.close() 52 return converter.output() 53 54 55class _W3CTestConverter(HTMLParser): 56 def __init__(self, new_path, filename, host=Host()): 57 HTMLParser.__init__(self) 58 59 self._host = host 60 self._filesystem = self._host.filesystem 61 self._webkit_root = WebKitFinder(self._filesystem).webkit_base() 62 63 self.converted_data = [] 64 self.converted_properties = [] 65 self.in_style_tag = False 66 self.style_data = [] 67 self.filename = filename 68 69 resources_path = self.path_from_webkit_root('LayoutTests', 'resources') 70 resources_relpath = self._filesystem.relpath(resources_path, new_path) 71 self.resources_relpath = resources_relpath 72 73 # These settings might vary between WebKit and Blink 74 self._css_property_file = self.path_from_webkit_root('Source', 'core', 'css', 'CSSProperties.in') 75 76 self.prefixed_properties = self.read_webkit_prefixed_css_property_list() 77 78 self.prefixed_properties = self.read_webkit_prefixed_css_property_list() 79 prop_regex = '([\s{]|^)(' + "|".join(prop.replace('-webkit-', '') for prop in self.prefixed_properties) + ')(\s+:|:)' 80 self.prop_re = re.compile(prop_regex) 81 82 def output(self): 83 return (self.converted_properties, ''.join(self.converted_data)) 84 85 def path_from_webkit_root(self, *comps): 86 return self._filesystem.abspath(self._filesystem.join(self._webkit_root, *comps)) 87 88 def read_webkit_prefixed_css_property_list(self): 89 prefixed_properties = [] 90 unprefixed_properties = set() 91 92 contents = self._filesystem.read_text_file(self._css_property_file) 93 for line in contents.splitlines(): 94 if re.match('^(#|//|$)', line): 95 # skip comments and preprocessor directives 96 continue 97 prop = line.split()[0] 98 # Find properties starting with the -webkit- prefix. 99 match = re.match('-webkit-([\w|-]*)', prop) 100 if match: 101 prefixed_properties.append(match.group(1)) 102 else: 103 unprefixed_properties.add(prop.strip()) 104 105 # Ignore any prefixed properties for which an unprefixed version is supported 106 return [prop for prop in prefixed_properties if prop not in unprefixed_properties] 107 108 def add_webkit_prefix_to_unprefixed_properties(self, text): 109 """ Searches |text| for instances of properties requiring the -webkit- prefix and adds the prefix to them. 110 111 Returns the list of converted properties and the modified text.""" 112 113 converted_properties = set() 114 text_chunks = [] 115 cur_pos = 0 116 for m in self.prop_re.finditer(text): 117 text_chunks.extend([text[cur_pos:m.start()], m.group(1), '-webkit-', m.group(2), m.group(3)]) 118 converted_properties.add(m.group(2)) 119 cur_pos = m.end() 120 text_chunks.append(text[cur_pos:]) 121 122 for prop in converted_properties: 123 _log.info(' converting %s', prop) 124 125 # FIXME: Handle the JS versions of these properties and GetComputedStyle, too. 126 return (converted_properties, ''.join(text_chunks)) 127 128 def convert_style_data(self, data): 129 converted = self.add_webkit_prefix_to_unprefixed_properties(data) 130 if converted[0]: 131 self.converted_properties.extend(list(converted[0])) 132 return converted[1] 133 134 def convert_attributes_if_needed(self, tag, attrs): 135 converted = self.get_starttag_text() 136 if tag in ('script', 'link'): 137 target_attr = 'src' 138 if tag != 'script': 139 target_attr = 'href' 140 for attr_name, attr_value in attrs: 141 if attr_name == target_attr: 142 new_path = re.sub('/resources/testharness', 143 self.resources_relpath + '/testharness', 144 attr_value) 145 converted = re.sub(attr_value, new_path, converted) 146 new_path = re.sub('/common/vendor-prefix', 147 self.resources_relpath + '/vendor-prefix', 148 attr_value) 149 converted = re.sub(attr_value, new_path, converted) 150 151 for attr_name, attr_value in attrs: 152 if attr_name == 'style': 153 new_style = self.convert_style_data(attr_value) 154 converted = re.sub(attr_value, new_style, converted) 155 if attr_name == 'class' and 'instructions' in attr_value: 156 # Always hide instructions, they're for manual testers. 157 converted = re.sub(' style=".*?"', '', converted) 158 converted = re.sub('\>', ' style="display:none">', converted) 159 160 self.converted_data.append(converted) 161 162 def handle_starttag(self, tag, attrs): 163 if tag == 'style': 164 self.in_style_tag = True 165 self.convert_attributes_if_needed(tag, attrs) 166 167 def handle_endtag(self, tag): 168 if tag == 'style': 169 self.converted_data.append(self.convert_style_data(''.join(self.style_data))) 170 self.in_style_tag = False 171 self.style_data = [] 172 self.converted_data.extend(['</', tag, '>']) 173 174 def handle_startendtag(self, tag, attrs): 175 self.convert_attributes_if_needed(tag, attrs) 176 177 def handle_data(self, data): 178 if self.in_style_tag: 179 self.style_data.append(data) 180 else: 181 self.converted_data.append(data) 182 183 def handle_entityref(self, name): 184 self.converted_data.extend(['&', name, ';']) 185 186 def handle_charref(self, name): 187 self.converted_data.extend(['&#', name, ';']) 188 189 def handle_comment(self, data): 190 self.converted_data.extend(['<!-- ', data, ' -->']) 191 192 def handle_decl(self, decl): 193 self.converted_data.extend(['<!', decl, '>']) 194 195 def handle_pi(self, data): 196 self.converted_data.extend(['<?', data, '>']) 197 198