add option to pass in bindings delimiters

BUG= https://github.com/dart-lang/polymer-dart/issues/35
R=sigmund@google.com

Review URL: https://codereview.chromium.org//1014423002
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 59705c9..8c0c07b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+#### 0.10.6
+  * Add `bindingStartDelimiters` option to the `ImportInlinerTransformer`. Any
+    urls which contain any of the supplied delimiters before the first `/` will
+    be left alone since they can't be reasoned about. If you want these urls to
+    be treated as relative to the current path you should add a `./` in front.
+
 #### 0.10.5+3
   * Fix normalization of relative paths inside of deep relative imports,
     https://github.com/dart-lang/polymer-dart/issues/30.
diff --git a/lib/build/import_inliner.dart b/lib/build/import_inliner.dart
index 4336587..0339ca6 100644
--- a/lib/build/import_inliner.dart
+++ b/lib/build/import_inliner.dart
@@ -22,8 +22,10 @@
 /// in html imports.
 class ImportInlinerTransformer extends Transformer {
   final List<String> entryPoints;
+  final List<String> bindingStartDelimiters;
 
-  ImportInlinerTransformer([this.entryPoints]);
+  ImportInlinerTransformer(
+      [this.entryPoints, this.bindingStartDelimiters = const []]);
 
   bool isPrimary(AssetId id) {
     if (entryPoints != null) return entryPoints.contains(id.path);
@@ -35,8 +37,8 @@
 
   apply(Transform transform) {
     var logger = new BuildLogger(transform, convertErrorsToWarnings: true);
-    return new ImportInliner(transform, transform.primaryInput.id, logger)
-        .run();
+    return new ImportInliner(transform, transform.primaryInput.id, logger,
+        bindingStartDelimiters: bindingStartDelimiters).run();
   }
 }
 
@@ -49,8 +51,11 @@
   final AssetId primaryInput;
   // The logger to use.
   final BuildLogger logger;
+  // The start delimiters for template bindings, such as '{{' or '[['.
+  final List<String> bindingStartDelimiters;
 
-  ImportInliner(this.transform, this.primaryInput, this.logger);
+  ImportInliner(this.transform, this.primaryInput, this.logger,
+      {this.bindingStartDelimiters: const []});
 
   Future run() {
     var crawler = new ImportCrawler(transform, primaryInput, logger);
@@ -58,7 +63,8 @@
       var primaryDocument = imports[primaryInput].document;
 
       // Normalize urls in the entry point.
-      var changed = new _UrlNormalizer(primaryInput, primaryInput, logger)
+      var changed = new _UrlNormalizer(
+              primaryInput, primaryInput, logger, bindingStartDelimiters)
           .visit(primaryDocument);
 
       // Inline things if needed, always have at least one (the entry point).
@@ -104,7 +110,8 @@
           .querySelectorAll('script[type="$dartType"]')
           .forEach((script) => script.remove());
       // Normalize urls in attributes and inline css.
-      new _UrlNormalizer(data.fromId, asset, logger).visit(document);
+      new _UrlNormalizer(data.fromId, asset, logger, bindingStartDelimiters)
+          .visit(document);
       // Replace the import with its contents by appending the nodes
       // immediately before the import one at a time, and then removing the
       // import from the document.
@@ -173,9 +180,15 @@
   /// Whether or not the normalizer has changed something in the tree.
   bool changed = false;
 
+  // The start delimiters for template bindings, such as '{{' or '[['. If these
+  // are found before the first `/` in a url, then the url will not be
+  // normalized.
+  final List<String> bindingStartDelimiters;
+
   final BuildLogger logger;
 
-  _UrlNormalizer(AssetId primaryInput, this.sourceId, this.logger)
+  _UrlNormalizer(AssetId primaryInput, this.sourceId, this.logger,
+      this.bindingStartDelimiters)
       : primaryInput = primaryInput,
         topLevelPath = '../' * (path.url.split(primaryInput.path).length - 2);
 
@@ -249,6 +262,10 @@
       hrefToParse = '${href.substring(0, firstFolder + 1)}';
     }
 
+    // If we found a binding before the first `/`, then just return the original
+    // href, we can't determine anything about it.
+    if (bindingStartDelimiters.any((d) => hrefToParse.contains(d))) return href;
+
     Uri uri;
     // Various template systems introduce invalid characters to uris which would
     // be typically replaced at runtime. Parse errors are assumed to be caused
diff --git a/pubspec.yaml b/pubspec.yaml
index 1274e1d..ce70bc0 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: web_components
-version: 0.10.5+3
+version: 0.10.6
 author: Polymer.dart Authors <web-ui-dev@dartlang.org>
 homepage: https://www.dartlang.org/polymer-dart/
 description: >
diff --git a/test/build/import_inliner_test.dart b/test/build/import_inliner_test.dart
index 7e060a2..f0dbef6 100644
--- a/test/build/import_inliner_test.dart
+++ b/test/build/import_inliner_test.dart
@@ -9,7 +9,7 @@
 import 'package:unittest/compact_vm_config.dart';
 import 'package:unittest/unittest.dart';
 
-var transformer = new ImportInlinerTransformer();
+var transformer = new ImportInlinerTransformer(null, ['{{', '[[']);
 var phases = [[transformer]];
 
 main() {
@@ -710,6 +710,24 @@
         </body></html>''',
   }, [], StringFormatter.noNewlinesOrSurroundingWhitespace);
 
+  testPhases('paths starting with a binding are treated as absolute', phases, {
+    'a|web/test.html': '''
+        <!DOCTYPE html><html><head>
+        <link rel="import" href="packages/a/foo.html">
+        </head></html>''',
+    'a|lib/foo.html': '''
+        <img _src="{{bar}}">
+        <img _src="[[bar]]">''',
+  }, {
+    'a|web/test.html': '''
+        <!DOCTYPE html><html><head></head><body>
+        <div hidden="">
+          <img _src="{{bar}}">
+          <img _src="[[bar]]">
+        </div>
+        </body></html>''',
+  }, [], StringFormatter.noNewlinesOrSurroundingWhitespace);
+
   testPhases('arbitrary bindings can exist in paths', phases, {
     'a|web/test.html': '''
         <!DOCTYPE html><html><head></head><body>