Skip to content

Commit 2c84bf1

Browse files
authored
Migrate the Dart Debug Extension to null-safety (#1655)
1 parent 3c30561 commit 2c84bf1

File tree

5 files changed

+5229
-5346
lines changed

5 files changed

+5229
-5346
lines changed

dwds/debug_extension/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
## 1.30
33
- Batch extension `Debugger.scriptParsed` events and send batches every 1000ms
44
to the server.
5+
- Enable null-safety.
56

67
## 1.29
78

dwds/debug_extension/pubspec.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ description: >-
66
A chrome extension for Dart debugging.
77
88
environment:
9-
sdk: ">=2.5.0 <3.0.0"
9+
sdk: '>=2.12.0 <3.0.0'
1010

1111
dependencies:
1212
async: ^2.3.0
13+
collection: ^1.15.0
1314
js: ^0.6.1+1
1415
pub_semver: ^2.0.0
1516
sse: ^4.1.0

dwds/debug_extension/tool/copy_builder.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
// @dart = 2.9
6-
75
import 'package:build/build.dart';
86

97
/// Factory for the build script.

dwds/debug_extension/web/background.dart

Lines changed: 55 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
// @dart = 2.9
6-
75
@JS()
86
library background;
97

@@ -12,6 +10,12 @@ import 'dart:convert';
1210
import 'dart:html';
1311

1412
import 'package:built_collection/built_collection.dart';
13+
// TODO(elliette): The format_and_analyze Github actions complains about this
14+
// import because it is looking for it in DWDS' pubspec, not in the extension's
15+
// pubspec. We should fix the Github action and / or unnest the extension from
16+
// the DWDS directory.
17+
// ignore: depend_on_referenced_packages
18+
import 'package:collection/collection.dart' show IterableExtension;
1519
import 'package:dwds/data/devtools_request.dart';
1620
import 'package:dwds/data/extension_request.dart';
1721
import 'package:dwds/data/serializers.dart';
@@ -55,11 +59,11 @@ final _devToolsPanelsNotifier =
5559
// Keeps track of the most recent Dart tab that was opened. This is a heuristic
5660
// to let us guess which tab the user is trying to debug if they start debugging
5761
// from the Chrome DevTools Dart panel (which doesn't have a tab ID).
58-
Tab _mostRecentDartTab;
62+
Tab? _mostRecentDartTab;
5963

6064
// Keeps track of how debugging was triggered. This lets us know if we should
6165
// open Dart DevTools in a new window/tab or embed it in Chrome DevTools.
62-
DebuggerTrigger _debuggerTrigger;
66+
DebuggerTrigger? _debuggerTrigger;
6367

6468
class DebugSession {
6569
// The tab ID that contains the running Dart application.
@@ -69,7 +73,7 @@ class DebugSession {
6973
final String appId;
7074

7175
// The tab ID that contains the corresponding Dart DevTools.
72-
int devtoolsTabId;
76+
int? devtoolsTabId;
7377

7478
// Socket client for communication with dwds extension backend.
7579
final SocketClient _socketClient;
@@ -80,7 +84,7 @@ class DebugSession {
8084
// Collect events into batches to be send periodically to the server.
8185
final _batchController =
8286
BatchedStreamController<ExtensionEvent>(delay: _batchDelayMilliseconds);
83-
StreamSubscription<List<ExtensionEvent>> _batchSubscription;
87+
late final StreamSubscription<List<ExtensionEvent>> _batchSubscription;
8488

8589
DebugSession(this._socketClient, this.appTabId, this.appId) {
8690
// Collect extension events and send them periodically to the server.
@@ -107,10 +111,10 @@ class DebugSession {
107111

108112
class DevToolsPanel {
109113
// The Dart app ID.
110-
final String appId;
114+
final String? appId;
111115

112116
// The Chrome DevTools panel ID.
113-
String panelId;
117+
String? panelId;
114118

115119
// The URI for the embedded Dart DevTools, or an empty string if the debugger
116120
// is disconnected.
@@ -213,7 +217,7 @@ void _startDebugging(DebuggerTrigger debuggerTrigger) {
213217
if (tabs.isNotEmpty) {
214218
attachDebuggerToTab(tabs.first as Tab);
215219
} else if (_mostRecentDartTab != null) {
216-
attachDebuggerToTab(_mostRecentDartTab);
220+
attachDebuggerToTab(_mostRecentDartTab!);
217221
} else {
218222
alert('''
219223
Could not find a Dart app to start debugging.
@@ -235,8 +239,8 @@ void _attachDebuggerToTab(Tab currentTab) async {
235239
attach(Debuggee(tabId: currentTab.id), '1.3', allowInterop(() async {
236240
if (lastError != null) {
237241
String alertMessage;
238-
if (lastError.message.contains('Cannot access') ||
239-
lastError.message.contains('Cannot attach')) {
242+
if (lastError!.message.contains('Cannot access') ||
243+
lastError!.message.contains('Cannot attach')) {
240244
alertMessage = _notADartAppAlert;
241245
} else {
242246
alertMessage = 'DevTools is already opened on a different window.';
@@ -298,12 +302,12 @@ void _maybeAttachDebugSession(
298302
if (method != 'Runtime.executionContextCreated') return;
299303

300304
final context = json.decode(stringify(params))['context'];
301-
final tab = _tabsToAttach.firstWhere((tab) => tab.id == source.tabId,
302-
orElse: () => null);
303-
if (tab != null) {
305+
final tab = _tabsToAttach.firstWhereOrNull((tab) => tab.id == source.tabId);
306+
final contextId = context['id'] as int?;
307+
if (tab != null && contextId != null) {
304308
final launchInChromeDevTools =
305309
_debuggerTrigger == DebuggerTrigger.dartPanel;
306-
if (await _tryAttach(context['id'] as int, tab, launchInChromeDevTools)) {
310+
if (await _tryAttach(contextId, tab, launchInChromeDevTools)) {
307311
_tabsToAttach.remove(tab);
308312
}
309313
}
@@ -322,9 +326,8 @@ void _removeAndDetachDebugSessionForTab(int tabId, _) {
322326
// Tries to remove the debug session for the specified tab. If no session is
323327
// found, returns -1. Otherwise returns the tab ID.
324328
int _removeDebugSessionForTab(int tabId) {
325-
final session = _debugSessions.firstWhere(
326-
(session) => session.appTabId == tabId || session.devtoolsTabId == tabId,
327-
orElse: () => null);
329+
final session = _debugSessions.firstWhereOrNull(
330+
(session) => session.appTabId == tabId || session.devtoolsTabId == tabId);
328331
if (session != null) {
329332
// Note: package:sse will try to keep the connection alive, even after the
330333
// client has been closed. Therefore the extension sends an event to notify
@@ -435,23 +438,25 @@ Future<bool> _tryAttach(
435438
expression:
436439
'[\$dartExtensionUri, \$dartAppId, \$dartAppInstanceId, window.\$dwdsVersion]',
437440
returnByValue: true,
438-
contextId: contextId), allowInterop((e) {
439-
String extensionUri, appId, instanceId, dwdsVersion;
440-
if (e.result.value == null) {
441+
contextId: contextId), allowInterop((evalResponse) {
442+
final value = evalResponse.result.value;
443+
final extensionUri = value?[0] as String?;
444+
final appId = value?[1] as String?;
445+
final instanceId = value?[2] as String?;
446+
final dwdsVersion = value?[3] as String?;
447+
if (extensionUri == null || appId == null || instanceId == null) {
448+
consoleWarn(
449+
'Unable to debug app. Missing Dart debugging global variables');
441450
successCompleter.complete(false);
442451
return;
443452
}
444-
extensionUri = e.result.value[0] as String;
445-
appId = e.result.value[1] as String;
446-
instanceId = e.result.value[2] as String;
447-
dwdsVersion = e.result.value[3] as String;
448453
_startSseClient(
449454
Uri.parse(extensionUri),
450455
appId,
451456
instanceId,
452457
contextId,
453458
tab,
454-
dwdsVersion,
459+
dwdsVersion ?? '0.0.0',
455460
launchInChromeDevTools,
456461
);
457462
successCompleter.complete(true);
@@ -472,16 +477,16 @@ Future<void> _startSseClient(
472477
String dwdsVersion,
473478
bool launchInChromeDevTools,
474479
) async {
475-
if (Version.parse(dwdsVersion ?? '0.0.0') >= Version.parse('9.1.0')) {
480+
if (Version.parse(dwdsVersion) >= Version.parse('9.1.0')) {
476481
var authUri = uri.replace(path: authenticationPath);
477482
if (authUri.scheme == 'ws') authUri = authUri.replace(scheme: 'http');
478483
if (authUri.scheme == 'wss') authUri = authUri.replace(scheme: 'https');
479484
final authUrl = authUri.toString();
480485
try {
481486
final response = await HttpRequest.request(authUrl,
482487
method: 'GET', withCredentials: true);
483-
if (!response.responseText
484-
.contains('Dart Debug Authentication Success!')) {
488+
final responseText = response.responseText ?? '';
489+
if (!responseText.contains('Dart Debug Authentication Success!')) {
485490
throw Exception('Not authenticated.');
486491
}
487492
} catch (_) {
@@ -507,8 +512,9 @@ Future<void> _startSseClient(
507512
client.stream.listen((data) {
508513
final message = serializers.deserialize(jsonDecode(data));
509514
if (message is ExtensionRequest) {
515+
final messageParams = message.commandParams ?? '{}';
510516
final params =
511-
BuiltMap<String, Object>(json.decode(message.commandParams)).toMap();
517+
BuiltMap<String, Object>(json.decode(messageParams)).toMap();
512518
sendCommand(Debuggee(tabId: currentTab.id), message.command,
513519
js_util.jsify(params), allowInterop(([e]) {
514520
// No arguments indicate that an error occurred.
@@ -588,7 +594,6 @@ void _updateIcon() {
588594
// Therefore, do not update the icon:
589595
if (tabs.isEmpty) return;
590596
final tab = tabs.first as Tab;
591-
if (tab.id == null) return;
592597

593598
if (_tabIdToWarning.containsKey(tab.id)) {
594599
// Set the warning icon (red):
@@ -605,16 +610,15 @@ void _updateIcon() {
605610
}
606611

607612
/// Construct an [ExtensionEvent] from [method] and [params].
608-
ExtensionEvent _extensionEventFor(String method, Object params) =>
613+
ExtensionEvent _extensionEventFor(String method, Object? params) =>
609614
ExtensionEvent((b) => b
610615
..params = jsonEncode(json.decode(stringify(params)))
611616
..method = jsonEncode(method));
612617

613618
/// Forward debugger events to the backend if applicable.
614619
void _filterAndForwardToBackend(Debuggee source, String method, Object params) {
615-
final debugSession = _debugSessions.firstWhere(
616-
(session) => session.appTabId == source.tabId,
617-
orElse: () => null);
620+
final debugSession = _debugSessions
621+
.firstWhereOrNull((session) => session.appTabId == source.tabId);
618622

619623
if (debugSession == null) return;
620624

@@ -662,7 +666,7 @@ external void browserActionOnClickedAddListener(Function callback);
662666

663667
@JS('chrome.debugger.sendCommand')
664668
external void sendCommand(
665-
Debuggee target, String method, Object commandParams, Function callback);
669+
Debuggee target, String method, Object? commandParams, Function callback);
666670

667671
@JS('chrome.debugger.attach')
668672
external void attach(
@@ -684,7 +688,7 @@ external List<Tab> queryTabs(QueryInfo queryInfo, Function callback);
684688
external String stringify(o);
685689

686690
@JS('window.alert')
687-
external void alert([String message]);
691+
external void alert([String? message]);
688692

689693
@JS('chrome.tabs.onCreated.addListener')
690694
external void tabsOnCreatedAddListener(Function callback);
@@ -715,16 +719,20 @@ external void sendMessage(
715719
String id, Object message, Object options, Function callback);
716720

717721
@JS('chrome.runtime.sendMessage')
718-
external void sendSimpleMessage(String id, SimpleMessage message);
722+
external void sendSimpleMessage(String? id, SimpleMessage message);
723+
724+
@JS('console.warn')
725+
external void consoleWarn(String header,
726+
[String? style1, String? style2, String? style3]);
719727

720728
// For debugging purposes:
721729
@JS('console.log')
722730
external void consoleLog(String header,
723-
[String style1, String style2, String style3]);
731+
[String? style1, String? style2, String? style3]);
724732

725733
// Note: Not checking the lastError when one occurs throws a runtime exception.
726734
@JS('chrome.runtime.lastError')
727-
external ChromeError get lastError;
735+
external ChromeError? get lastError;
728736

729737
@JS()
730738
class ChromeError {
@@ -736,7 +744,7 @@ class ChromeError {
736744
class QueryInfo {
737745
external bool get active;
738746
external bool get currentWindow;
739-
external factory QueryInfo({bool active, bool currentWindow});
747+
external factory QueryInfo({bool? active, bool? currentWindow});
740748
}
741749

742750
@JS()
@@ -759,7 +767,7 @@ class Debuggee {
759767
external int get tabId;
760768
external String get extensionId;
761769
external String get targetId;
762-
external factory Debuggee({int tabId, String extensionId, String targetId});
770+
external factory Debuggee({int tabId, String? extensionId, String? targetId});
763771
}
764772

765773
@JS()
@@ -787,7 +795,8 @@ class NavigationInfo {
787795
class SimpleMessage {
788796
external String get recipient;
789797
external String get body;
790-
external factory SimpleMessage({String recipient, String body});
798+
external factory SimpleMessage(
799+
{required String recipient, required String body});
791800
}
792801

793802
@JS()
@@ -800,7 +809,8 @@ class Request {
800809
external dynamic get options;
801810
external String get warning;
802811
external String get message;
803-
external factory Request({int tabId, String name, dynamic options});
812+
external factory Request(
813+
{required int tabId, required String name, required dynamic options});
804814
}
805815

806816
@JS()
@@ -858,14 +868,7 @@ class InjectedParams {
858868
external bool get returnByValue;
859869
external int get contextId;
860870
external factory InjectedParams(
861-
{String expression, bool returnByValue, int contextId});
862-
}
863-
864-
@JS()
865-
@anonymous
866-
class ScriptIdParam {
867-
external String get scriptId;
868-
external factory ScriptIdParam({String scriptId});
871+
{String? expression, bool? returnByValue, int? contextId});
869872
}
870873

871874
@JS()

0 commit comments

Comments
 (0)