Skip to content

Commit

Permalink
fix data url with charsets in source map resolver
Browse files Browse the repository at this point in the history
Closes #2856

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=190115235
  • Loading branch information
tadeegan authored and brad4d committed Mar 23, 2018
1 parent 29763b4 commit 80ee3a4
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 7 deletions.
43 changes: 36 additions & 7 deletions src/com/google/javascript/jscomp/SourceMapResolver.java
Expand Up @@ -15,19 +15,28 @@
*/ */
package com.google.javascript.jscomp; package com.google.javascript.jscomp;


import static java.nio.charset.StandardCharsets.UTF_8;

import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.GwtIncompatible;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.BaseEncoding; import com.google.common.io.BaseEncoding;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystems; import java.nio.file.FileSystems;
import javax.annotation.Nullable; import javax.annotation.Nullable;


/** Utility class for resolving source maps and files referenced in source maps. */ /** Utility class for resolving source maps and files referenced in source maps. */
@GwtIncompatible("Accesses the file system") @GwtIncompatible("Accesses the file system")
public class SourceMapResolver { public class SourceMapResolver {
private static final String BASE64_URL_PREFIX = "data:application/json;base64,"; private static final String BASE64_URL_PREFIX = "data:";
private static final String BASE64_START = "base64,";

// For now only accept UTF-8. We could use the actual charset information in the future.
private static final ImmutableSet<String> ACCEPTED_MEDIA_TYPES = ImmutableSet.of(
"application/json;charset=utf-8;",
// default is UTF-8 if unspecified.
"application/json;");


/** /**
* For a given //# sourceMappingUrl, this locates the appropriate sourcemap on disk. This is use * For a given //# sourceMappingUrl, this locates the appropriate sourcemap on disk. This is use
Expand All @@ -38,10 +47,11 @@ public class SourceMapResolver {
static SourceFile extractSourceMap( static SourceFile extractSourceMap(
SourceFile jsFile, String sourceMapURL, boolean parseInlineSourceMaps) { SourceFile jsFile, String sourceMapURL, boolean parseInlineSourceMaps) {
if (parseInlineSourceMaps && sourceMapURL.startsWith(BASE64_URL_PREFIX)) { if (parseInlineSourceMaps && sourceMapURL.startsWith(BASE64_URL_PREFIX)) {
byte[] data = String extractedString = extractBase64String(sourceMapURL);
BaseEncoding.base64().decode(sourceMapURL.substring(BASE64_URL_PREFIX.length())); if (extractedString != null) {
String source = new String(data, StandardCharsets.UTF_8); return SourceFile.fromCode(jsFile.getName() + ".inline.map", extractedString);
return SourceFile.fromCode(jsFile.getName() + ".inline.map", source); }
return null;
} }
// TODO(tdeegan): Handle absolute urls here. The compiler needs to come up with a scheme for // TODO(tdeegan): Handle absolute urls here. The compiler needs to come up with a scheme for
// properly resolving absolute urls from http:// or the root /some/abs/path/... See b/62544959. // properly resolving absolute urls from http:// or the root /some/abs/path/... See b/62544959.
Expand All @@ -53,6 +63,25 @@ static SourceFile extractSourceMap(
return getRelativePath(jsFile.getName(), sourceMapURL); return getRelativePath(jsFile.getName(), sourceMapURL);
} }


/**
* Based on https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs
* @param url
* @return String or null.
*/
@Nullable
private static String extractBase64String(String url) {
if (url.startsWith(BASE64_URL_PREFIX) && url.contains(BASE64_START)) {
int base64StartIndex = url.indexOf(BASE64_START);
String mediaType = url.substring(BASE64_URL_PREFIX.length(), base64StartIndex);
if (ACCEPTED_MEDIA_TYPES.contains(mediaType)) {
byte[] data = BaseEncoding.base64().decode(
url.substring(base64StartIndex + BASE64_START.length()));
return new String(data, UTF_8);
}
}
return null;
}

@VisibleForTesting @VisibleForTesting
private static boolean isAbsolute(String url) { private static boolean isAbsolute(String url) {
try { try {
Expand All @@ -71,6 +100,6 @@ private static boolean isAbsolute(String url) {
static SourceFile getRelativePath(String baseFilePath, String relativePath) { static SourceFile getRelativePath(String baseFilePath, String relativePath) {
return SourceFile.fromPath( return SourceFile.fromPath(
FileSystems.getDefault().getPath(baseFilePath).resolveSibling(relativePath).normalize(), FileSystems.getDefault().getPath(baseFilePath).resolveSibling(relativePath).normalize(),
StandardCharsets.UTF_8); UTF_8);
} }
} }
20 changes: 20 additions & 0 deletions test/com/google/javascript/jscomp/SourceMapResolverTest.java
Expand Up @@ -41,6 +41,26 @@ public void testResolveBase64Inline() throws Exception {
assertNull(noInline); assertNull(noInline);
} }


public void testResolveBase64WithCharsetInline() throws Exception {
String sourceMap = "{map: 'asdfasdf'}";
String encoded = BaseEncoding.base64().encode(sourceMap.getBytes("UTF-8"));
String url = "data:application/json;charset=utf-8;base64," + encoded;
String code = "console.log('asdf')\n//# sourceMappingURL=" + url;
SourceFile s =
SourceMapResolver.extractSourceMap(
SourceFile.fromCode("somePath/hello.js", code), url, true);
assertEquals(sourceMap, s.getCode());
assertEquals("somePath/hello.js.inline.map", s.getName());

// Try non supported charset.
String dataURLWithBadCharset = "data:application/json;charset=asdf;base64," + encoded;
String charsetCode = "console.log('asdf')\n//# sourceMappingURL=" + dataURLWithBadCharset;
SourceFile result =
SourceMapResolver.extractSourceMap(
SourceFile.fromCode("somePath/hello.js", charsetCode), dataURLWithBadCharset, true);
assertNull(result);
}

public void testAbsolute() { public void testAbsolute() {
SourceFile jsFile = SourceFile.fromCode("somePath/hello.js", "console.log(1)"); SourceFile jsFile = SourceFile.fromCode("somePath/hello.js", "console.log(1)");
// We cannot reslove absolute urls. // We cannot reslove absolute urls.
Expand Down

0 comments on commit 80ee3a4

Please sign in to comment.