Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dwds/debug_extension/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
## 1.30
- Batch extension `Debugger.scriptParsed` events and send batches every 1000ms
to the server.
- Enable null-safety.

## 1.29

Expand Down
3 changes: 2 additions & 1 deletion dwds/debug_extension/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ description: >-
A chrome extension for Dart debugging.

environment:
sdk: ">=2.5.0 <3.0.0"
sdk: '>=2.12.0 <3.0.0'

dependencies:
async: ^2.3.0
collection: ^1.15.0
js: ^0.6.1+1
pub_semver: ^2.0.0
sse: ^4.1.0
Expand Down
2 changes: 0 additions & 2 deletions dwds/debug_extension/tool/copy_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// @dart = 2.9

import 'package:build/build.dart';

/// Factory for the build script.
Expand Down
107 changes: 55 additions & 52 deletions dwds/debug_extension/web/background.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// @dart = 2.9

@JS()
library background;

Expand All @@ -12,6 +10,12 @@ import 'dart:convert';
import 'dart:html';

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

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

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

// The tab ID that contains the corresponding Dart DevTools.
int devtoolsTabId;
int? devtoolsTabId;

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

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

class DevToolsPanel {
// The Dart app ID.
final String appId;
final String? appId;

// The Chrome DevTools panel ID.
String panelId;
String? panelId;

// The URI for the embedded Dart DevTools, or an empty string if the debugger
// is disconnected.
Expand Down Expand Up @@ -213,7 +217,7 @@ void _startDebugging(DebuggerTrigger debuggerTrigger) {
if (tabs.isNotEmpty) {
attachDebuggerToTab(tabs.first as Tab);
} else if (_mostRecentDartTab != null) {
attachDebuggerToTab(_mostRecentDartTab);
attachDebuggerToTab(_mostRecentDartTab!);
} else {
alert('''
Could not find a Dart app to start debugging.
Expand All @@ -235,8 +239,8 @@ void _attachDebuggerToTab(Tab currentTab) async {
attach(Debuggee(tabId: currentTab.id), '1.3', allowInterop(() async {
if (lastError != null) {
String alertMessage;
if (lastError.message.contains('Cannot access') ||
lastError.message.contains('Cannot attach')) {
if (lastError!.message.contains('Cannot access') ||
lastError!.message.contains('Cannot attach')) {
alertMessage = _notADartAppAlert;
} else {
alertMessage = 'DevTools is already opened on a different window.';
Expand Down Expand Up @@ -298,12 +302,12 @@ void _maybeAttachDebugSession(
if (method != 'Runtime.executionContextCreated') return;

final context = json.decode(stringify(params))['context'];
final tab = _tabsToAttach.firstWhere((tab) => tab.id == source.tabId,
orElse: () => null);
if (tab != null) {
final tab = _tabsToAttach.firstWhereOrNull((tab) => tab.id == source.tabId);
final contextId = context['id'] as int?;
if (tab != null && contextId != null) {
final launchInChromeDevTools =
_debuggerTrigger == DebuggerTrigger.dartPanel;
if (await _tryAttach(context['id'] as int, tab, launchInChromeDevTools)) {
if (await _tryAttach(contextId, tab, launchInChromeDevTools)) {
_tabsToAttach.remove(tab);
}
}
Expand All @@ -322,9 +326,8 @@ void _removeAndDetachDebugSessionForTab(int tabId, _) {
// Tries to remove the debug session for the specified tab. If no session is
// found, returns -1. Otherwise returns the tab ID.
int _removeDebugSessionForTab(int tabId) {
final session = _debugSessions.firstWhere(
(session) => session.appTabId == tabId || session.devtoolsTabId == tabId,
orElse: () => null);
final session = _debugSessions.firstWhereOrNull(
(session) => session.appTabId == tabId || session.devtoolsTabId == tabId);
if (session != null) {
// Note: package:sse will try to keep the connection alive, even after the
// client has been closed. Therefore the extension sends an event to notify
Expand Down Expand Up @@ -435,23 +438,25 @@ Future<bool> _tryAttach(
expression:
'[\$dartExtensionUri, \$dartAppId, \$dartAppInstanceId, window.\$dwdsVersion]',
returnByValue: true,
contextId: contextId), allowInterop((e) {
String extensionUri, appId, instanceId, dwdsVersion;
if (e.result.value == null) {
contextId: contextId), allowInterop((evalResponse) {
final value = evalResponse.result.value;
final extensionUri = value?[0] as String?;
final appId = value?[1] as String?;
final instanceId = value?[2] as String?;
final dwdsVersion = value?[3] as String?;
if (extensionUri == null || appId == null || instanceId == null) {
consoleWarn(
'Unable to debug app. Missing Dart debugging global variables');
successCompleter.complete(false);
return;
}
extensionUri = e.result.value[0] as String;
appId = e.result.value[1] as String;
instanceId = e.result.value[2] as String;
dwdsVersion = e.result.value[3] as String;
_startSseClient(
Uri.parse(extensionUri),
appId,
instanceId,
contextId,
tab,
dwdsVersion,
dwdsVersion ?? '0.0.0',
launchInChromeDevTools,
);
successCompleter.complete(true);
Expand All @@ -472,16 +477,16 @@ Future<void> _startSseClient(
String dwdsVersion,
bool launchInChromeDevTools,
) async {
if (Version.parse(dwdsVersion ?? '0.0.0') >= Version.parse('9.1.0')) {
if (Version.parse(dwdsVersion) >= Version.parse('9.1.0')) {
var authUri = uri.replace(path: authenticationPath);
if (authUri.scheme == 'ws') authUri = authUri.replace(scheme: 'http');
if (authUri.scheme == 'wss') authUri = authUri.replace(scheme: 'https');
final authUrl = authUri.toString();
try {
final response = await HttpRequest.request(authUrl,
method: 'GET', withCredentials: true);
if (!response.responseText
.contains('Dart Debug Authentication Success!')) {
final responseText = response.responseText ?? '';
if (!responseText.contains('Dart Debug Authentication Success!')) {
throw Exception('Not authenticated.');
}
} catch (_) {
Expand All @@ -507,8 +512,9 @@ Future<void> _startSseClient(
client.stream.listen((data) {
final message = serializers.deserialize(jsonDecode(data));
if (message is ExtensionRequest) {
final messageParams = message.commandParams ?? '{}';
final params =
BuiltMap<String, Object>(json.decode(message.commandParams)).toMap();
BuiltMap<String, Object>(json.decode(messageParams)).toMap();
sendCommand(Debuggee(tabId: currentTab.id), message.command,
js_util.jsify(params), allowInterop(([e]) {
// No arguments indicate that an error occurred.
Expand Down Expand Up @@ -588,7 +594,6 @@ void _updateIcon() {
// Therefore, do not update the icon:
if (tabs.isEmpty) return;
final tab = tabs.first as Tab;
if (tab.id == null) return;

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

/// Construct an [ExtensionEvent] from [method] and [params].
ExtensionEvent _extensionEventFor(String method, Object params) =>
ExtensionEvent _extensionEventFor(String method, Object? params) =>
ExtensionEvent((b) => b
..params = jsonEncode(json.decode(stringify(params)))
..method = jsonEncode(method));

/// Forward debugger events to the backend if applicable.
void _filterAndForwardToBackend(Debuggee source, String method, Object params) {
final debugSession = _debugSessions.firstWhere(
(session) => session.appTabId == source.tabId,
orElse: () => null);
final debugSession = _debugSessions
.firstWhereOrNull((session) => session.appTabId == source.tabId);

if (debugSession == null) return;

Expand Down Expand Up @@ -662,7 +666,7 @@ external void browserActionOnClickedAddListener(Function callback);

@JS('chrome.debugger.sendCommand')
external void sendCommand(
Debuggee target, String method, Object commandParams, Function callback);
Debuggee target, String method, Object? commandParams, Function callback);

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

@JS('window.alert')
external void alert([String message]);
external void alert([String? message]);

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

@JS('chrome.runtime.sendMessage')
external void sendSimpleMessage(String id, SimpleMessage message);
external void sendSimpleMessage(String? id, SimpleMessage message);

@JS('console.warn')
external void consoleWarn(String header,
[String? style1, String? style2, String? style3]);

// For debugging purposes:
@JS('console.log')
external void consoleLog(String header,
[String style1, String style2, String style3]);
[String? style1, String? style2, String? style3]);

// Note: Not checking the lastError when one occurs throws a runtime exception.
@JS('chrome.runtime.lastError')
external ChromeError get lastError;
external ChromeError? get lastError;

@JS()
class ChromeError {
Expand All @@ -736,7 +744,7 @@ class ChromeError {
class QueryInfo {
external bool get active;
external bool get currentWindow;
external factory QueryInfo({bool active, bool currentWindow});
external factory QueryInfo({bool? active, bool? currentWindow});
}

@JS()
Expand All @@ -759,7 +767,7 @@ class Debuggee {
external int get tabId;
external String get extensionId;
external String get targetId;
external factory Debuggee({int tabId, String extensionId, String targetId});
external factory Debuggee({int tabId, String? extensionId, String? targetId});
}

@JS()
Expand Down Expand Up @@ -787,7 +795,8 @@ class NavigationInfo {
class SimpleMessage {
external String get recipient;
external String get body;
external factory SimpleMessage({String recipient, String body});
external factory SimpleMessage(
{required String recipient, required String body});
}

@JS()
Expand All @@ -800,7 +809,8 @@ class Request {
external dynamic get options;
external String get warning;
external String get message;
external factory Request({int tabId, String name, dynamic options});
external factory Request(
{required int tabId, required String name, required dynamic options});
}

@JS()
Expand Down Expand Up @@ -858,14 +868,7 @@ class InjectedParams {
external bool get returnByValue;
external int get contextId;
external factory InjectedParams(
{String expression, bool returnByValue, int contextId});
}

@JS()
@anonymous
class ScriptIdParam {
external String get scriptId;
external factory ScriptIdParam({String scriptId});
{String? expression, bool? returnByValue, int? contextId});
}

@JS()
Expand Down
Loading