Skip to content

Commit ae3e2ea

Browse files
authored
[MV3] Dart Debug Extension supports cross-extension communication with AngularDart DevTools (#1866)
1 parent 0662af9 commit ae3e2ea

10 files changed

+329
-22
lines changed

dwds/debug_extension_mv3/web/background.dart

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import 'package:js/js.dart';
1515
import 'data_types.dart';
1616
import 'debug_session.dart';
1717
import 'chrome_api.dart';
18+
import 'cross_extension_communication.dart';
1819
import 'lifeline_ports.dart';
1920
import 'logger.dart';
2021
import 'messaging.dart';
@@ -29,7 +30,15 @@ void main() {
2930
}
3031

3132
void _registerListeners() {
32-
chrome.runtime.onMessage.addListener(allowInterop(_handleRuntimeMessages));
33+
chrome.runtime.onMessage.addListener(
34+
allowInterop(_handleRuntimeMessages),
35+
);
36+
// The only extension allowed to send messages to this extension is the
37+
// AngularDart DevTools extension. Its permission is set in the manifest.json
38+
// externally_connectable field.
39+
chrome.runtime.onMessageExternal.addListener(
40+
allowInterop(handleMessagesFromAngularDartDevTools),
41+
);
3342
chrome.tabs.onRemoved
3443
.addListener(allowInterop((tabId, _) => maybeRemoveLifelinePort(tabId)));
3544
// Update the extension icon on tab navigation:

dwds/debug_extension_mv3/web/chrome_api.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,8 @@ class Runtime {
176176
external ConnectionHandler get onConnect;
177177

178178
external OnMessageHandler get onMessage;
179+
180+
external OnMessageHandler get onMessageExternal;
179181
}
180182

181183
@JS()
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
@JS()
6+
library cross_extension_communication;
7+
8+
import 'package:js/js.dart';
9+
10+
import 'chrome_api.dart';
11+
import 'data_types.dart';
12+
import 'debug_session.dart';
13+
import 'logger.dart';
14+
import 'storage.dart';
15+
import 'web_api.dart';
16+
17+
// The only extension allowed to communicate with this extension is the
18+
// AngularDart DevTools extension.
19+
//
20+
// This ID is used to send messages to AngularDart DevTools, while the
21+
// externally_connectable field in the manifest.json allows AngularDart DevTools
22+
// to send messages to this extension.
23+
const _angularDartDevToolsId = 'nbkbficgbembimioedhceniahniffgpl';
24+
25+
// A set of events to forward to the AngularDart DevTools extension.
26+
final _eventsForAngularDartDevTools = {
27+
'Overlay.inspectNodeRequested',
28+
'dwds.encodedUri',
29+
};
30+
31+
void handleMessagesFromAngularDartDevTools(
32+
dynamic jsRequest, MessageSender sender, Function sendResponse) async {
33+
if (jsRequest == null) return;
34+
final message = jsRequest as ExternalExtensionMessage;
35+
if (message.name == 'chrome.debugger.sendCommand') {
36+
_forwardCommandToChromeDebugger(message, sendResponse);
37+
} else if (message.name == 'dwds.encodedUri') {
38+
_respondWithEncodedUri(message.tabId, sendResponse);
39+
} else if (message.name == 'dwds.startDebugging') {
40+
attachDebugger(message.tabId, trigger: Trigger.angularDartDevTools);
41+
sendResponse(true);
42+
} else {
43+
sendResponse(
44+
ErrorResponse()..error = 'Unknown message name: ${message.name}');
45+
}
46+
}
47+
48+
void maybeForwardMessageToAngularDartDevTools(
49+
{required String method, required dynamic params, required int tabId}) {
50+
if (!_eventsForAngularDartDevTools.contains(method)) return;
51+
52+
final message = method.startsWith('dwds')
53+
? _dwdsEventMessage(method: method, params: params, tabId: tabId)
54+
: _debugEventMessage(method: method, params: params, tabId: tabId);
55+
56+
_forwardMessageToAngularDartDevTools(message);
57+
}
58+
59+
void _forwardCommandToChromeDebugger(
60+
ExternalExtensionMessage message, Function sendResponse) {
61+
try {
62+
final options = message.options as SendCommandOptions;
63+
chrome.debugger.sendCommand(
64+
Debuggee(tabId: message.tabId),
65+
options.method,
66+
options.commandParams,
67+
allowInterop(
68+
([result]) => _respondWithChromeResult(result, sendResponse)),
69+
);
70+
} catch (e) {
71+
sendResponse(ErrorResponse()..error = '$e');
72+
}
73+
}
74+
75+
void _respondWithChromeResult(Object? chromeResult, Function sendResponse) {
76+
// No result indicates that an error occurred.
77+
if (chromeResult == null) {
78+
sendResponse(ErrorResponse()
79+
..error = JSON.stringify(
80+
chrome.runtime.lastError ?? 'Unknown error.',
81+
));
82+
} else {
83+
sendResponse(chromeResult);
84+
}
85+
}
86+
87+
void _respondWithEncodedUri(int tabId, Function sendResponse) async {
88+
final encodedUri = await fetchStorageObject<EncodedUri>(
89+
type: StorageObject.encodedUri, tabId: tabId);
90+
sendResponse(encodedUri ?? '');
91+
}
92+
93+
void _forwardMessageToAngularDartDevTools(ExternalExtensionMessage message) {
94+
chrome.runtime.sendMessage(
95+
_angularDartDevToolsId,
96+
message,
97+
/* options */ null,
98+
allowInterop(([result]) => _checkForErrors(result, message.name)),
99+
);
100+
}
101+
102+
void _checkForErrors(Object? chromeResult, String messageName) {
103+
// No result indicates that an error occurred.
104+
if (chromeResult == null) {
105+
final errorMessage = chrome.runtime.lastError?.message ?? 'Unknown error.';
106+
debugWarn('Error forwarding $messageName: $errorMessage');
107+
}
108+
}
109+
110+
ExternalExtensionMessage _debugEventMessage({
111+
required String method,
112+
required dynamic params,
113+
required int tabId,
114+
}) =>
115+
ExternalExtensionMessage(
116+
name: 'chrome.debugger.event',
117+
tabId: tabId,
118+
options: DebugEvent(method: method, params: params),
119+
);
120+
121+
ExternalExtensionMessage _dwdsEventMessage({
122+
required String method,
123+
required dynamic params,
124+
required int tabId,
125+
}) =>
126+
ExternalExtensionMessage(
127+
name: method,
128+
tabId: tabId,
129+
options: params,
130+
);
131+
132+
// This message is used for cross-extension communication between this extension
133+
// and the AngularDart DevTools extension.
134+
@JS()
135+
@anonymous
136+
class ExternalExtensionMessage {
137+
external int get tabId;
138+
external String get name;
139+
external dynamic get options;
140+
external factory ExternalExtensionMessage(
141+
{required int tabId, required String name, required dynamic options});
142+
}
143+
144+
@JS()
145+
@anonymous
146+
class DebugEvent {
147+
external factory DebugEvent({String method, Object? params});
148+
}
149+
150+
@JS()
151+
@anonymous
152+
class SendCommandOptions {
153+
external String get method;
154+
external Object get commandParams;
155+
}
156+
157+
@JS()
158+
@anonymous
159+
class ErrorResponse {
160+
external set error(String error);
161+
}

dwds/debug_extension_mv3/web/data_serializers.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ part 'data_serializers.g.dart';
2121
DevToolsOpener,
2222
DevToolsUrl,
2323
DevToolsRequest,
24+
EncodedUri,
2425
ExtensionEvent,
2526
ExtensionRequest,
2627
ExtensionResponse,

dwds/debug_extension_mv3/web/data_serializers.g.dart

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dwds/debug_extension_mv3/web/data_types.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,18 @@ abstract class DevToolsOpener
3535
bool get newWindow;
3636
}
3737

38+
// TODO(elliette): Standardize on uri or url here and across DWDS, instead of a
39+
// combination of both.
40+
abstract class EncodedUri implements Built<EncodedUri, EncodedUriBuilder> {
41+
static Serializer<EncodedUri> get serializer => _$encodedUriSerializer;
42+
43+
factory EncodedUri([Function(EncodedUriBuilder) updates]) = _$EncodedUri;
44+
45+
EncodedUri._();
46+
47+
String get uri;
48+
}
49+
3850
abstract class DevToolsUrl implements Built<DevToolsUrl, DevToolsUrlBuilder> {
3951
static Serializer<DevToolsUrl> get serializer => _$devToolsUrlSerializer;
4052

dwds/debug_extension_mv3/web/data_types.g.dart

Lines changed: 118 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)