Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(322)

Side by Side Diff: tools/dom/templates/html/impl/impl_Element.darttemplate

Issue 16374007: First rev of Safe DOM (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 part of $LIBRARYNAME; 5 part of $LIBRARYNAME;
6 6
7 class _ChildrenElementList extends ListBase<Element> { 7 class _ChildrenElementList extends ListBase<Element> {
8 // Raw Element. 8 // Raw Element.
9 final Element _element; 9 final Element _element;
10 final HtmlCollection _childElements; 10 final HtmlCollection _childElements;
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after
228 * 228 *
229 * Important: the contents of [html] should not contain any user-supplied 229 * Important: the contents of [html] should not contain any user-supplied
230 * data. Without strict data validation it is impossible to prevent script 230 * data. Without strict data validation it is impossible to prevent script
231 * injection exploits. 231 * injection exploits.
232 * 232 *
233 * It is instead recommended that elements be constructed via [Element.tag] 233 * It is instead recommended that elements be constructed via [Element.tag]
234 * and text be added via [text]. 234 * and text be added via [text].
235 * 235 *
236 * var element = new Element.html('<div class="foo">content</div>'); 236 * var element = new Element.html('<div class="foo">content</div>');
237 */ 237 */
238 factory $CLASSNAME.html(String html) => 238 factory Element.html(String html,
239 _$(CLASSNAME)FactoryProvider.createElement_html(html); 239 {NodeValidator validator, NodeTreeSanitizer treeSanitizer}) {
240 var fragment = document.body.createFragment(html, validator: validator,
241 treeSanitizer: treeSanitizer);
242 return fragment.nodes.where((e) => e is Element).single;
243 }
240 244
241 /** 245 /**
242 * Creates the HTML element specified by the tag name. 246 * Creates the HTML element specified by the tag name.
243 * 247 *
244 * This is similar to [Document.createElement]. 248 * This is similar to [Document.createElement].
245 * [tag] should be a valid HTML tag name. If [tag] is an unknown tag then 249 * [tag] should be a valid HTML tag name. If [tag] is an unknown tag then
246 * this will create an [UnknownElement]. 250 * this will create an [UnknownElement].
247 * 251 *
248 * var divElement = new Element.tag('div'); 252 * var divElement = new Element.tag('div');
249 * print(divElement is DivElement); // 'true' 253 * print(divElement is DivElement); // 'true'
(...skipping 667 matching lines...) Expand 10 before | Expand all | Expand 10 after
917 @Experimental 921 @Experimental
918 bool get isTemplate => tagName == 'TEMPLATE' || _isAttributeTemplate; 922 bool get isTemplate => tagName == 'TEMPLATE' || _isAttributeTemplate;
919 923
920 void _ensureTemplate() { 924 void _ensureTemplate() {
921 if (!isTemplate) { 925 if (!isTemplate) {
922 throw new UnsupportedError('$this is not a template.'); 926 throw new UnsupportedError('$this is not a template.');
923 } 927 }
924 TemplateElement.decorate(this); 928 TemplateElement.decorate(this);
925 } 929 }
926 930
931 /**
932 * Create a DocumentFragment from the HTML fragment and ensure that it follows
933 * the sanitization rules specified by the validator or treeSanitizer.
934 *
935 * If the default validation behavior is too restrictive then a new
936 * NodeValidator should be created, either extending or wrapping a default
937 * validator and overriding the validation APIs.
938 *
939 * The treeSanitizer is used to walk the generated node tree and sanitize it.
940 * A custom treeSanitizer can also be provided to perform special validation
941 * rules but since the API is more complex to implement this is discouraged.
942 *
943 * The returned tree is guaranteed to only contain nodes and attributes which
944 * are allowed by the provided validator.
945 *
946 * See also:
947 *
948 * * [NodeValidator]
949 * * [NodeTreeSanitizer]
950 */
951 DocumentFragment createFragment(String html,
952 {NodeValidator validator, NodeTreeSanitizer treeSanitizer}) {
953
954 if (treeSanitizer == null) {
955 if (validator == null) {
956 validator = new NodeValidatorBuilder.common();
957 }
958 treeSanitizer = new NodeTreeSanitizer(validator);
959 }
960
961 return _ElementFactoryProvider._parseHtml(this, html, treeSanitizer);
962 }
963
964 void set innerHtml(String html) {
965 this.setInnerHtml(html);
966 }
967
968 void setInnerHtml(String html,
969 {NodeValidator validator, NodeTreeSanitizer treeSanitizer}) {
970 text = null;
971 append(createFragment(
972 html, validator: validator, treeSanitizer: treeSanitizer));
973 }
974
975 String get innerHtml => deprecatedInnerHtml;
976
927 $!MEMBERS 977 $!MEMBERS
928 } 978 }
929 979
930 980
931 final _START_TAG_REGEXP = new RegExp('<(\\w+)');
932 class _ElementFactoryProvider { 981 class _ElementFactoryProvider {
933 static const _CUSTOM_PARENT_TAG_MAP = const {
934 'body' : 'html',
935 'head' : 'html',
936 'caption' : 'table',
937 'td': 'tr',
938 'th': 'tr',
939 'colgroup': 'table',
940 'col' : 'colgroup',
941 'tr' : 'tbody',
942 'tbody' : 'table',
943 'tfoot' : 'table',
944 'thead' : 'table',
945 'track' : 'audio',
946 };
947 982
948 @DomName('Document.createElement') 983 static DocumentFragment _parseHtml(Element context, String html,
949 static Element createElement_html(String html) { 984 NodeTreeSanitizer treeSanitizer) {
950 // TODO(jacobr): this method can be made more robust and performant. 985
951 // 1) Cache the dummy parent elements required to use innerHTML rather than 986 var doc = document.implementation.createHtmlDocument('');
952 // creating them every call. 987 var contextElement;
953 // 2) Verify that the html does not contain leading or trailing text nodes. 988 if (context == null || context is BodyElement) {
954 // 3) Verify that the html does not contain both <head> and <body> tags. 989 contextElement = doc.body;
955 // 4) Detatch the created element from its dummy parent. 990 } else {
956 String parentTag = 'div'; 991 contextElement = doc.$dom_createElement(context.tagName);
957 String tag; 992 doc.body.append(contextElement);
958 final match = _START_TAG_REGEXP.firstMatch(html);
959 if (match != null) {
960 tag = match.group(1).toLowerCase();
961 if (Device.isIE && Element._TABLE_TAGS.containsKey(tag)) {
962 return _createTableForIE(html, tag);
963 }
964 parentTag = _CUSTOM_PARENT_TAG_MAP[tag];
965 if (parentTag == null) parentTag = 'div';
966 } 993 }
967 994
968 final temp = new Element.tag(parentTag); 995 if (Range.supportsCreateContextualFragment) {
969 temp.innerHtml = html; 996 var range = doc.$dom_createRange();
997 range.selectNodeContents(contextElement);
998 var fragment = range.createContextualFragment(html);
970 999
971 Element element; 1000 treeSanitizer.sanitizeTree(fragment);
972 if (temp.children.length == 1) { 1001 return fragment;
973 element = temp.children[0];
974 } else if (parentTag == 'html' && temp.children.length == 2) {
975 // In html5 the root <html> tag will always have a <body> and a <head>,
976 // even though the inner html only contains one of them.
977 element = temp.children[tag == 'head' ? 0 : 1];
978 } else { 1002 } else {
979 _singleNode(temp.children); 1003 contextElement.deprecatedInnerHtml = html;
1004
1005 treeSanitizer.sanitizeTree(contextElement);
1006
1007 var fragment = new DocumentFragment();
1008 while (contextElement.$dom_firstChild != null) {
1009 fragment.append(contextElement.$dom_firstChild);
1010 }
1011 return fragment;
980 } 1012 }
981 element.remove();
982 return element;
983 }
984
985 /**
986 * IE table elements don't support innerHTML (even in standards mode).
987 * Instead we use a div and inject the table element in the innerHtml string.
988 * This technique works on other browsers too, but it's probably slower,
989 * so we only use it when running on IE.
990 *
991 * See also innerHTML:
992 * <http://msdn.microsoft.com/en-us/library/ie/ms533897(v=vs.85).aspx>
993 * and Building Tables Dynamically:
994 * <http://msdn.microsoft.com/en-us/library/ie/ms532998(v=vs.85).aspx>.
995 */
996 static Element _createTableForIE(String html, String tag) {
997 var div = new Element.tag('div');
998 div.innerHtml = '<table>$html</table>';
999 var table = _singleNode(div.children);
1000 Element element;
1001 switch (tag) {
1002 case 'td':
1003 case 'th':
1004 TableRowElement row = _singleNode(table.rows);
1005 element = _singleNode(row.cells);
1006 break;
1007 case 'tr':
1008 element = _singleNode(table.rows);
1009 break;
1010 case 'tbody':
1011 element = _singleNode(table.tBodies);
1012 break;
1013 case 'thead':
1014 element = table.tHead;
1015 break;
1016 case 'tfoot':
1017 element = table.tFoot;
1018 break;
1019 case 'caption':
1020 element = table.caption;
1021 break;
1022 case 'colgroup':
1023 element = _getColgroup(table);
1024 break;
1025 case 'col':
1026 element = _singleNode(_getColgroup(table).children);
1027 break;
1028 }
1029 element.remove();
1030 return element;
1031 }
1032
1033 static TableColElement _getColgroup(TableElement table) {
1034 // TODO(jmesserly): is there a better way to do this?
1035 return _singleNode(table.children.where((n) => n.tagName == 'COLGROUP')
1036 .toList());
1037 }
1038
1039 static Node _singleNode(List<Node> list) {
1040 if (list.length == 1) return list[0];
1041 throw new ArgumentError('HTML had ${list.length} '
1042 'top level elements but 1 expected');
1043 } 1013 }
1044 1014
1045 @DomName('Document.createElement') 1015 @DomName('Document.createElement')
1046 $if DART2JS 1016 $if DART2JS
1047 // Optimization to improve performance until the dart2js compiler inlines this 1017 // Optimization to improve performance until the dart2js compiler inlines this
1048 // method. 1018 // method.
1049 static dynamic createElement_tag(String tag) => 1019 static dynamic createElement_tag(String tag) =>
1050 // Firefox may return a JS function for some types (Embed, Object). 1020 // Firefox may return a JS function for some types (Embed, Object).
1051 JS('Element|=Object', 'document.createElement(#)', tag); 1021 JS('Element|=Object', 'document.createElement(#)', tag);
1052 $else 1022 $else
(...skipping 11 matching lines...) Expand all
1064 const ScrollAlignment._internal(this._value); 1034 const ScrollAlignment._internal(this._value);
1065 toString() => 'ScrollAlignment.$_value'; 1035 toString() => 'ScrollAlignment.$_value';
1066 1036
1067 /// Attempt to align the element to the top of the scrollable area. 1037 /// Attempt to align the element to the top of the scrollable area.
1068 static const TOP = const ScrollAlignment._internal('TOP'); 1038 static const TOP = const ScrollAlignment._internal('TOP');
1069 /// Attempt to center the element in the scrollable area. 1039 /// Attempt to center the element in the scrollable area.
1070 static const CENTER = const ScrollAlignment._internal('CENTER'); 1040 static const CENTER = const ScrollAlignment._internal('CENTER');
1071 /// Attempt to align the element to the bottom of the scrollable area. 1041 /// Attempt to align the element to the bottom of the scrollable area.
1072 static const BOTTOM = const ScrollAlignment._internal('BOTTOM'); 1042 static const BOTTOM = const ScrollAlignment._internal('BOTTOM');
1073 } 1043 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698