diff --git a/user/src/com/google/gwt/core/server/StackTraceDeobfuscator.java b/user/src/com/google/gwt/core/server/StackTraceDeobfuscator.java index b2ba404fd0..2f412f2bf3 100644 --- a/user/src/com/google/gwt/core/server/StackTraceDeobfuscator.java +++ b/user/src/com/google/gwt/core/server/StackTraceDeobfuscator.java @@ -147,7 +147,7 @@ Map getAll(String strongName, Set symbols) { } private static final Pattern JsniRefPattern = Pattern.compile("@?([^:]+)::([^(]+)(\\((.*)\\))?"); - private static final Pattern fragmentIdPattern = Pattern.compile(".*(\\d+)\\.js"); + private static final Pattern fragmentIdPattern = Pattern.compile("(?:.*\\D)?(\\d+)\\.js"); // Matches ServerSerializationStreamReader: the strong name reaches us straight from the // client (X-GWT-Permutation header) and is concatenated into symbol/source map file names. private static final Pattern strongNamePattern = Pattern.compile("[a-zA-Z0-9_]+"); diff --git a/user/test/com/google/gwt/core/server/StackTraceDeobfuscatorTest.java b/user/test/com/google/gwt/core/server/StackTraceDeobfuscatorTest.java index b82490fc19..c8e221404f 100644 --- a/user/test/com/google/gwt/core/server/StackTraceDeobfuscatorTest.java +++ b/user/test/com/google/gwt/core/server/StackTraceDeobfuscatorTest.java @@ -41,6 +41,18 @@ private static StackTraceElement[] trace() { return new StackTraceElement[] {new StackTraceElement("C", "m", "C.java", 1)}; } + /** + * Builds a frame as the browser reports it for a fragment, whose file name comes from the + * "//# sourceURL=<module>-<fragment>.js" comment written by CrossSiteIframeLinker. + * The column marker makes the frame source map capable, and an unknown method symbol leaves + * the fragment id to be recovered from the file name. + */ + private static StackTraceElement[] traceInFragmentFile(String fileName) { + return new StackTraceElement[] {new StackTraceElement("C", "unknown", fileName + "@1", 1)}; + } + + private static final String STRONG_NAME = "0F2C4A6E8B1D3F5709ABCDEF12345678"; + public void testTraversalStrongNameIsNotUsedToBuildPath() { RecordingDeobfuscator d = new RecordingDeobfuscator(); d.resymbolize(trace(), "../../../../../../etc/passwd"); @@ -53,4 +65,25 @@ public void testValidStrongNameStillLoadsSymbolMap() { d.resymbolize(trace(), "0F2C4A6E8B1D3F5709ABCDEF12345678"); assertEquals("0F2C4A6E8B1D3F5709ABCDEF12345678.symbolMap", d.opened.get(0)); } + + public void testSingleDigitFragmentIdIsReadFromFileName() { + RecordingDeobfuscator d = new RecordingDeobfuscator(); + d.resymbolize(traceInFragmentFile("app-5.js"), STRONG_NAME); + assertTrue("expected fragment 5 to be requested: " + d.opened, + d.opened.contains(STRONG_NAME + "_sourceMap5.json")); + } + + public void testMultiDigitFragmentIdIsReadFromFileName() { + RecordingDeobfuscator d = new RecordingDeobfuscator(); + d.resymbolize(traceInFragmentFile("app-12.js"), STRONG_NAME); + assertTrue("expected fragment 12 to be requested: " + d.opened, + d.opened.contains(STRONG_NAME + "_sourceMap12.json")); + } + + public void testMultiDigitFragmentIdIsReadFromModuleNameEndingInDigit() { + RecordingDeobfuscator d = new RecordingDeobfuscator(); + d.resymbolize(traceInFragmentFile("app2-104.js"), STRONG_NAME); + assertTrue("expected fragment 104 to be requested: " + d.opened, + d.opened.contains(STRONG_NAME + "_sourceMap104.json")); + } }