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

Side by Side Diff: pkg/polymer/lib/src/build/import_inliner.dart

Issue 433913002: Strip bindings before calling Uri.parse (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: add support for [[ bindings and unify bindings regexes Created 6 years, 4 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
« no previous file with comments | « no previous file | pkg/polymer/test/build/import_inliner_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, 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 /// Transfomer that inlines polymer-element definitions from html imports. 5 /// Transfomer that inlines polymer-element definitions from html imports.
6 library polymer.src.build.import_inliner; 6 library polymer.src.build.import_inliner;
7 7
8 import 'dart:async'; 8 import 'dart:async';
9 import 'dart:convert'; 9 import 'dart:convert';
10 10
(...skipping 244 matching lines...) Expand 10 before | Expand all | Expand 10 after
255 /// 255 ///
256 /// myPkgName|lib/foo/bar.html -> myPkgName.foo.bar_html 256 /// myPkgName|lib/foo/bar.html -> myPkgName.foo.bar_html
257 /// myPkgName|web/foo/bar.html -> myPkgName.web.foo.bar_html 257 /// myPkgName|web/foo/bar.html -> myPkgName.web.foo.bar_html
258 /// 258 ///
259 /// This should roughly match the recommended library name conventions. 259 /// This should roughly match the recommended library name conventions.
260 String _libraryNameFor(AssetId id, int suffix) { 260 String _libraryNameFor(AssetId id, int suffix) {
261 var name = '${path.withoutExtension(id.path)}_' 261 var name = '${path.withoutExtension(id.path)}_'
262 '${path.extension(id.path).substring(1)}'; 262 '${path.extension(id.path).substring(1)}';
263 if (name.startsWith('lib/')) name = name.substring(4); 263 if (name.startsWith('lib/')) name = name.substring(4);
264 name = name.split('/').map((part) { 264 name = name.split('/').map((part) {
265 part = part.replaceAll(INVALID_LIB_CHARS_REGEX, '_'); 265 part = part.replaceAll(_INVALID_LIB_CHARS_REGEX, '_');
266 if (part.startsWith(NUM_REGEX)) part = '_${part}'; 266 if (part.startsWith(_NUM_REGEX)) part = '_${part}';
267 return part; 267 return part;
268 }).join("."); 268 }).join(".");
269 return '${id.package}.${name}_$suffix'; 269 return '${id.package}.${name}_$suffix';
270 } 270 }
271 271
272 /// Parse [code] and determine whether it has a library directive. 272 /// Parse [code] and determine whether it has a library directive.
273 bool _hasLibraryDirective(String code) => 273 bool _hasLibraryDirective(String code) =>
274 parseDirectives(code, suppressErrors: true) 274 parseDirectives(code, suppressErrors: true)
275 .directives.any((d) => d is LibraryDirective); 275 .directives.any((d) => d is LibraryDirective);
276 276
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
330 return changed; 330 return changed;
331 } 331 }
332 332
333 visitElement(Element node) { 333 visitElement(Element node) {
334 // TODO(jakemac): Support custom elements that extend html elements which 334 // TODO(jakemac): Support custom elements that extend html elements which
335 // have url-like attributes. This probably means keeping a list of which 335 // have url-like attributes. This probably means keeping a list of which
336 // html elements support each url-like attribute. 336 // html elements support each url-like attribute.
337 if (!isCustomTagName(node.localName)) { 337 if (!isCustomTagName(node.localName)) {
338 node.attributes.forEach((name, value) { 338 node.attributes.forEach((name, value) {
339 if (_urlAttributes.contains(name)) { 339 if (_urlAttributes.contains(name)) {
340 if (value != '' && !value.trim().startsWith(_BINDINGS)) { 340 if (value != '' && !value.trim().startsWith(_BINDINGS_REGEX)) {
341 node.attributes[name] = _newUrl(value, node.sourceSpan); 341 node.attributes[name] = _newUrl(value, node.sourceSpan);
342 changed = changed || value != node.attributes[name]; 342 changed = changed || value != node.attributes[name];
343 } 343 }
344 } 344 }
345 }); 345 });
346 } 346 }
347 if (node.localName == 'style') { 347 if (node.localName == 'style') {
348 node.text = visitCss(node.text); 348 node.text = visitCss(node.text);
349 changed = true; 349 changed = true;
350 } else if (node.localName == 'script' && 350 } else if (node.localName == 'script' &&
351 node.attributes['type'] == TYPE_DART && 351 node.attributes['type'] == TYPE_DART &&
352 !node.attributes.containsKey('src')) { 352 !node.attributes.containsKey('src')) {
353 // TODO(jmesserly): we might need to visit JS too to handle ES Harmony 353 // TODO(jmesserly): we might need to visit JS too to handle ES Harmony
354 // modules. 354 // modules.
355 node.text = visitInlineDart(node.text); 355 node.text = visitInlineDart(node.text);
356 changed = true; 356 changed = true;
357 } 357 }
358 return super.visitElement(node); 358 return super.visitElement(node);
359 } 359 }
360 360
361 static final _URL = new RegExp(r'url\(([^)]*)\)', multiLine: true); 361 static final _URL = new RegExp(r'url\(([^)]*)\)', multiLine: true);
362 static final _QUOTE = new RegExp('["\']', multiLine: true); 362 static final _QUOTE = new RegExp('["\']', multiLine: true);
363 static final _BINDINGS = new RegExp(r'({{)|(\[\[)');
364 363
365 /// Visit the CSS text and replace any relative URLs so we can inline it. 364 /// Visit the CSS text and replace any relative URLs so we can inline it.
366 // Ported from: 365 // Ported from:
367 // https://github.com/Polymer/vulcanize/blob/c14f63696797cda18dc3d372b78aa3378 acc691f/lib/vulcan.js#L149 366 // https://github.com/Polymer/vulcanize/blob/c14f63696797cda18dc3d372b78aa3378 acc691f/lib/vulcan.js#L149
368 // TODO(jmesserly): use csslib here instead? Parsing with RegEx is sadness. 367 // TODO(jmesserly): use csslib here instead? Parsing with RegEx is sadness.
369 // Maybe it's reliable enough for finding URLs in CSS? I'm not sure. 368 // Maybe it's reliable enough for finding URLs in CSS? I'm not sure.
370 String visitCss(String cssText) { 369 String visitCss(String cssText) {
371 var url = spanUrlFor(sourceId, transform); 370 var url = spanUrlFor(sourceId, transform);
372 var src = new SourceFile.text(url, cssText); 371 var src = new SourceFile.text(url, cssText);
373 return cssText.replaceAllMapped(_URL, (match) { 372 return cssText.replaceAllMapped(_URL, (match) {
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
410 } 409 }
411 410
412 if (!output.hasEdits) return code; 411 if (!output.hasEdits) return code;
413 412
414 // TODO(sigmund): emit source maps when barback supports it (see 413 // TODO(sigmund): emit source maps when barback supports it (see
415 // dartbug.com/12340) 414 // dartbug.com/12340)
416 return (output.commit()..build(file.url)).text; 415 return (output.commit()..build(file.url)).text;
417 } 416 }
418 417
419 String _newUrl(String href, Span span) { 418 String _newUrl(String href, Span span) {
420 // Uri.parse blows up on invalid characters (like {{). Encoding the uri 419 // Take all bindings out of the path and replace with [_BINDING_PLACEHOLDER]
421 // allows it to be parsed, which does the correct thing in the general case. 420 // so [Uri.parse] will not fail.
422 // This uri not used to build the new uri, so it never needs to be decoded. 421 var bindingMatches = _BINDING_REGEX.allMatches(href).map((m) => m.group(0));
423 var uri = Uri.parse(Uri.encodeFull(href)); 422 href = href.replaceAll(_BINDING_REGEX, _BINDING_PLACEHOLDER);
Siggi Cherem (dart-lang) 2014/07/31 21:54:22 I wonder if we need to replace all, or if we shoul
jakemac 2014/08/01 15:20:47 This seems like a good idea, but I tried it out an
Siggi Cherem (dart-lang) 2014/08/01 16:02:51 Interesting, here are a couple other ideas... it m
jakemac 2014/08/01 22:12:35 Done.
423
424 var uri = Uri.parse(href);
424 if (uri.isAbsolute) return href; 425 if (uri.isAbsolute) return href;
Siggi Cherem (dart-lang) 2014/07/31 21:54:22 shouldn't this return the original href without th
jakemac 2014/08/01 15:20:47 Good catch, I have updated it so I don't write ove
425 if (!uri.scheme.isEmpty) return href; 426 if (!uri.scheme.isEmpty) return href;
426 if (!uri.host.isEmpty) return href; 427 if (!uri.host.isEmpty) return href;
427 if (uri.path.isEmpty) return href; // Implies standalone ? or # in URI. 428 if (uri.path.isEmpty) return href; // Implies standalone ? or # in URI.
428 if (path.isAbsolute(href)) return href; 429 if (path.isAbsolute(href)) return href;
429 430
430 var id = uriToAssetId(sourceId, href, transform.logger, span); 431 var id = uriToAssetId(sourceId, href, transform.logger, span);
431 if (id == null) return href; 432 if (id == null) return href;
432 var primaryId = transform.primaryInput.id; 433 var primaryId = transform.primaryInput.id;
433 434
434 if (id.path.startsWith('lib/')) { 435 // Put the original bindings back into the path now that we have parsed it.
435 return '${topLevelPath}packages/${id.package}/${id.path.substring(4)}'; 436 var newPath = id.path;
437 for (var bindingMatch in bindingMatches) {
438 newPath = newPath.replaceFirst(_BINDING_PLACEHOLDER, bindingMatch);
436 } 439 }
437 440
438 if (id.path.startsWith('asset/')) { 441 if (newPath.startsWith('lib/')) {
439 return '${topLevelPath}assets/${id.package}/${id.path.substring(6)}'; 442 return '${topLevelPath}packages/${id.package}/${newPath.substring(4)}';
443 }
444
445 if (newPath.startsWith('asset/')) {
446 return '${topLevelPath}assets/${id.package}/${newPath.substring(6)}';
440 } 447 }
441 448
442 if (primaryId.package != id.package) { 449 if (primaryId.package != id.package) {
443 // Techincally we shouldn't get there 450 // Techincally we shouldn't get there
444 transform.logger.error("don't know how to include $id from $primaryId", 451 transform.logger.error("don't know how to include $id from $primaryId",
445 span: span); 452 span: span);
446 return href; 453 return href;
447 } 454 }
448 455
449 var builder = path.url; 456 var builder = path.url;
450 return builder.relative(builder.join('/', id.path), 457 return builder.relative(builder.join('/', newPath),
451 from: builder.join('/', builder.dirname(primaryId.path))); 458 from: builder.join('/', builder.dirname(primaryId.path)));
452 } 459 }
453 } 460 }
454 461
455 /// HTML attributes that expect a URL value. 462 /// HTML attributes that expect a URL value.
456 /// <http://dev.w3.org/html5/spec/section-index.html#attributes-1> 463 /// <http://dev.w3.org/html5/spec/section-index.html#attributes-1>
457 /// 464 ///
458 /// Every one of these attributes is a URL in every context where it is used in 465 /// Every one of these attributes is a URL in every context where it is used in
459 /// the DOM. The comments show every DOM element where an attribute can be used. 466 /// the DOM. The comments show every DOM element where an attribute can be used.
460 const _urlAttributes = const [ 467 const _urlAttributes = const [
461 'action', // in form 468 'action', // in form
462 'background', // in body 469 'background', // in body
463 'cite', // in blockquote, del, ins, q 470 'cite', // in blockquote, del, ins, q
464 'data', // in object 471 'data', // in object
465 'formaction', // in button, input 472 'formaction', // in button, input
466 'href', // in a, area, link, base, command 473 'href', // in a, area, link, base, command
467 'icon', // in command 474 'icon', // in command
468 'manifest', // in html 475 'manifest', // in html
469 'poster', // in video 476 'poster', // in video
470 'src', // in audio, embed, iframe, img, input, script, source, track, 477 'src', // in audio, embed, iframe, img, input, script, source, track,
471 // video 478 // video
472 ]; 479 ];
473 480
474 /// When inlining <link rel="stylesheet"> tags copy over all attributes to the 481 /// When inlining <link rel="stylesheet"> tags copy over all attributes to the
475 /// style tag except these ones. 482 /// style tag except these ones.
476 const IGNORED_LINKED_STYLE_ATTRS = 483 const IGNORED_LINKED_STYLE_ATTRS =
477 const ['charset', 'href', 'href-lang', 'rel', 'rev']; 484 const ['charset', 'href', 'href-lang', 'rel', 'rev'];
478 485
479 /// Global RegExp objects for validating generated library names. 486 /// Global RegExp objects.
480 final INVALID_LIB_CHARS_REGEX = new RegExp('[^a-z0-9_]'); 487 final _INVALID_LIB_CHARS_REGEX = new RegExp('[^a-z0-9_]');
481 final NUM_REGEX = new RegExp('[0-9]'); 488 final _NUM_REGEX = new RegExp('[0-9]');
489 final _BINDING_REGEX = new RegExp(r'(({{.*}})|(\[\[.*\]\]))');
490
491 /// Placeholder for bindings in urls for [Uri.parse].
492 final _BINDING_PLACEHOLDER = '%BINDING%';
482 493
483 _getSpan(SourceFile file, AstNode node) => file.span(node.offset, node.end); 494 _getSpan(SourceFile file, AstNode node) => file.span(node.offset, node.end);
OLDNEW
« no previous file with comments | « no previous file | pkg/polymer/test/build/import_inliner_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698