Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(103)

Side by Side Diff: third_party/WebKit/Source/platform/v8_inspector/DebuggerScript.js

Issue 2295913003: [DevTools] Switch from platform/v8_inspector to v8/v8-inspector.h. (Closed)
Patch Set: rebase Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30 "use strict";
31
32 (function () {
33
34 var DebuggerScript = {};
35
36 /** @enum */
37 DebuggerScript.PauseOnExceptionsState = {
38 DontPauseOnExceptions: 0,
39 PauseOnAllExceptions: 1,
40 PauseOnUncaughtExceptions: 2
41 };
42
43 DebuggerScript._pauseOnExceptionsState = DebuggerScript.PauseOnExceptionsState.D ontPauseOnExceptions;
44 Debug.clearBreakOnException();
45 Debug.clearBreakOnUncaughtException();
46
47 /**
48 * @param {?CompileEvent} eventData
49 */
50 DebuggerScript.getAfterCompileScript = function(eventData)
51 {
52 var script = eventData.script().value();
53 if (!script.is_debugger_script)
54 return DebuggerScript._formatScript(eventData.script().value());
55 return null;
56 }
57
58 /** @type {!Map<!ScopeType, string>} */
59 DebuggerScript._scopeTypeNames = new Map();
60 DebuggerScript._scopeTypeNames.set(ScopeType.Global, "global");
61 DebuggerScript._scopeTypeNames.set(ScopeType.Local, "local");
62 DebuggerScript._scopeTypeNames.set(ScopeType.With, "with");
63 DebuggerScript._scopeTypeNames.set(ScopeType.Closure, "closure");
64 DebuggerScript._scopeTypeNames.set(ScopeType.Catch, "catch");
65 DebuggerScript._scopeTypeNames.set(ScopeType.Block, "block");
66 DebuggerScript._scopeTypeNames.set(ScopeType.Script, "script");
67
68 /**
69 * @param {function()} fun
70 * @return {?Array<!Scope>}
71 */
72 DebuggerScript.getFunctionScopes = function(fun)
73 {
74 var mirror = MakeMirror(fun);
75 if (!mirror.isFunction())
76 return null;
77 var functionMirror = /** @type {!FunctionMirror} */(mirror);
78 var count = functionMirror.scopeCount();
79 if (count == 0)
80 return null;
81 var result = [];
82 for (var i = 0; i < count; i++) {
83 var scopeDetails = functionMirror.scope(i).details();
84 var scopeObject = DebuggerScript._buildScopeObject(scopeDetails.type(), scopeDetails.object());
85 if (!scopeObject)
86 continue;
87 result.push({
88 type: /** @type {string} */(DebuggerScript._scopeTypeNames.get(scope Details.type())),
89 object: scopeObject,
90 name: scopeDetails.name() || ""
91 });
92 }
93 return result;
94 }
95
96 /**
97 * @param {Object} object
98 * @return {?RawLocation}
99 */
100 DebuggerScript.getGeneratorObjectLocation = function(object)
101 {
102 var mirror = MakeMirror(object, true /* transient */);
103 if (!mirror.isGenerator())
104 return null;
105 var generatorMirror = /** @type {!GeneratorMirror} */(mirror);
106 var funcMirror = generatorMirror.func();
107 if (!funcMirror.resolved())
108 return null;
109 var location = generatorMirror.sourceLocation() || funcMirror.sourceLocation ();
110 var script = funcMirror.script();
111 if (script && location) {
112 return {
113 scriptId: "" + script.id(),
114 lineNumber: location.line,
115 columnNumber: location.column
116 };
117 }
118 return null;
119 }
120
121 /**
122 * @param {Object} object
123 * @return {!Array<!{value: *}>|undefined}
124 */
125 DebuggerScript.getCollectionEntries = function(object)
126 {
127 var mirror = MakeMirror(object, true /* transient */);
128 if (mirror.isMap())
129 return /** @type {!MapMirror} */(mirror).entries();
130 if (mirror.isSet() || mirror.isIterator()) {
131 var result = [];
132 var values = mirror.isSet() ? /** @type {!SetMirror} */(mirror).values() : /** @type {!IteratorMirror} */(mirror).preview();
133 for (var i = 0; i < values.length; ++i)
134 result.push({ value: values[i] });
135 return result;
136 }
137 }
138
139 /**
140 * @param {string|undefined} contextData
141 * @return {number}
142 */
143 DebuggerScript._executionContextId = function(contextData)
144 {
145 if (!contextData)
146 return 0;
147 var match = contextData.match(/^[^,]*,([^,]*),.*$/);
148 if (!match)
149 return 0;
150 return parseInt(match[1], 10) || 0;
151 }
152
153 /**
154 * @param {string|undefined} contextData
155 * @return {string}
156 */
157 DebuggerScript._executionContextAuxData = function(contextData)
158 {
159 if (!contextData)
160 return "";
161 var match = contextData.match(/^[^,]*,[^,]*,(.*)$/);
162 return match ? match[1] : "";
163 }
164
165 /**
166 * @param {string} contextGroupId
167 * @return {!Array<!FormattedScript>}
168 */
169 DebuggerScript.getScripts = function(contextGroupId)
170 {
171 var result = [];
172 var scripts = Debug.scripts();
173 var contextDataPrefix = null;
174 if (contextGroupId)
175 contextDataPrefix = contextGroupId + ",";
176 for (var i = 0; i < scripts.length; ++i) {
177 var script = scripts[i];
178 if (contextDataPrefix) {
179 if (!script.context_data)
180 continue;
181 // Context data is a string in the following format:
182 // <contextGroupId>,<contextId>,<auxData>
183 if (script.context_data.indexOf(contextDataPrefix) !== 0)
184 continue;
185 }
186 if (script.is_debugger_script)
187 continue;
188 result.push(DebuggerScript._formatScript(script));
189 }
190 return result;
191 }
192
193 /**
194 * @param {!Script} script
195 * @return {!FormattedScript}
196 */
197 DebuggerScript._formatScript = function(script)
198 {
199 var lineEnds = script.line_ends;
200 var lineCount = lineEnds.length;
201 var endLine = script.line_offset + lineCount - 1;
202 var endColumn;
203 // V8 will not count last line if script source ends with \n.
204 if (script.source[script.source.length - 1] === '\n') {
205 endLine += 1;
206 endColumn = 0;
207 } else {
208 if (lineCount === 1)
209 endColumn = script.source.length + script.column_offset;
210 else
211 endColumn = script.source.length - (lineEnds[lineCount - 2] + 1);
212 }
213 return {
214 id: script.id,
215 name: script.nameOrSourceURL(),
216 sourceURL: script.source_url,
217 sourceMappingURL: script.source_mapping_url,
218 source: script.source,
219 startLine: script.line_offset,
220 startColumn: script.column_offset,
221 endLine: endLine,
222 endColumn: endColumn,
223 executionContextId: DebuggerScript._executionContextId(script.context_da ta),
224 // Note that we cannot derive aux data from context id because of compil ation cache.
225 executionContextAuxData: DebuggerScript._executionContextAuxData(script. context_data)
226 };
227 }
228
229 /**
230 * @param {!ExecutionState} execState
231 * @param {!BreakpointInfo} info
232 * @return {string|undefined}
233 */
234 DebuggerScript.setBreakpoint = function(execState, info)
235 {
236 var breakId = Debug.setScriptBreakPointById(info.sourceID, info.lineNumber, info.columnNumber, info.condition, undefined, Debug.BreakPositionAlignment.State ment);
237 var locations = Debug.findBreakPointActualLocations(breakId);
238 if (!locations.length)
239 return undefined;
240 info.lineNumber = locations[0].line;
241 info.columnNumber = locations[0].column;
242 return breakId.toString();
243 }
244
245 /**
246 * @param {!ExecutionState} execState
247 * @param {!{breakpointId: number}} info
248 */
249 DebuggerScript.removeBreakpoint = function(execState, info)
250 {
251 Debug.findBreakPoint(info.breakpointId, true);
252 }
253
254 /**
255 * @return {number}
256 */
257 DebuggerScript.pauseOnExceptionsState = function()
258 {
259 return DebuggerScript._pauseOnExceptionsState;
260 }
261
262 /**
263 * @param {number} newState
264 */
265 DebuggerScript.setPauseOnExceptionsState = function(newState)
266 {
267 DebuggerScript._pauseOnExceptionsState = newState;
268
269 if (DebuggerScript.PauseOnExceptionsState.PauseOnAllExceptions === newState)
270 Debug.setBreakOnException();
271 else
272 Debug.clearBreakOnException();
273
274 if (DebuggerScript.PauseOnExceptionsState.PauseOnUncaughtExceptions === newS tate)
275 Debug.setBreakOnUncaughtException();
276 else
277 Debug.clearBreakOnUncaughtException();
278 }
279
280 /**
281 * @param {!ExecutionState} execState
282 * @param {number} limit
283 * @return {!Array<!JavaScriptCallFrame>}
284 */
285 DebuggerScript.currentCallFrames = function(execState, limit)
286 {
287 var frames = [];
288 for (var i = 0; i < execState.frameCount() && (!limit || i < limit); ++i)
289 frames.push(DebuggerScript._frameMirrorToJSCallFrame(execState.frame(i)) );
290 return frames;
291 }
292
293 /**
294 * @param {!ExecutionState} execState
295 */
296 DebuggerScript.stepIntoStatement = function(execState)
297 {
298 execState.prepareStep(Debug.StepAction.StepIn);
299 }
300
301 /**
302 * @param {!ExecutionState} execState
303 */
304 DebuggerScript.stepFrameStatement = function(execState)
305 {
306 execState.prepareStep(Debug.StepAction.StepFrame);
307 }
308
309 /**
310 * @param {!ExecutionState} execState
311 */
312 DebuggerScript.stepOverStatement = function(execState)
313 {
314 execState.prepareStep(Debug.StepAction.StepNext);
315 }
316
317 /**
318 * @param {!ExecutionState} execState
319 */
320 DebuggerScript.stepOutOfFunction = function(execState)
321 {
322 execState.prepareStep(Debug.StepAction.StepOut);
323 }
324
325 DebuggerScript.clearStepping = function()
326 {
327 Debug.clearStepping();
328 }
329
330 // Returns array in form:
331 // [ 0, <v8_result_report> ] in case of success
332 // or [ 1, <general_error_message>, <compiler_message>, <line_number>, <column _number> ] in case of compile error, numbers are 1-based.
333 // or throws exception with message.
334 /**
335 * @param {number} scriptId
336 * @param {string} newSource
337 * @param {boolean} preview
338 * @return {!Array<*>}
339 */
340 DebuggerScript.liveEditScriptSource = function(scriptId, newSource, preview)
341 {
342 var scripts = Debug.scripts();
343 var scriptToEdit = null;
344 for (var i = 0; i < scripts.length; i++) {
345 if (scripts[i].id == scriptId) {
346 scriptToEdit = scripts[i];
347 break;
348 }
349 }
350 if (!scriptToEdit)
351 throw("Script not found");
352
353 var changeLog = [];
354 try {
355 var result = Debug.LiveEdit.SetScriptSource(scriptToEdit, newSource, pre view, changeLog);
356 return [0, result.stack_modified];
357 } catch (e) {
358 if (e instanceof Debug.LiveEdit.Failure && "details" in e) {
359 var details = /** @type {!LiveEditErrorDetails} */(e.details);
360 if (details.type === "liveedit_compile_error") {
361 var startPosition = details.position.start;
362 return [1, String(e), String(details.syntaxErrorMessage), Number (startPosition.line), Number(startPosition.column)];
363 }
364 }
365 throw e;
366 }
367 }
368
369 /**
370 * @param {!ExecutionState} execState
371 */
372 DebuggerScript.clearBreakpoints = function(execState)
373 {
374 Debug.clearAllBreakPoints();
375 }
376
377 /**
378 * @param {!ExecutionState} execState
379 * @param {!{enabled: boolean}} info
380 */
381 DebuggerScript.setBreakpointsActivated = function(execState, info)
382 {
383 Debug.debuggerFlags().breakPointsActive.setValue(info.enabled);
384 }
385
386 /**
387 * @param {!BreakEvent} eventData
388 */
389 DebuggerScript.getBreakpointNumbers = function(eventData)
390 {
391 var breakpoints = eventData.breakPointsHit();
392 var numbers = [];
393 if (!breakpoints)
394 return numbers;
395
396 for (var i = 0; i < breakpoints.length; i++) {
397 var breakpoint = breakpoints[i];
398 var scriptBreakPoint = breakpoint.script_break_point();
399 numbers.push(scriptBreakPoint ? scriptBreakPoint.number() : breakpoint.n umber());
400 }
401 return numbers;
402 }
403
404 // NOTE: This function is performance critical, as it can be run on every
405 // statement that generates an async event (like addEventListener) to support
406 // asynchronous call stacks. Thus, when possible, initialize the data lazily.
407 /**
408 * @param {!FrameMirror} frameMirror
409 * @return {!JavaScriptCallFrame}
410 */
411 DebuggerScript._frameMirrorToJSCallFrame = function(frameMirror)
412 {
413 // Stuff that can not be initialized lazily (i.e. valid while paused with a valid break_id).
414 // The frameMirror and scopeMirror can be accessed only while paused on the debugger.
415 var frameDetails = frameMirror.details();
416
417 var funcObject = frameDetails.func();
418 var sourcePosition = frameDetails.sourcePosition();
419 var thisObject = frameDetails.receiver();
420
421 var isAtReturn = !!frameDetails.isAtReturn();
422 var returnValue = isAtReturn ? frameDetails.returnValue() : undefined;
423
424 var scopeMirrors = frameMirror.allScopes(false);
425 /** @type {!Array<ScopeType>} */
426 var scopeTypes = new Array(scopeMirrors.length);
427 /** @type {?Array<!Object>} */
428 var scopeObjects = new Array(scopeMirrors.length);
429 /** @type {!Array<string|undefined>} */
430 var scopeNames = new Array(scopeMirrors.length);
431 /** @type {?Array<number>} */
432 var scopeStartPositions = new Array(scopeMirrors.length);
433 /** @type {?Array<number>} */
434 var scopeEndPositions = new Array(scopeMirrors.length);
435 /** @type {?Array<function()|null>} */
436 var scopeFunctions = new Array(scopeMirrors.length);
437 for (var i = 0; i < scopeMirrors.length; ++i) {
438 var scopeDetails = scopeMirrors[i].details();
439 scopeTypes[i] = scopeDetails.type();
440 scopeObjects[i] = scopeDetails.object();
441 scopeNames[i] = scopeDetails.name();
442 scopeStartPositions[i] = scopeDetails.startPosition ? scopeDetails.start Position() : 0;
443 scopeEndPositions[i] = scopeDetails.endPosition ? scopeDetails.endPositi on() : 0;
444 scopeFunctions[i] = scopeDetails.func ? scopeDetails.func() : null;
445 }
446
447 // Calculated lazily.
448 var scopeChain;
449 var funcMirror;
450 var location;
451 /** @type {!Array<?RawLocation>} */
452 var scopeStartLocations;
453 /** @type {!Array<?RawLocation>} */
454 var scopeEndLocations;
455 var details;
456
457 /**
458 * @param {!ScriptMirror|undefined} script
459 * @param {number} pos
460 * @return {?RawLocation}
461 */
462 function createLocation(script, pos)
463 {
464 if (!script)
465 return null;
466
467 var location = script.locationFromPosition(pos, true);
468 return {
469 "lineNumber": location.line,
470 "columnNumber": location.column,
471 "scriptId": String(script.id())
472 }
473 }
474
475 /**
476 * @return {!Array<!Object>}
477 */
478 function ensureScopeChain()
479 {
480 if (!scopeChain) {
481 scopeChain = [];
482 scopeStartLocations = [];
483 scopeEndLocations = [];
484 for (var i = 0, j = 0; i < scopeObjects.length; ++i) {
485 var scopeObject = DebuggerScript._buildScopeObject(scopeTypes[i] , scopeObjects[i]);
486 if (scopeObject) {
487 scopeTypes[j] = scopeTypes[i];
488 scopeNames[j] = scopeNames[i];
489 scopeChain[j] = scopeObject;
490
491 var funcMirror = scopeFunctions ? MakeMirror(scopeFunctions[ i]) : null;
492 if (!funcMirror || !funcMirror.isFunction())
493 funcMirror = new UnresolvedFunctionMirror(funcObject);
494
495 var script = /** @type {!FunctionMirror} */(funcMirror).scri pt();
496 scopeStartLocations[j] = createLocation(script, scopeStartPo sitions[i]);
497 scopeEndLocations[j] = createLocation(script, scopeEndPositi ons[i]);
498 ++j;
499 }
500 }
501 scopeTypes.length = scopeChain.length;
502 scopeNames.length = scopeChain.length;
503 scopeObjects = null; // Free for GC.
504 scopeFunctions = null;
505 scopeStartPositions = null;
506 scopeEndPositions = null;
507 }
508 return scopeChain;
509 }
510
511 /**
512 * @return {!JavaScriptCallFrameDetails}
513 */
514 function lazyDetails()
515 {
516 if (!details) {
517 var scopeObjects = ensureScopeChain();
518 var script = ensureFuncMirror().script();
519 /** @type {!Array<Scope>} */
520 var scopes = [];
521 for (var i = 0; i < scopeObjects.length; ++i) {
522 var scope = {
523 "type": /** @type {string} */(DebuggerScript._scopeTypeNames .get(scopeTypes[i])),
524 "object": scopeObjects[i],
525 };
526 if (scopeNames[i])
527 scope.name = scopeNames[i];
528 if (scopeStartLocations[i])
529 scope.startLocation = /** @type {!RawLocation} */(scopeStart Locations[i]);
530 if (scopeEndLocations[i])
531 scope.endLocation = /** @type {!RawLocation} */(scopeEndLoca tions[i]);
532 scopes.push(scope);
533 }
534 details = {
535 "functionName": ensureFuncMirror().debugName(),
536 "location": {
537 "lineNumber": line(),
538 "columnNumber": column(),
539 "scriptId": String(script.id())
540 },
541 "this": thisObject,
542 "scopeChain": scopes
543 };
544 var functionLocation = ensureFuncMirror().sourceLocation();
545 if (functionLocation) {
546 details.functionLocation = {
547 "lineNumber": functionLocation.line,
548 "columnNumber": functionLocation.column,
549 "scriptId": String(script.id())
550 };
551 }
552 if (isAtReturn)
553 details.returnValue = returnValue;
554 }
555 return details;
556 }
557
558 /**
559 * @return {!FunctionMirror}
560 */
561 function ensureFuncMirror()
562 {
563 if (!funcMirror) {
564 funcMirror = MakeMirror(funcObject);
565 if (!funcMirror.isFunction())
566 funcMirror = new UnresolvedFunctionMirror(funcObject);
567 }
568 return /** @type {!FunctionMirror} */(funcMirror);
569 }
570
571 /**
572 * @return {!{line: number, column: number}}
573 */
574 function ensureLocation()
575 {
576 if (!location) {
577 var script = ensureFuncMirror().script();
578 if (script)
579 location = script.locationFromPosition(sourcePosition, true);
580 if (!location)
581 location = { line: 0, column: 0 };
582 }
583 return location;
584 }
585
586 /**
587 * @return {number}
588 */
589 function line()
590 {
591 return ensureLocation().line;
592 }
593
594 /**
595 * @return {number}
596 */
597 function column()
598 {
599 return ensureLocation().column;
600 }
601
602 /**
603 * @return {number}
604 */
605 function contextId()
606 {
607 var mirror = ensureFuncMirror();
608 // Old V8 do not have context() function on these objects
609 if (!mirror.context)
610 return DebuggerScript._executionContextId(mirror.script().value().co ntext_data);
611 var context = mirror.context();
612 if (context)
613 return DebuggerScript._executionContextId(context.data());
614 return 0;
615 }
616
617 /**
618 * @return {number|undefined}
619 */
620 function sourceID()
621 {
622 var script = ensureFuncMirror().script();
623 return script && script.id();
624 }
625
626 /**
627 * @param {string} expression
628 * @return {*}
629 */
630 function evaluate(expression)
631 {
632 return frameMirror.evaluate(expression, false).value();
633 }
634
635 /** @return {undefined} */
636 function restart()
637 {
638 return frameMirror.restart();
639 }
640
641 /**
642 * @param {number} scopeNumber
643 * @param {string} variableName
644 * @param {*} newValue
645 */
646 function setVariableValue(scopeNumber, variableName, newValue)
647 {
648 var scopeMirror = frameMirror.scope(scopeNumber);
649 if (!scopeMirror)
650 throw new Error("Incorrect scope index");
651 scopeMirror.setVariableValue(variableName, newValue);
652 }
653
654 return {
655 "sourceID": sourceID,
656 "line": line,
657 "column": column,
658 "contextId": contextId,
659 "thisObject": thisObject,
660 "evaluate": evaluate,
661 "restart": restart,
662 "setVariableValue": setVariableValue,
663 "isAtReturn": isAtReturn,
664 "details": lazyDetails
665 };
666 }
667
668 /**
669 * @param {number} scopeType
670 * @param {!Object} scopeObject
671 * @return {!Object|undefined}
672 */
673 DebuggerScript._buildScopeObject = function(scopeType, scopeObject)
674 {
675 var result;
676 switch (scopeType) {
677 case ScopeType.Local:
678 case ScopeType.Closure:
679 case ScopeType.Catch:
680 case ScopeType.Block:
681 case ScopeType.Script:
682 // For transient objects we create a "persistent" copy that contains
683 // the same properties.
684 // Reset scope object prototype to null so that the proto properties
685 // don't appear in the local scope section.
686 var properties = /** @type {!ObjectMirror} */(MakeMirror(scopeObject, tr ue /* transient */)).properties();
687 // Almost always Script scope will be empty, so just filter out that noi se.
688 // Also drop empty Block scopes, should we get any.
689 if (!properties.length && (scopeType === ScopeType.Script || scopeType = == ScopeType.Block))
690 break;
691 result = { __proto__: null };
692 for (var j = 0; j < properties.length; j++) {
693 var name = properties[j].name();
694 if (name.length === 0 || name.charAt(0) === ".")
695 continue; // Skip internal variables like ".arguments" and varia bles with empty name
696 result[name] = properties[j].value_;
697 }
698 break;
699 case ScopeType.Global:
700 case ScopeType.With:
701 result = scopeObject;
702 break;
703 }
704 return result;
705 }
706
707 // We never resolve Mirror by its handle so to avoid memory leaks caused by Mirr ors in the cache we disable it.
708 ToggleMirrorCache(false);
709
710 return DebuggerScript;
711 })();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698