|
1 /* Any copyright is dedicated to the Public Domain. |
|
2 http://creativecommons.org/publicdomain/zero/1.0/ */ |
|
3 |
|
4 /** |
|
5 * Check setting breakpoints in source mapped sources. |
|
6 */ |
|
7 |
|
8 var gDebuggee; |
|
9 var gClient; |
|
10 var gThreadClient; |
|
11 |
|
12 Components.utils.import('resource:///modules/devtools/SourceMap.jsm'); |
|
13 |
|
14 function run_test() |
|
15 { |
|
16 initTestDebuggerServer(); |
|
17 gDebuggee = addTestGlobal("test-source-map"); |
|
18 gClient = new DebuggerClient(DebuggerServer.connectPipe()); |
|
19 gClient.connect(function() { |
|
20 attachTestTabAndResume(gClient, "test-source-map", function(aResponse, aTabClient, aThreadClient) { |
|
21 gThreadClient = aThreadClient; |
|
22 test_simple_source_map(); |
|
23 }); |
|
24 }); |
|
25 do_test_pending(); |
|
26 } |
|
27 |
|
28 function testBreakpointMapping(aName, aCallback) |
|
29 { |
|
30 // Pause so we can set a breakpoint. |
|
31 gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { |
|
32 do_check_true(!aPacket.error); |
|
33 do_check_eq(aPacket.why.type, "debuggerStatement"); |
|
34 |
|
35 gThreadClient.setBreakpoint({ |
|
36 url: "http://example.com/www/js/" + aName + ".js", |
|
37 // Setting the breakpoint on an empty line so that it is pushed down one |
|
38 // line and we can check the source mapped actualLocation later. |
|
39 line: 3, |
|
40 column: 0 |
|
41 }, function (aResponse) { |
|
42 do_check_true(!aResponse.error); |
|
43 |
|
44 // Actual location should come back source mapped still so that |
|
45 // breakpoints are displayed in the UI correctly, etc. |
|
46 do_check_eq(aResponse.actualLocation.line, 4); |
|
47 do_check_eq(aResponse.actualLocation.url, |
|
48 "http://example.com/www/js/" + aName + ".js"); |
|
49 |
|
50 // The eval will cause us to resume, then we get an unsolicited pause |
|
51 // because of our breakpoint, we resume again to finish the eval, and |
|
52 // finally receive our last pause which has the result of the client |
|
53 // evaluation. |
|
54 gThreadClient.eval(null, aName + "()", function (aResponse) { |
|
55 do_check_true(!aResponse.error, "Shouldn't be an error resuming to eval"); |
|
56 do_check_eq(aResponse.type, "resumed"); |
|
57 |
|
58 gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { |
|
59 do_check_eq(aPacket.why.type, "breakpoint"); |
|
60 // Assert that we paused because of the breakpoint at the correct |
|
61 // location in the code by testing that the value of `ret` is still |
|
62 // undefined. |
|
63 do_check_eq(aPacket.frame.environment.bindings.variables.ret.value.type, |
|
64 "undefined"); |
|
65 |
|
66 gThreadClient.resume(function (aResponse) { |
|
67 do_check_true(!aResponse.error); |
|
68 |
|
69 gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { |
|
70 do_check_eq(aPacket.why.type, "clientEvaluated"); |
|
71 do_check_eq(aPacket.why.frameFinished.return, aName); |
|
72 |
|
73 gThreadClient.resume(function (aResponse) { |
|
74 do_check_true(!aResponse.error); |
|
75 aCallback(); |
|
76 }); |
|
77 }); |
|
78 }); |
|
79 }); |
|
80 }); |
|
81 }); |
|
82 }); |
|
83 |
|
84 gDebuggee.eval("(" + function () { |
|
85 debugger; |
|
86 } + "());"); |
|
87 } |
|
88 |
|
89 function test_simple_source_map() |
|
90 { |
|
91 let expectedSources = new Set([ |
|
92 "http://example.com/www/js/a.js", |
|
93 "http://example.com/www/js/b.js", |
|
94 "http://example.com/www/js/c.js" |
|
95 ]); |
|
96 |
|
97 gClient.addListener("newSource", function _onNewSource(aEvent, aPacket) { |
|
98 expectedSources.delete(aPacket.source.url); |
|
99 if (expectedSources.size > 0) { |
|
100 return; |
|
101 } |
|
102 gClient.removeListener("newSource", _onNewSource); |
|
103 |
|
104 testBreakpointMapping("a", function () { |
|
105 testBreakpointMapping("b", function () { |
|
106 testBreakpointMapping("c", function () { |
|
107 finishClient(gClient); |
|
108 }); |
|
109 }); |
|
110 }); |
|
111 }); |
|
112 |
|
113 let a = new SourceNode(null, null, null, [ |
|
114 new SourceNode(1, 0, "a.js", "function a() {\n"), |
|
115 new SourceNode(2, 0, "a.js", " var ret;\n"), |
|
116 new SourceNode(3, 0, "a.js", " // Empty line\n"), |
|
117 new SourceNode(4, 0, "a.js", " ret = 'a';\n"), |
|
118 new SourceNode(5, 0, "a.js", " return ret;\n"), |
|
119 new SourceNode(6, 0, "a.js", "}\n") |
|
120 ]); |
|
121 let b = new SourceNode(null, null, null, [ |
|
122 new SourceNode(1, 0, "b.js", "function b() {\n"), |
|
123 new SourceNode(2, 0, "b.js", " var ret;\n"), |
|
124 new SourceNode(3, 0, "b.js", " // Empty line\n"), |
|
125 new SourceNode(4, 0, "b.js", " ret = 'b';\n"), |
|
126 new SourceNode(5, 0, "b.js", " return ret;\n"), |
|
127 new SourceNode(6, 0, "b.js", "}\n") |
|
128 ]); |
|
129 let c = new SourceNode(null, null, null, [ |
|
130 new SourceNode(1, 0, "c.js", "function c() {\n"), |
|
131 new SourceNode(2, 0, "c.js", " var ret;\n"), |
|
132 new SourceNode(3, 0, "c.js", " // Empty line\n"), |
|
133 new SourceNode(4, 0, "c.js", " ret = 'c';\n"), |
|
134 new SourceNode(5, 0, "c.js", " return ret;\n"), |
|
135 new SourceNode(6, 0, "c.js", "}\n") |
|
136 ]); |
|
137 |
|
138 let { code, map } = (new SourceNode(null, null, null, [ |
|
139 a, b, c |
|
140 ])).toStringWithSourceMap({ |
|
141 file: "http://example.com/www/js/abc.js", |
|
142 sourceRoot: "http://example.com/www/js/" |
|
143 }); |
|
144 |
|
145 code += "//# sourceMappingURL=data:text/json;base64," + btoa(map.toString()); |
|
146 |
|
147 Components.utils.evalInSandbox(code, gDebuggee, "1.8", |
|
148 "http://example.com/www/js/abc.js", 1); |
|
149 } |