bccb2be4d041f6a8157a01f517361475e5e1a12e
[WebKit-https.git] / Tools / Scripts / webkitpy / w3c / test_converter.py
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
30 import re
31
32 from webkitpy.common.host import Host
33 from webkitpy.thirdparty.BeautifulSoup import BeautifulSoup, Tag
34
35
36 class W3CTestConverter(object):
37
38     def __init__(self):
39         self._host = Host()
40         self._filesystem = self._host.filesystem
41         self._host.initialize_scm()
42         self._webkit_root = self._host.scm().checkout_root
43         self.prefixed_properties = self.read_webkit_prefixed_css_property_list()
44
45     def path_from_webkit_root(self, *comps):
46         return self._filesystem.abspath(self._filesystem.join(self._webkit_root, *comps))
47
48     def read_webkit_prefixed_css_property_list(self):
49         prefixed_properties = []
50
51         contents = self._filesystem.read_text_file(self.path_from_webkit_root('Source', 'WebCore', 'css', 'CSSPropertyNames.in'))
52         for line in contents.splitlines():
53             # Find lines starting with the -webkit- prefix.
54             match = re.match('-webkit-[\w|-]*', line)
55             if match:
56                 # Ignore lines where both the prefixed and non-prefixed property
57                 # are supported - denoted by -webkit-some-property = some-property.
58                 fields = line.split('=')
59                 if len(fields) == 2 and fields[1].strip() in fields[0].strip():
60                     continue
61                 prefixed_properties.append(match.group(0))
62
63         return prefixed_properties
64
65     def convert_for_webkit(self, new_path, filename):
66         """ Converts a file's |contents| so it will function correctly in its |new_path| in Webkit.
67
68         Returns the list of modified properties and the modified text if the file was modifed, None otherwise."""
69         contents = self._filesystem.read_text_file(filename)
70         if filename.endswith('.css'):
71             return self.convert_css(contents)
72         return self.convert_html(new_path, contents)
73
74     def convert_css(self, contents):
75         return self.add_webkit_prefix_to_unprefixed_properties(contents)
76
77     def convert_html(self, new_path, contents):
78         doc = BeautifulSoup(contents)
79         did_modify_paths = self.convert_testharness_paths(doc, new_path)
80         converted_properties_and_content = self.convert_prefixed_properties(doc)
81         return converted_properties_and_content if (did_modify_paths or converted_properties_and_content[0]) else None
82
83     def convert_testharness_paths(self, doc, new_path):
84         """ Update links to testharness.js in the BeautifulSoup |doc| to point to the copy in |new_path|.
85
86         Returns whether the document was modified."""
87
88         # Look for the W3C-style path to any testharness files - scripts (.js) or links (.css)
89         pattern = re.compile('/resources/testharness')
90         script_tags = doc.findAll(src=pattern)
91         link_tags = doc.findAll(href=pattern)
92         testharness_tags = script_tags + link_tags
93
94         if not testharness_tags:
95             return False
96
97         resources_path = self.path_from_webkit_root('LayoutTests', 'resources')
98         resources_relpath = self._filesystem.relpath(resources_path, new_path)
99
100         for tag in testharness_tags:
101             attr = 'src'
102             if tag.name != 'script':
103                 attr = 'href'
104
105             old_path = tag[attr]
106             new_tag = Tag(doc, tag.name, tag.attrs)
107             new_tag[attr] = re.sub(pattern, resources_relpath + '/testharness', old_path)
108
109             self.replace_tag(tag, new_tag)
110
111         return True
112
113     def convert_prefixed_properties(self, doc):
114         """ Searches a BeautifulSoup |doc| for any CSS properties requiring the -webkit- prefix and converts them.
115
116         Returns the list of converted properties and the modified document as a string """
117
118         converted_properties = []
119
120         # Look for inline and document styles.
121         inline_styles = doc.findAll(style=re.compile('.*'))
122         style_tags = doc.findAll('style')
123         all_styles = inline_styles + style_tags
124
125         for tag in all_styles:
126
127             # Get the text whether in a style tag or style attribute.
128             style_text = ''
129             if tag.name == 'style':
130                 if not tag.contents:
131                     continue
132                 style_text = tag.contents[0]
133             else:
134                 style_text = tag['style']
135
136             updated_style_text = self.add_webkit_prefix_to_unprefixed_properties(style_text)
137
138             # Rewrite tag only if changes were made.
139             if updated_style_text[0]:
140                 converted_properties.extend(updated_style_text[0])
141
142                 new_tag = Tag(doc, tag.name, tag.attrs)
143                 new_tag.insert(0, updated_style_text[1])
144
145                 self.replace_tag(tag, new_tag)
146
147         return (converted_properties, doc.prettify())
148
149     def add_webkit_prefix_to_unprefixed_properties(self, text):
150         """ Searches |text| for instances of properties requiring the -webkit- prefix and adds the prefix to them.
151
152         Returns the list of converted properties and the modified text."""
153
154         converted_properties = []
155
156         for prefixed_property in self.prefixed_properties:
157
158             unprefixed_property = prefixed_property.replace('-webkit-', '')
159
160             # Look for the various ways it might be in the CSS
161             # Match the the property preceded by either whitespace or left curly brace
162             # or at the beginning of the string (for inline style attribute)
163             pattern = '([\s{]|^)' + unprefixed_property + '(\s+:|:)'
164             if re.search(pattern, text):
165                 print 'converting %s -> %s' % (unprefixed_property, prefixed_property)
166                 converted_properties.append(prefixed_property)
167                 text = re.sub(pattern, prefixed_property + ':', text)
168
169         # FIXME: Handle the JS versions of these properties, too.
170         return (converted_properties, text)
171
172     def replace_tag(self, old_tag, new_tag):
173         index = old_tag.parent.contents.index(old_tag)
174         old_tag.parent.insert(index, new_tag)
175         old_tag.extract()