@@ -18,6 +18,7 @@ import 'package:dwds/src/utilities/server.dart';
18
18
import 'package:dwds/src/utilities/shared.dart' ;
19
19
import 'package:dwds/src/utilities/synchronized.dart' ;
20
20
import 'package:logging/logging.dart' ;
21
+ import 'package:path/path.dart' as p;
21
22
import 'package:vm_service/vm_service.dart' ;
22
23
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart'
23
24
hide StackTrace;
@@ -36,6 +37,14 @@ const _pauseModePauseStates = {
36
37
'unhandled' : PauseState .uncaught,
37
38
};
38
39
40
+ /// Mapping from the path of a script in Chrome to the Runtime.ScriptId Chrome
41
+ /// uses to reference it.
42
+ ///
43
+ /// See https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-ScriptId
44
+ ///
45
+ /// e.g. 'packages/myapp/main.dart.lib.js' -> '12'
46
+ final chromePathToRuntimeScriptId = < String , String > {};
47
+
39
48
class Debugger extends Domain {
40
49
static final logger = Logger ('Debugger' );
41
50
@@ -44,18 +53,17 @@ class Debugger extends Domain {
44
53
final StreamNotify _streamNotify;
45
54
final Locations _locations;
46
55
final SkipLists _skipLists;
47
- final String _root;
48
56
49
57
Debugger ._(
50
58
this ._remoteDebugger,
51
59
this ._streamNotify,
52
60
this ._locations,
53
61
this ._skipLists,
54
- this ._root ,
62
+ root ,
55
63
) : _breakpoints = _Breakpoints (
56
64
locations: _locations,
57
65
remoteDebugger: _remoteDebugger,
58
- root: _root ,
66
+ root: root ,
59
67
);
60
68
61
69
/// The breakpoints we have set so far, indexable by either
@@ -207,6 +215,7 @@ class Debugger extends Domain {
207
215
// miss events.
208
216
// Allow a null debugger/connection for unit tests.
209
217
runZonedGuarded (() {
218
+ _remoteDebugger.onScriptParsed.listen (_scriptParsedHandler);
210
219
_remoteDebugger.onPaused.listen (_pauseHandler);
211
220
_remoteDebugger.onResumed.listen (_resumeHandler);
212
221
_remoteDebugger.onTargetCrashed.listen (_crashHandler);
@@ -253,67 +262,6 @@ class Debugger extends Domain {
253
262
return breakpoint;
254
263
}
255
264
256
- Future <ScriptRef ?> _updatedScriptRefFor (Breakpoint breakpoint) async {
257
- final oldRef = (breakpoint.location as SourceLocation ).script;
258
- final uri = oldRef? .uri;
259
- if (uri == null ) return null ;
260
- final dartUri = DartUri (uri, _root);
261
- return await inspector.scriptRefFor (dartUri.serverPath);
262
- }
263
-
264
- Future <void > reestablishBreakpoints (
265
- Set <Breakpoint > previousBreakpoints,
266
- Set <Breakpoint > disabledBreakpoints,
267
- ) async {
268
- // Previous breakpoints were never removed from Chrome since we use
269
- // `setBreakpointByUrl`. We simply need to update the references.
270
- for (var breakpoint in previousBreakpoints) {
271
- final dartBpId = breakpoint.id! ;
272
- final scriptRef = await _updatedScriptRefFor (breakpoint);
273
- final scriptUri = scriptRef? .uri;
274
- if (scriptRef != null && scriptUri != null ) {
275
- final jsBpId = _breakpoints.jsIdFor (dartBpId)! ;
276
- final updatedLocation = await _locations.locationForDart (
277
- DartUri (scriptUri, _root),
278
- _lineNumberFor (breakpoint),
279
- _columnNumberFor (breakpoint),
280
- );
281
- if (updatedLocation != null ) {
282
- final updatedBreakpoint = _breakpoints._dartBreakpoint (
283
- scriptRef,
284
- updatedLocation,
285
- dartBpId,
286
- );
287
- _breakpoints._note (bp: updatedBreakpoint, jsId: jsBpId);
288
- _notifyBreakpoint (updatedBreakpoint);
289
- } else {
290
- logger.warning ('Cannot update breakpoint $dartBpId :'
291
- ' cannot update location.' );
292
- }
293
- } else {
294
- logger.warning ('Cannot update breakpoint $dartBpId :'
295
- ' cannot find script ref.' );
296
- }
297
- }
298
-
299
- // Disabled breakpoints were actually removed from Chrome so simply add
300
- // them back.
301
- for (var breakpoint in disabledBreakpoints) {
302
- final scriptRef = await _updatedScriptRefFor (breakpoint);
303
- final scriptId = scriptRef? .id;
304
- if (scriptId != null ) {
305
- await addBreakpoint (
306
- scriptId,
307
- _lineNumberFor (breakpoint),
308
- column: _columnNumberFor (breakpoint),
309
- );
310
- } else {
311
- logger.warning ('Cannot update disabled breakpoint ${breakpoint .id }:'
312
- ' cannot find script ref.' );
313
- }
314
- }
315
- }
316
-
317
265
void _notifyBreakpoint (Breakpoint breakpoint) {
318
266
final event = Event (
319
267
kind: EventKind .kBreakpointAdded,
@@ -520,6 +468,31 @@ class Debugger extends Domain {
520
468
return dartFrame;
521
469
}
522
470
471
+ void _scriptParsedHandler (ScriptParsedEvent e) {
472
+ final scriptPath = _pathForChromeScript (e.script.url);
473
+ if (scriptPath != null ) {
474
+ chromePathToRuntimeScriptId[scriptPath] = e.script.scriptId;
475
+ }
476
+ }
477
+
478
+ String ? _pathForChromeScript (String scriptUrl) {
479
+ final scriptPathSegments = Uri .parse (scriptUrl).pathSegments;
480
+ if (scriptPathSegments.isEmpty) {
481
+ return null ;
482
+ }
483
+
484
+ final isInternal = globalToolConfiguration.appMetadata.isInternalBuild;
485
+ const packagesDir = 'packages' ;
486
+ if (isInternal && scriptUrl.contains (packagesDir)) {
487
+ final packagesIdx = scriptPathSegments.indexOf (packagesDir);
488
+ return p.joinAll (scriptPathSegments.sublist (packagesIdx));
489
+ }
490
+
491
+ // Note: Replacing "\" with "/" is necessary because `joinAll` uses "\" if
492
+ // the platform is Windows. However, only "/" is expected by the browser.
493
+ return p.joinAll (scriptPathSegments).replaceAll ('\\ ' , '/' );
494
+ }
495
+
523
496
/// Handles pause events coming from the Chrome connection.
524
497
Future <void > _pauseHandler (DebuggerPausedEvent e) async {
525
498
final isolate = inspector.isolate;
@@ -738,14 +711,6 @@ Future<T> sendCommandAndValidateResult<T>(
738
711
return result;
739
712
}
740
713
741
- /// Returns the Dart line number for the provided breakpoint.
742
- int _lineNumberFor (Breakpoint breakpoint) =>
743
- int .parse (breakpoint.id! .split ('#' ).last.split (':' ).first);
744
-
745
- /// Returns the Dart column number for the provided breakpoint.
746
- int _columnNumberFor (Breakpoint breakpoint) =>
747
- int .parse (breakpoint.id! .split ('#' ).last.split (':' ).last);
748
-
749
714
/// Returns the breakpoint ID for the provided Dart script ID and Dart line
750
715
/// number.
751
716
String breakpointIdFor (String scriptId, int line, int column) =>
@@ -779,8 +744,10 @@ class _Breakpoints extends Domain {
779
744
int line,
780
745
int column,
781
746
) async {
747
+ print ('creating breakpoint at $scriptId :$line :$column )' );
782
748
final dartScript = inspector.scriptWithId (scriptId);
783
749
final dartScriptUri = dartScript? .uri;
750
+ print ('dart script uri is $dartScriptUri ' );
784
751
Location ? location;
785
752
if (dartScriptUri != null ) {
786
753
final dartUri = DartUri (dartScriptUri, root);
@@ -853,22 +820,27 @@ class _Breakpoints extends Domain {
853
820
854
821
/// Calls the Chrome protocol setBreakpoint and returns the remote ID.
855
822
Future <String ?> _setJsBreakpoint (Location location) {
856
- // The module can be loaded from a nested path and contain an ETAG suffix.
857
- final urlRegex = '.*${location .jsLocation .module }.*' ;
858
823
// Prevent `Aww, snap!` errors when setting multiple breakpoints
859
824
// simultaneously by serializing the requests.
860
825
return _queue.run (() async {
861
- final breakPointId = await sendCommandAndValidateResult <String >(
862
- remoteDebugger,
863
- method: 'Debugger.setBreakpointByUrl' ,
864
- resultField: 'breakpointId' ,
865
- params: {
866
- 'urlRegex' : urlRegex,
867
- 'lineNumber' : location.jsLocation.line,
868
- 'columnNumber' : location.jsLocation.column,
869
- },
870
- );
871
- return breakPointId;
826
+ final scriptId = location.jsLocation.runtimeScriptId;
827
+ if (scriptId != null ) {
828
+ return sendCommandAndValidateResult <String >(
829
+ remoteDebugger,
830
+ method: 'Debugger.setBreakpoint' ,
831
+ resultField: 'breakpointId' ,
832
+ params: {
833
+ 'location' : {
834
+ 'lineNumber' : location.jsLocation.line,
835
+ 'columnNumber' : location.jsLocation.column,
836
+ 'scriptId' : scriptId,
837
+ },
838
+ },
839
+ );
840
+ } else {
841
+ _logger.fine ('No runtime script ID for location $location ' );
842
+ return null ;
843
+ }
872
844
});
873
845
}
874
846
0 commit comments