Add browser-compatible version.

Still need to find a way to test it.

R=floitsch@google.com

Review URL: https://codereview.chromium.org//1612003002 .
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dd213b1..674b8df 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Changelog
 
+## 1.1.0
+
+- Added browser-compatible version as `browser_resource.dart` library.
+  Only needed because configurable imports are not available yet.
+
 ## 1.0.0
 
 - Initial version
diff --git a/README.md b/README.md
index b5f5d74..1fc9691 100644
--- a/README.md
+++ b/README.md
@@ -4,8 +4,10 @@
 
 ## Features and bugs
 
-The current version depends on the `dart:io` library, and doesn't work
-in the browser. A browser alternative will be added soon.
+The current version of `resource.dart` depends on the `dart:io` library,
+and doesn't work in the browser.
+Use `package:resource/browser_resource.dart` in the browser until
+further notice.
 
 Please file feature requests and bugs at the [issue tracker][tracker].
 
diff --git a/lib/browser_resource.dart b/lib/browser_resource.dart
new file mode 100644
index 0000000..d66eeb3
--- /dev/null
+++ b/lib/browser_resource.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// A [Resource] is data that can be read into a Dart program.
+///
+/// A resource is identified by a URI. It can be loaded as bytes or data.
+/// The resource URI may be a `package:` URI.
+///
+/// Example:
+///
+///     var resource = new Resource("package:foo/foo_data.txt");
+///     var string = await resource.readAsString(UTF8);
+///     print(string);
+///
+/// Example:
+///
+///     var resource = new Resource("http://example.com/data.json");
+///     var obj = await resource.openRead()   // Reads as stream of bytes.
+///                             .transform(UTF8.fuse(JSON).decoder)
+///                             .first;
+///
+///
+/// Notice: Currently this library requires `dart:Html` to do the reading,
+/// so it doesn't work outside of a browser.
+/// This library will eventually be mergeded into the `resource.dart` when
+/// features are available to make that possible.
+library resource;
+
+export "src/browser/resource.dart" show Resource;
+export "src/browser/loader.dart" show ResourceLoader;
diff --git a/lib/resource.dart b/lib/resource.dart
index 83c75c6..91c74a0 100644
--- a/lib/resource.dart
+++ b/lib/resource.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
@@ -21,9 +21,10 @@
 ///                             .first;
 ///
 ///
-/// Notice: Currently this package requires `dart:io` to do the reading,
-/// so it doesn't work in the browser.
+/// Notice: Currently this library requires `dart:io` to do the reading,
+/// so it doesn't work in the browser. Use `browser_resource.dart` in the
+/// browser.
 library resource;
 
-export "src/resource.dart" show Resource;
-export "src/loader.dart" show ResourceLoader;
+export "src/io/resource.dart" show Resource;
+export "src/io/loader.dart" show ResourceLoader;
diff --git a/lib/src/browser/html_io.dart b/lib/src/browser/html_io.dart
new file mode 100644
index 0000000..2896967
--- /dev/null
+++ b/lib/src/browser/html_io.dart
@@ -0,0 +1,60 @@
+// Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "dart:async" show Future, Stream;
+import "dart:convert" show Encoding, LATIN1, UTF8;
+import "dart:html";
+import "dart:typed_data" show Uint8List, ByteBuffer;
+
+/// Reads the bytes of a URI as a stream of bytes.
+Stream<List<int>> readAsStream(Uri uri) async* {
+  // TODO(lrn): Should file be run through XmlHTTPRequest too?
+  if (uri.scheme == "http" || uri.scheme == "https") {
+    // TODO: Stream in chunks if DOM has a way to do so.
+    List<int> response = await _httpGetBytes(uri);
+    yield response;
+    return;
+  }
+  if (uri.scheme == "data") {
+    yield uri.data.contentAsBytes();
+    return;
+  }
+  throw new UnsupportedError("Unsupported scheme: $uri");
+}
+
+/// Reads the bytes of a URI as a list of bytes.
+Future<List<int>> readAsBytes(Uri uri) async {
+  if (uri.scheme == "http" || uri.scheme == "https") {
+    return _httpGetBytes(uri);
+  }
+  if (uri.scheme == "data") {
+    return uri.data.contentAsBytes();
+  }
+  throw new UnsupportedError("Unsupported scheme: $uri");
+}
+
+/// Reads the bytes of a URI as a string.
+Future<String> readAsString(Uri uri, Encoding encoding) async {
+  if (uri.scheme == "http" || uri.scheme == "https") {
+    // Fetch as string if the encoding is expected to be understood,
+    // otherwise fetch as bytes and do decoding using the encoding.
+    if (encoding != null) {
+      return encoding.decode(await _httpGetBytes(uri));
+    }
+    return HttpRequest.getString(uri.toString());
+  }
+  if (uri.scheme == "data") {
+    return uri.data.contentAsString(encoding: encoding);
+  }
+  throw new UnsupportedError("Unsupported scheme: $uri");
+}
+
+Future<List<int>> _httpGetBytes(Uri uri) {
+  return HttpRequest
+      .request(uri.toString(), responseType: "arraybuffer")
+      .then((request) {
+    ByteBuffer data = request.response;
+    return data.asUint8List();
+  });
+}
diff --git a/lib/src/browser/loader.dart b/lib/src/browser/loader.dart
new file mode 100644
index 0000000..0ecaf67
--- /dev/null
+++ b/lib/src/browser/loader.dart
@@ -0,0 +1,55 @@
+// Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "dart:async" show Future, Stream;
+import "dart:convert" show Encoding;
+
+import "../resource_loader.dart" as base;
+import "../package_loader.dart"as base;
+import "html_io.dart" as io;
+
+/// Resource loading strategy.
+///
+/// An abstraction of the functionality needed to load resources.
+///
+/// Implementations of this interface decide which URI schemes they support.
+abstract class ResourceLoader implements base.ResourceLoader {
+  /// A resource loader that can load as many of the following URI
+  /// schemes as are supported by the platform:
+  /// * file
+  /// * http
+  /// * https
+  /// * data
+  /// * package
+  ///
+  /// (For example, file: URIs are not supported in the browser).
+  /// Relative URI references are accepted - they are resolved against
+  /// [Uri.base] before being loaded.
+  static ResourceLoader get defaultLoader =>
+      const PackageLoader(const DefaultLoader());
+}
+
+/// Default implementation of [ResourceLoader]..
+///
+/// Uses the system's available loading functionality to implement the
+/// loading functions.
+///
+/// Supports as many of `http:`, `https:`, `file:` and `data:` URIs as
+/// possible.
+class DefaultLoader implements ResourceLoader {
+  const DefaultLoader();
+
+  Stream<List<int>> openRead(Uri uri) => io.readAsStream(uri);
+
+  Future<List<int>> readAsBytes(Uri uri) => io.readAsBytes(uri);
+
+  Future<String> readAsString(Uri uri, {Encoding encoding}) =>
+      io.readAsString(uri, encoding);
+}
+
+// A loader that implements base.PackageLoader *and* ResourceLoader from this
+// file.
+class PackageLoader extends base.PackageLoader implements ResourceLoader {
+  const PackageLoader(ResourceLoader loader) : super(loader);
+}
diff --git a/lib/src/browser/resource.dart b/lib/src/browser/resource.dart
new file mode 100644
index 0000000..9368e4e
--- /dev/null
+++ b/lib/src/browser/resource.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "dart:core" hide Resource;
+
+import "loader.dart" show ResourceLoader, DefaultLoader;
+
+// TODO(lrn): Merge with implementation when configured imports removes
+// the need to share code.
+import "../resource.dart" as base show Resource;
+
+class Resource extends base.Resource {
+  /// Creates a resource object with the given [uri] as location.
+  ///
+  /// The [uri] must be either a [Uri] or a string containing a valid URI.
+  /// If the string is not a valid URI, using any of the functions on
+  /// the resource object will fail.
+  ///
+  /// The URI may be relative, in which case it will be resolved
+  /// against [Uri.base] before being used.
+  ///
+  /// The URI may use the `package` scheme, which is always supported.
+  /// Other schemes may also be supported where possible.
+  ///
+  /// If [loader] is provided, it is used to load absolute non-package URIs.
+  /// Package: URIs are resolved to a non-package URI before being loaded, so
+  /// the loader doesn't have to support package: URIs, nor does it need to
+  /// support relative URI references.
+  /// If [loader] is omitted, a default implementation is used which supports
+  /// as many of `http`, `https`, `file` and `data` as are available on the
+  /// current platform.
+  const Resource(uri, {ResourceLoader loader})
+      : super(uri, (loader != null) ? loader : const DefaultLoader());
+}
diff --git a/lib/src/io.dart b/lib/src/io/io.dart
similarity index 89%
rename from lib/src/io.dart
rename to lib/src/io/io.dart
index ff8761b..b24244d 100644
--- a/lib/src/io.dart
+++ b/lib/src/io/io.dart
@@ -1,14 +1,13 @@
-// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// dart:io based strategy for loading resources.
-
 import "dart:async" show Future, Stream;
 import "dart:convert" show Encoding, LATIN1, UTF8;
 import "dart:io" show
     File, HttpClient, HttpClientResponse, HttpClientRequest, HttpHeaders;
 import "dart:typed_data" show Uint8List;
+
 import "package:typed_data/typed_buffers.dart" show Uint8Buffer;
 
 /// Read the bytes of a URI as a stream of bytes.
@@ -18,7 +17,7 @@
     return;
   }
   if (uri.scheme == "http" || uri.scheme == "https") {
-    HttpClientResponse response = await _httpGet(uri);
+    HttpClientResponse response = await _httpGetBytes(uri);
     yield* response;
     return;
   }
@@ -35,7 +34,7 @@
     return new File.fromUri(uri).readAsBytes();
   }
   if (uri.scheme == "http" || uri.scheme == "https") {
-    HttpClientResponse response = await _httpGet(uri);
+    HttpClientResponse response = await _httpGetBytes(uri);
     int length = response.contentLength;
     if (length < 0) length = 0;
     var buffer = new Uint8Buffer(length);
@@ -74,7 +73,7 @@
       await for (var bytes in response) {
         buffer.addAll(bytes);
       }
-      var byteList = new Uint8List.view(buffer.buffer, 0, buffer.length);
+      var byteList = buffer.buffer.asUint8List(0, buffer.length);
       return new String.fromCharCodes(byteList);
     }
     return response.transform(encoding.decoder).join();
@@ -85,7 +84,7 @@
   throw new UnsupportedError("Unsupported scheme: $uri");
 }
 
-Future<HttpClientResponse> _httpGet(Uri uri) async {
+Future<HttpClientResponse> _httpGetBytes(Uri uri) async {
   HttpClientRequest request = await new HttpClient().getUrl(uri);
     request.headers.set(HttpHeaders.ACCEPT, "application/octet-stream, */*");
   return request.close();
diff --git a/lib/src/io/loader.dart b/lib/src/io/loader.dart
new file mode 100644
index 0000000..d52cf44
--- /dev/null
+++ b/lib/src/io/loader.dart
@@ -0,0 +1,55 @@
+// Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "dart:async" show Future, Stream;
+import "dart:convert" show Encoding;
+
+import "../resource_loader.dart" as base;
+import "../package_loader.dart"as base;
+import "io.dart" as io;
+
+/// Resource loading strategy.
+///
+/// An abstraction of the functionality needed to load resources.
+///
+/// Implementations of this interface decide which URI schemes they support.
+abstract class ResourceLoader implements base.ResourceLoader {
+  /// A resource loader that can load as many of the following URI
+  /// schemes as are supported by the platform:
+  /// * file
+  /// * http
+  /// * https
+  /// * data
+  /// * package
+  ///
+  /// (For example, file: URIs are not supported in the browser).
+  /// Relative URI references are accepted - they are resolved against
+  /// [Uri.base] before being loaded.
+  static ResourceLoader get defaultLoader =>
+      const PackageLoader(const DefaultLoader());
+}
+
+/// Default implementation of [ResourceLoader]..
+///
+/// Uses the system's available loading functionality to implement the
+/// loading functions.
+///
+/// Supports as many of `http:`, `https:`, `file:` and `data:` URIs as
+/// possible.
+class DefaultLoader implements ResourceLoader {
+  const DefaultLoader();
+
+  Stream<List<int>> openRead(Uri uri) => io.readAsStream(uri);
+
+  Future<List<int>> readAsBytes(Uri uri) => io.readAsBytes(uri);
+
+  Future<String> readAsString(Uri uri, {Encoding encoding}) =>
+      io.readAsString(uri, encoding);
+}
+
+// A loader that implements base.PackageLoader *and* ResourceLoader from this
+// file.
+class PackageLoader extends base.PackageLoader implements ResourceLoader {
+  const PackageLoader(ResourceLoader loader) : super(loader);
+}
diff --git a/lib/src/io/resource.dart b/lib/src/io/resource.dart
new file mode 100644
index 0000000..4ca3b8f
--- /dev/null
+++ b/lib/src/io/resource.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "dart:core" hide Resource;
+
+import "loader.dart" show ResourceLoader, DefaultLoader;
+
+// TODO(lrn): Merge with implementation when configured imports removes
+// the need to share code.
+import "../resource.dart" as base show Resource;
+
+class Resource extends base.Resource {
+  /// Creates a resource object with the given [uri] as location.
+  ///
+  /// The [uri] must be either a [Uri] or a string containing a valid URI.
+  /// If the string is not a valid URI, using any of the functions on
+  /// the resource object will fail.
+  ///
+  /// The URI may be relative, in which case it will be resolved
+  /// against [Uri.base] before being used.
+  ///
+  /// The URI may use the `package` scheme, which is always supported.
+  /// Other schemes may also be supported where possible.
+  ///
+  /// If [loader] is provided, it is used to load absolute non-package URIs.
+  /// Package: URIs are resolved to a non-package URI before being loaded, so
+  /// the loader doesn't have to support package: URIs, nor does it need to
+  /// support relative URI references.
+  /// If [loader] is omitted, a default implementation is used which supports
+  /// as many of `http`, `https`, `file` and `data` as are available on the
+  /// current platform.
+  const Resource(uri, {ResourceLoader loader})
+      : super(uri, (loader != null) ? loader : const DefaultLoader());
+}
diff --git a/lib/src/loader.dart b/lib/src/loader.dart
deleted file mode 100644
index b6b4e67..0000000
--- a/lib/src/loader.dart
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import "dart:async" show Future, Stream;
-import "dart:convert" show Encoding;
-import "dart:isolate" show Isolate;
-import "io.dart" as io;  // TODO: make this import configuration dependent.
-
-/// Resource loading strategy.
-///
-/// An abstraction of the functionality needed to load resources.
-///
-/// Implementations of this interface decide which URI schemes they support.
-abstract class ResourceLoader {
-  /// A resource loader that can load as many of the following URI
-  /// schemes as are supported by the platform:
-  /// * file
-  /// * http
-  /// * https
-  /// * data
-  /// * package
-  ///
-  /// (For example, file: URIs are not supported in the browser).
-  /// Relative URI references are accepted - they are resolved against
-  /// [Uri.base] before being loaded.
-  static const ResourceLoader defaultLoader = const PackageLoader();
-
-  /// Reads the file located by [uri] as a stream of bytes.
-  Stream<List<int>> openRead(Uri uri);
-
-  /// Reads the file located by [uri] as a list of bytes.
-  Future<List<int>> readAsBytes(Uri uri);
-
-  /// Reads the file located by [uri] as a [String].
-  ///
-  /// The file bytes are decoded using [encoding], if provided.
-  ///
-  /// If [encoding] is omitted, the default for the `file:` scheme is UTF-8.
-  /// For `http`, `https` and `data` URIs, the Content-Type header's charset
-  /// is used, if available and recognized by [Encoding.getByName],
-  /// otherwise it defaults to Latin-1 for `http` and `https`
-  /// and to ASCII for `data` URIs.
-  Future<String> readAsString(Uri uri, { Encoding encoding });
-}
-
-/// Default implementation of [ResourceLoader].
-///
-/// Uses the system's available loading functionality to implement the
-/// loading functions.
-///
-/// Supports `http:`, `https:`, `file:` and `data:` URIs.
-class DefaultLoader implements ResourceLoader {
-  const DefaultLoader();
-
-  Stream<List<int>> openRead(Uri uri) => io.readAsStream(uri);
-
-  Future<List<int>> readAsBytes(Uri uri) => io.readAsBytes(uri);
-
-  Future<String> readAsString(Uri uri, { Encoding encoding }) =>
-      io.readAsString(uri, encoding);
-}
-
-
-/// Implementation of [ResourceLoader] that accepts relative and package: URIs.
-///
-/// Like [DefaultLoader] except that it resolves package URIs and relative
-/// URI references as well.
-///
-/// This class may be useful when you don't want to bother creating a [Resource]
-/// object, and just want to load a resource directly.
-class PackageLoader implements ResourceLoader {
-  const PackageLoader();
-
-  Stream<List<int>> openRead(Uri uri) async* {
-    yield* io.readAsStream(await resolveUri(uri));
-  }
-
-  Future<List<int>> readAsBytes(Uri uri) async =>
-      io.readAsBytes(await resolveUri(uri));
-
-  Future<String> readAsString(Uri uri, { Encoding encoding }) async =>
-      io.readAsString(await resolveUri(uri), encoding);
-}
-
-/// Helper function for resolving to a non-relative, non-package URI.
-Future<Uri> resolveUri(Uri uri) async {
-  if (uri.scheme == "package") {
-    return Isolate.resolvePackageUri(uri);
-  }
-  return Uri.base.resolveUri(uri);
-}
diff --git a/lib/src/package_loader.dart b/lib/src/package_loader.dart
new file mode 100644
index 0000000..b834c79
--- /dev/null
+++ b/lib/src/package_loader.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "dart:async" show Future, Stream;
+import "dart:convert" show Encoding;
+
+import "resource_loader.dart";
+import "resolve.dart";
+
+/// Implementation of [ResourceLoader] that accepts relative and package: URIs.
+///
+/// Acts like the `loader` it is provided with except that it resolves
+/// package URIs and relative URI references before loading them.
+///
+/// This class may be useful when you don't want to bother creating a resource
+/// object, and just want to load a resource directly.
+class PackageLoader implements ResourceLoader {
+  final ResourceLoader _loader;
+  const PackageLoader(ResourceLoader loader) : _loader = loader;
+
+  Stream<List<int>> openRead(Uri uri) async* {
+    yield* _loader.openRead(await resolveUri(uri));
+  }
+
+  Future<List<int>> readAsBytes(Uri uri) async =>
+      _loader.readAsBytes(await resolveUri(uri));
+
+  Future<String> readAsString(Uri uri, { Encoding encoding }) async =>
+      _loader.readAsString(await resolveUri(uri), encoding: encoding);
+}
diff --git a/lib/src/resolve.dart b/lib/src/resolve.dart
new file mode 100644
index 0000000..7a54520
--- /dev/null
+++ b/lib/src/resolve.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "dart:async" show Future;
+import "dart:isolate" show Isolate;
+
+/// Helper function for resolving to a non-relative, non-package URI.
+Future<Uri> resolveUri(Uri uri) async {
+  if (uri.scheme == "package") {
+    return Isolate.resolvePackageUri(uri);
+  }
+  return Uri.base.resolveUri(uri);
+}
diff --git a/lib/src/resource.dart b/lib/src/resource.dart
index 924ec05..2b19d9e 100644
--- a/lib/src/resource.dart
+++ b/lib/src/resource.dart
@@ -1,87 +1,57 @@
-// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import "dart:core" hide Resource;
 import "dart:async" show Future, Stream;
 import "dart:convert" show Encoding;
-import "dart:isolate" show Isolate;
-import "loader.dart";
 
-/// A resource that can be read into the program.
-///
-/// A resource is data that can be located using a URI and read into
-/// the program at runtime.
-/// The URI may use the `package` scheme to read resources provided
-/// along with package sources.
-abstract class Resource {
-  /// Creates a resource object with the given [uri] as location.
-  ///
-  /// The [uri] must be either a [Uri] or a string containing a valid URI.
-  /// If the string is not a valid URI, using any of the functions on
-  /// the resource object will fail.
-  ///
-  /// The URI may be relative, in which case it will be resolved
-  /// against [Uri.base] before being used.
-  ///
-  /// The URI may use the `package` scheme, which is always supported.
-  /// Other schemes may also be supported where possible.
-  ///
-  /// If [loader] is provided, it is used to load absolute non-package URIs.
-  /// Package: URIs are resolved to a non-package URI before being loaded, so
-  /// the loader doesn't have to support package: URIs, nor does it need to
-  /// support relative URI references.
-  /// If [loader] is omitted, a default implementation is used which supports
-  /// as many of `http`, `https`, `file` and `data` as are available on the
-  /// current platform.
-  const factory Resource(uri, {ResourceLoader loader}) = _Resource;
+import "resource_loader.dart";
+import "resolve.dart";
 
-  /// The location URI of this resource.
-  ///
-  /// This is a [Uri] of the `uri` parameter given to the constructor.
-  /// If the parameter was a string that did not contain a valid URI,
-  /// reading `uri` will fail.
-  Uri get uri;
+/// Base resource implementation.
+class Resource {
+  // Default implementation of a generic `Resource` class.
+  //
+  // Actually exposed `Resource` interfaces uses this as implementation,
+  // but expose a different `Resource` class with plaform-dependent statics.
+  // Requires a loader to be provided.
 
-  /// Reads the resource content as a stream of bytes.
-  Stream<List<int>> openRead();
-
-  /// Reads the resource content as a single list of bytes.
-  Future<List<int>> readAsBytes();
-
-  /// Reads the resource content as a string.
-  ///
-  /// The content is decoded into a string using an [Encoding].
-  /// If no other encoding is provided, it defaults to UTF-8.
-  Future<String> readAsString({Encoding encoding});
-}
-
-class _Resource implements Resource {
   /// Loading strategy for the resource.
   final ResourceLoader _loader;
 
   /// The URI of the resource.
   final _uri;
 
-  const _Resource(uri, {ResourceLoader loader})
-      : _uri = uri, _loader = (loader != null) ? loader : const DefaultLoader();
-  // TODO: Make this `loader ?? const DefaultLoader()` when ?? is const.
+  const Resource(uri, ResourceLoader loader)
+      : _uri = uri, _loader = loader;
 
+  /// The location URI of this resource.
+  ///
+  /// This is a [Uri] of the `uri` parameter given to the constructor.
+  /// If the parameter was a string that did not contain a valid URI,
+  /// reading `uri` will fail.
   Uri get uri => (_uri is String) ? Uri.parse(_uri) : (_uri as Uri);
 
+  /// Reads the resource content as a stream of bytes.
   Stream<List<int>> openRead() async* {
-    Uri uri = await _resolvedUri;
+    Uri uri = await resolveUri(this.uri);
     yield* _loader.openRead(uri);
   }
 
+  /// Reads the resource content as a single list of bytes.
   Future<List<int>> readAsBytes() async {
-    Uri uri = await _resolvedUri;
+    Uri uri = await resolveUri(this.uri);
     return _loader.readAsBytes(uri);
   }
 
+  /// Reads the resource content as a string.
+  ///
+  /// The content is decoded into a string using an [Encoding].
+  /// If no encoding is provided, an encoding is chosen depending on the
+  /// protocol and/or available metadata.
   Future<String> readAsString({Encoding encoding}) async {
-    Uri uri = await _resolvedUri;
+    Uri uri = await resolveUri(this.uri);
     return _loader.readAsString(uri, encoding: encoding);
   }
-
-  Future<Uri> get _resolvedUri => resolveUri(uri);
 }
diff --git a/lib/src/resource_loader.dart b/lib/src/resource_loader.dart
new file mode 100644
index 0000000..1a189e1
--- /dev/null
+++ b/lib/src/resource_loader.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "dart:async" show Future, Stream;
+import "dart:convert" show Encoding;
+
+/// Resource loading strategy.
+///
+/// An abstraction of the functionality needed to load resources.
+///
+/// Implementations of this interface decide which URI schemes they support.
+abstract class ResourceLoader {
+  /// Reads the file located by [uri] as a stream of bytes.
+  Stream<List<int>> openRead(Uri uri);
+
+  /// Reads the file located by [uri] as a list of bytes.
+  Future<List<int>> readAsBytes(Uri uri);
+
+  /// Reads the file located by [uri] as a [String].
+  ///
+  /// The file bytes are decoded using [encoding], if provided.
+  ///
+  /// If [encoding] is omitted, the default for the `file:` scheme is UTF-8.
+  /// For `http`, `https` and `data` URIs, the Content-Type header's charset
+  /// is used, if available and recognized by [Encoding.getByName],
+  /// otherwise it defaults to Latin-1 for `http` and `https`
+  /// and to ASCII for `data` URIs.
+  Future<String> readAsString(Uri uri, {Encoding encoding});
+}
+
diff --git a/pubspec.yaml b/pubspec.yaml
index d4734e4..5b3d5f5 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: resource
-version: 1.0.1
+version: 1.1.0
 description: Reading resource data from (package and other) files.
 author: Dart Team <misc@dartlang.org>
 homepage: https://github.com/dart-lang/resource
diff --git a/test/browser_http_test.dart b/test/browser_http_test.dart
new file mode 100644
index 0000000..ecec680
--- /dev/null
+++ b/test/browser_http_test.dart
@@ -0,0 +1,56 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+@TestOn("browser")
+
+import "dart:async";
+import "dart:convert";
+
+import "package:resource/browser_resource.dart";
+import "package:test/test.dart";
+
+const content = "Rødgrød med fløde";
+
+main() {
+  // Assume test files located next to Uri.base.
+  // This is how "pub run test" currently works.
+
+  test("Default encoding", () async {
+    var loader = ResourceLoader.defaultLoader;
+    // The HTTPXmlRequest loader defaults to UTF-8 encoding.
+    var uri = Uri.base.resolve("testfile-utf8.txt");
+    String string = await loader.readAsString(uri);
+    expect(string, content);
+  });
+
+  test("Latin-1 encoding", () async {
+    var loader = ResourceLoader.defaultLoader;
+    var uri = Uri.base.resolve("testfile-latin1.txt");
+    String string = await loader.readAsString(uri, encoding: LATIN1);
+    expect(string, content);
+  });
+
+  test("UTF-8 encoding", () async {
+    var loader = ResourceLoader.defaultLoader;
+    var uri = Uri.base.resolve("testfile-utf8.txt");
+    String string = await loader.readAsString(uri, encoding: UTF8);
+    expect(string, content);
+  });
+
+  test("bytes", () async {
+    var loader = ResourceLoader.defaultLoader;
+    var uri = Uri.base.resolve("testfile-latin1.txt");
+    List<int> bytes = await loader.readAsBytes(uri);
+    expect(bytes, content.codeUnits);
+  });
+
+  test("byte stream", () async {
+    var loader = ResourceLoader.defaultLoader;
+    var uri = Uri.base.resolve("testfile-latin1.txt");
+    Stream<List<int>> bytes = loader.openRead(uri);
+    var buffer = [];
+    await bytes.forEach(buffer.addAll);
+    expect(buffer, content.codeUnits);
+  });
+}
diff --git a/test/loader_data_test.dart b/test/loader_data_test.dart
index 8c84f7d..6a575e1 100644
--- a/test/loader_data_test.dart
+++ b/test/loader_data_test.dart
@@ -2,6 +2,8 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+@TestOn("vm");
+
 import "dart:async";
 import "dart:convert";
 import "dart:io";
diff --git a/test/loader_file_test.dart b/test/loader_file_test.dart
index aa4c238..8264855 100644
--- a/test/loader_file_test.dart
+++ b/test/loader_file_test.dart
@@ -2,6 +2,8 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+@TestOn("vm");
+
 import "dart:async";
 import "dart:convert";
 import "dart:io";
@@ -11,11 +13,11 @@
 
 const content = "Rødgrød med fløde";
 
-
 main() {
   var dir;
+  int dirCounter = 0;
   setUp(() {
-    dir = Directory.systemTemp.createTempSync('testdir');
+    dir = Directory.systemTemp.createTempSync('testdir${dirCounter++}');
   });
   testFile(Encoding encoding) {
     group("${encoding.name}", () {
@@ -25,10 +27,7 @@
         var dirUri = dir.uri;
         uri = dirUri.resolve("file.txt");
         file = new File.fromUri(uri);
-        file.createSync();
-        var sink = file.openWrite(encoding: encoding);
-        sink.write(content);
-        sink.close();
+        file.writeAsBytesSync(encoding.encode(content));
       });
 
       test("read string", () async {
diff --git a/test/loader_http_test.dart b/test/loader_http_test.dart
index c47fa27..5b61f86 100644
--- a/test/loader_http_test.dart
+++ b/test/loader_http_test.dart
@@ -2,6 +2,8 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+@TestOn("vm");
+
 import "dart:async";
 import "dart:convert";
 import "dart:io";
diff --git a/test/testfile-latin1.txt b/test/testfile-latin1.txt
new file mode 100644
index 0000000..64cfd84
--- /dev/null
+++ b/test/testfile-latin1.txt
@@ -0,0 +1 @@
+Rødgrød med fløde
\ No newline at end of file
diff --git a/test/testfile-utf8.txt b/test/testfile-utf8.txt
new file mode 100644
index 0000000..802b46b
--- /dev/null
+++ b/test/testfile-utf8.txt
@@ -0,0 +1 @@
+Rødgrød med fløde
\ No newline at end of file