From 92a66ce6748caa47ebe6abbf0c590eacc7b8bd28 Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Wed, 4 Jan 2023 14:19:16 -0800 Subject: [PATCH 1/9] Forward messages to external extensions --- dwds/debug_extension_mv3/web/background.dart | 1 - .../web/debug_session.dart | 55 ++++++++++++++--- dwds/debug_extension_mv3/web/messaging.dart | 59 +++++++++++++++++++ 3 files changed, 106 insertions(+), 9 deletions(-) diff --git a/dwds/debug_extension_mv3/web/background.dart b/dwds/debug_extension_mv3/web/background.dart index 87cd29f1f..72728fd34 100644 --- a/dwds/debug_extension_mv3/web/background.dart +++ b/dwds/debug_extension_mv3/web/background.dart @@ -48,7 +48,6 @@ void _registerListeners() { chrome.action.onClicked.addListener(allowInterop(_startDebugSession)); } -// TODO(elliette): Start a debug session instead. Future _startDebugSession(Tab currentTab) async { final tabId = currentTab.id; final debugInfo = await _fetchDebugInfo(tabId); diff --git a/dwds/debug_extension_mv3/web/debug_session.dart b/dwds/debug_extension_mv3/web/debug_session.dart index 4e265c392..8ac6ee6bd 100644 --- a/dwds/debug_extension_mv3/web/debug_session.dart +++ b/dwds/debug_extension_mv3/web/debug_session.dart @@ -27,6 +27,7 @@ import 'chrome_api.dart'; import 'data_serializers.dart'; import 'data_types.dart'; import 'logger.dart'; +import 'messaging.dart'; import 'storage.dart'; import 'utils.dart'; import 'web_api.dart'; @@ -115,6 +116,14 @@ String _translateChromeError(String chromeErrorMessage) { Future _onDebuggerEvent( Debuggee source, String method, Object? params) async { + final externalExtensions = debugEventsForExternalExtensions[method] ?? []; + if (externalExtensions.isNotEmpty) { + _forwardMessageToExternalExtensions( + debugEventMessage(method: method, params: params, tabId: source.tabId), + extensionIds: externalExtensions, + ); + } + if (method == 'Runtime.executionContextCreated') { return _maybeConnectToDwds(source.tabId, params); } @@ -194,14 +203,18 @@ void _routeDwdsEvent(String eventData, SocketClient client, int tabId) { final message = serializers.deserialize(jsonDecode(eventData)); if (message is ExtensionRequest) { _forwardDwdsEventToChromeDebugger(message, client, tabId); - } else if (message is ExtensionEvent) { - switch (message.method) { - case 'dwds.encodedUri': - // TODO(elliette): Forward to external extensions. - break; - case 'dwds.devtoolsUri': - _openDevTools(message.params, dartTabId: tabId); - break; + } + if (message is ExtensionEvent) { + final method = message.method; + final params = message.params; + final externalExtensions = dwdsEventsForExternalExtensions[method] ?? []; + if (externalExtensions.isNotEmpty) { + _forwardMessageToExternalExtensions( + dwdsEventMessage(method: method, params: params, tabId: tabId), + extensionIds: externalExtensions); + } + if (method == 'dwds.devtoolsUri') { + _openDevTools(params, dartTabId: tabId); } } } @@ -243,6 +256,32 @@ void _forwardChromeDebuggerEventToDwds( } } +void _forwardMessageToExternalExtensions( + ExternalExtensionMessage message, { + required List extensionIds, +}) { + for (final extensionId in extensionIds) { + try { + chrome.runtime.sendMessage( + extensionId, + message, + /* options */ null, + allowInterop(([e]) { + if (e == null) { + // Error sending message: + final errorMessage = + chrome.runtime.lastError?.message ?? 'Unknown error.'; + debugWarn( + 'Error forwarding ${message.name} to $extensionId: $errorMessage'); + } + }), + ); + } catch (error) { + debugWarn('Error forwarding ${message.name} to $extensionId: $error'); + } + } +} + void _openDevTools(String devToolsUrl, {required int dartTabId}) async { if (devToolsUrl.isEmpty) { debugError('DevTools URL is empty.'); diff --git a/dwds/debug_extension_mv3/web/messaging.dart b/dwds/debug_extension_mv3/web/messaging.dart index 8041e4ade..79a0f5e77 100644 --- a/dwds/debug_extension_mv3/web/messaging.dart +++ b/dwds/debug_extension_mv3/web/messaging.dart @@ -29,6 +29,8 @@ enum MessageType { } } +// This message is used for communication within the extension itself (eg, for +// the service worker to communicate with the content scripts). class Message { final Script to; final Script from; @@ -89,3 +91,60 @@ void interceptMessage({ 'Error intercepting $expectedType from $expectedSender to $expectedRecipient: $error'); } } + +const _angularDartDevToolsId = 'nbkbficgbembimioedhceniahniffgpl'; + +// A map of DWDS events to the IDs of the external extensions that those events +// should be forwarded to. This is only used to forward outgoing messages. +// Incoming messages are restricted by externally_connectable in manifest.json. +final dwdsEventsForExternalExtensions = { + 'dwds.encodedUri': [_angularDartDevToolsId], +}; + +// A map of chrome.debugger events to the IDs of the external extensions that +// those events should be forwarded to. This is only used to forward outgoing +// messages. Incoming messages are restricted by externally_connectable in +// manifest.json. +final debugEventsForExternalExtensions = { + 'Overlay.inspectNodeRequested': [_angularDartDevToolsId], +}; + +ExternalExtensionMessage debugEventMessage({ + required String method, + required dynamic params, + required int tabId, +}) => + ExternalExtensionMessage( + name: 'chrome.debugger.event', + tabId: tabId, + options: DebugEvent(method: method, params: params), + ); + +ExternalExtensionMessage dwdsEventMessage({ + required String method, + required dynamic params, + required int tabId, +}) => + ExternalExtensionMessage( + name: method, + tabId: tabId, + options: params, + ); + +// This message is used for cross-extension communication (eg, to send messages +// between this extension and the AngularDart DevTools extension.) +@JS() +@anonymous +class ExternalExtensionMessage { + external int get tabId; + external String get name; + external dynamic get options; + external factory ExternalExtensionMessage( + {required int tabId, required String name, required dynamic options}); +} + +@JS() +@anonymous +class DebugEvent { + external factory DebugEvent({String method, Object? params}); +} From d622817f105091997ffa9a5c602d38f7dfbfeeb0 Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Wed, 4 Jan 2023 17:15:29 -0800 Subject: [PATCH 2/9] Wip --- dwds/debug_extension_mv3/web/background.dart | 2 + dwds/debug_extension_mv3/web/chrome_api.dart | 2 + .../web/cross_extension_communication.dart | 172 ++++++++++++++++++ .../web/data_serializers.dart | 1 + .../web/debug_session.dart | 51 +----- dwds/debug_extension_mv3/web/messaging.dart | 57 ------ dwds/debug_extension_mv3/web/storage.dart | 7 +- 7 files changed, 190 insertions(+), 102 deletions(-) create mode 100644 dwds/debug_extension_mv3/web/cross_extension_communication.dart diff --git a/dwds/debug_extension_mv3/web/background.dart b/dwds/debug_extension_mv3/web/background.dart index 72728fd34..754c55e71 100644 --- a/dwds/debug_extension_mv3/web/background.dart +++ b/dwds/debug_extension_mv3/web/background.dart @@ -17,6 +17,7 @@ import 'chrome_api.dart'; import 'lifeline_ports.dart'; import 'logger.dart'; import 'messaging.dart'; +import 'cross_extension_communication.dart'; import 'storage.dart'; import 'utils.dart'; import 'web_api.dart'; @@ -29,6 +30,7 @@ void main() { void _registerListeners() { chrome.runtime.onMessage.addListener(allowInterop(_handleRuntimeMessages)); + chrome.runtime.onMessageExternal.addListener(allowInterop(handleMessagesFromExternalExtensions)); chrome.tabs.onRemoved .addListener(allowInterop((tabId, _) => maybeRemoveLifelinePort(tabId))); // Update the extension icon on tab navigation: diff --git a/dwds/debug_extension_mv3/web/chrome_api.dart b/dwds/debug_extension_mv3/web/chrome_api.dart index b3468f7f3..038b93876 100644 --- a/dwds/debug_extension_mv3/web/chrome_api.dart +++ b/dwds/debug_extension_mv3/web/chrome_api.dart @@ -174,6 +174,8 @@ class Runtime { external ConnectionHandler get onConnect; external OnMessageHandler get onMessage; + + external OnMessageHandler get onMessageExternal; } @JS() diff --git a/dwds/debug_extension_mv3/web/cross_extension_communication.dart b/dwds/debug_extension_mv3/web/cross_extension_communication.dart new file mode 100644 index 000000000..6eaeeb863 --- /dev/null +++ b/dwds/debug_extension_mv3/web/cross_extension_communication.dart @@ -0,0 +1,172 @@ +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// 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. + +@JS() +library cross_extension_communication; + +import 'dart:convert'; + +import 'package:dwds/data/serializers.dart'; +import 'package:js/js.dart'; + +import 'debug_session.dart'; +import 'logger.dart'; +import 'chrome_api.dart'; +import 'web_api.dart'; +import 'storage.dart'; +import 'debug_session.dart'; + +const _angularDartDevToolsId = 'nbkbficgbembimioedhceniahniffgpl'; + +// A map of events to the IDs of the external extensions that +// those events should be forwarded to. This is only used to forward outgoing +// messages. Incoming messages are restricted by externally_connectable in +// manifest.json. +final _eventsForExternalExtensions = { + 'Overlay.inspectNodeRequested': [_angularDartDevToolsId], + 'dwds.encodedUri': [_angularDartDevToolsId], +}; + +void handleMessagesFromExternalExtensions( + dynamic jsRequest, MessageSender sender, Function sendResponse) async { + if (jsRequest == null) return; + final message = jsRequest as ExternalExtensionMessage; + if (message.name == 'chrome.debugger.sendCommand') { + try { + final options = message.options as SendCommandOptions; + chrome.debugger.sendCommand( + Debuggee(tabId: message.tabId), + options.method, + options.commandParams, + allowInterop(([e]) { + // No arguments indicate that an error occurred. + if (e == null) { + sendResponse(ErrorResponse() + ..error = JSON.stringify(chrome.runtime.lastError)); + } else { + sendResponse(e); + } + } + )); + } catch (e) { + sendResponse(ErrorResponse()..error = '$e'); + } + } else if (message.name == 'dwds.encodedUri') { + final encodedUri = await fetchStorageObject( + type: StorageObject.encodedUri, tabId: message.tabId); + sendResponse(encodedUri ?? ''); + } else if (message.name == 'dwds.startDebugging') { + attachDebugger(message.tabId); + sendResponse(true); + } else { + sendResponse( + ErrorResponse()..error = 'Unknown message name: ${message.name}'); + } +} + +void maybeForwardMessageToExternalExtensions( + {required String method, required dynamic params, required int tabId}) { + final extensionIds = _eventsForExternalExtensions[method]; + if (extensionIds == null) return; + + final message = method.startsWith('dwds') + ? _dwdsEventMessage(method: method, params: params, tabId: tabId) + : _debugEventMessage(method: method, params: params, tabId: tabId); + + for (final extensionId in extensionIds) { + _forwardMessageToExternalExtension(message, recipient: extensionId); + } +} + +void _forwardMessageToExternalExtension( + ExternalExtensionMessage message, { + required String recipient, +}) { + try { + chrome.runtime.sendMessage( + recipient, + message, + /* options */ null, + allowInterop(([e]) { + if (e == null) { + // Error sending message: + final errorMessage = + chrome.runtime.lastError?.message ?? 'Unknown error.'; + debugWarn( + 'Error forwarding ${message.name} to $recipient: $errorMessage'); + } + }), + ); + } catch (error) { + debugWarn('Error forwarding ${message.name} to $recipient: $error'); + } +} + +ExternalExtensionMessage _debugEventMessage({ + required String method, + required dynamic params, + required int tabId, +}) => + ExternalExtensionMessage( + name: 'chrome.debugger.event', + tabId: tabId, + options: DebugEvent(method: method, params: params), + ); + +ExternalExtensionMessage _dwdsEventMessage({ + required String method, + required dynamic params, + required int tabId, +}) => + ExternalExtensionMessage( + name: method, + tabId: tabId, + options: params, + ); + +// This message is used for cross-extension communication (eg, to send messages +// between this extension and the AngularDart DevTools extension.) +@JS() +@anonymous +class ExternalExtensionMessage { + external int get tabId; + external String get name; + external dynamic get options; + external factory ExternalExtensionMessage( + {required int tabId, required String name, required dynamic options}); +} + +@JS() +@anonymous +class DebugEvent { + external factory DebugEvent({String method, Object? params}); +} + +@JS() +@anonymous +class SendCommandOptions { + external String get method; + external Object get commandParams; +} + +@JS() +@anonymous +class Request { + external String get dartAppId; + external String get sender; + external int get tabId; + external String get name; + external dynamic get options; + external String get warning; + external String get message; + external factory Request( + {required int tabId, required String name, required dynamic options}); +} + +@JS() +@anonymous +class ErrorResponse { + external set error(String error); +} + diff --git a/dwds/debug_extension_mv3/web/data_serializers.dart b/dwds/debug_extension_mv3/web/data_serializers.dart index 1cb6268ae..dd7787619 100644 --- a/dwds/debug_extension_mv3/web/data_serializers.dart +++ b/dwds/debug_extension_mv3/web/data_serializers.dart @@ -18,6 +18,7 @@ part 'data_serializers.g.dart'; DebugInfo, DevToolsOpener, DevToolsRequest, + EncodedUri, ExtensionEvent, ExtensionRequest, ExtensionResponse, diff --git a/dwds/debug_extension_mv3/web/debug_session.dart b/dwds/debug_extension_mv3/web/debug_session.dart index 8ac6ee6bd..04e4109cc 100644 --- a/dwds/debug_extension_mv3/web/debug_session.dart +++ b/dwds/debug_extension_mv3/web/debug_session.dart @@ -24,10 +24,10 @@ import 'package:sse/client/sse_client.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; import 'chrome_api.dart'; +import 'cross_extension_communication.dart'; import 'data_serializers.dart'; import 'data_types.dart'; import 'logger.dart'; -import 'messaging.dart'; import 'storage.dart'; import 'utils.dart'; import 'web_api.dart'; @@ -116,13 +116,8 @@ String _translateChromeError(String chromeErrorMessage) { Future _onDebuggerEvent( Debuggee source, String method, Object? params) async { - final externalExtensions = debugEventsForExternalExtensions[method] ?? []; - if (externalExtensions.isNotEmpty) { - _forwardMessageToExternalExtensions( - debugEventMessage(method: method, params: params, tabId: source.tabId), - extensionIds: externalExtensions, - ); - } + maybeForwardMessageToExternalExtensions( + method: method, params: params, tabId: source.tabId); if (method == 'Runtime.executionContextCreated') { return _maybeConnectToDwds(source.tabId, params); @@ -205,16 +200,10 @@ void _routeDwdsEvent(String eventData, SocketClient client, int tabId) { _forwardDwdsEventToChromeDebugger(message, client, tabId); } if (message is ExtensionEvent) { - final method = message.method; - final params = message.params; - final externalExtensions = dwdsEventsForExternalExtensions[method] ?? []; - if (externalExtensions.isNotEmpty) { - _forwardMessageToExternalExtensions( - dwdsEventMessage(method: method, params: params, tabId: tabId), - extensionIds: externalExtensions); - } - if (method == 'dwds.devtoolsUri') { - _openDevTools(params, dartTabId: tabId); + maybeForwardMessageToExternalExtensions( + method: message.method, params: message.params, tabId: tabId); + if (message.method == 'dwds.devtoolsUri') { + _openDevTools(message.params, dartTabId: tabId); } } } @@ -256,32 +245,6 @@ void _forwardChromeDebuggerEventToDwds( } } -void _forwardMessageToExternalExtensions( - ExternalExtensionMessage message, { - required List extensionIds, -}) { - for (final extensionId in extensionIds) { - try { - chrome.runtime.sendMessage( - extensionId, - message, - /* options */ null, - allowInterop(([e]) { - if (e == null) { - // Error sending message: - final errorMessage = - chrome.runtime.lastError?.message ?? 'Unknown error.'; - debugWarn( - 'Error forwarding ${message.name} to $extensionId: $errorMessage'); - } - }), - ); - } catch (error) { - debugWarn('Error forwarding ${message.name} to $extensionId: $error'); - } - } -} - void _openDevTools(String devToolsUrl, {required int dartTabId}) async { if (devToolsUrl.isEmpty) { debugError('DevTools URL is empty.'); diff --git a/dwds/debug_extension_mv3/web/messaging.dart b/dwds/debug_extension_mv3/web/messaging.dart index 79a0f5e77..c34f8c3a3 100644 --- a/dwds/debug_extension_mv3/web/messaging.dart +++ b/dwds/debug_extension_mv3/web/messaging.dart @@ -91,60 +91,3 @@ void interceptMessage({ 'Error intercepting $expectedType from $expectedSender to $expectedRecipient: $error'); } } - -const _angularDartDevToolsId = 'nbkbficgbembimioedhceniahniffgpl'; - -// A map of DWDS events to the IDs of the external extensions that those events -// should be forwarded to. This is only used to forward outgoing messages. -// Incoming messages are restricted by externally_connectable in manifest.json. -final dwdsEventsForExternalExtensions = { - 'dwds.encodedUri': [_angularDartDevToolsId], -}; - -// A map of chrome.debugger events to the IDs of the external extensions that -// those events should be forwarded to. This is only used to forward outgoing -// messages. Incoming messages are restricted by externally_connectable in -// manifest.json. -final debugEventsForExternalExtensions = { - 'Overlay.inspectNodeRequested': [_angularDartDevToolsId], -}; - -ExternalExtensionMessage debugEventMessage({ - required String method, - required dynamic params, - required int tabId, -}) => - ExternalExtensionMessage( - name: 'chrome.debugger.event', - tabId: tabId, - options: DebugEvent(method: method, params: params), - ); - -ExternalExtensionMessage dwdsEventMessage({ - required String method, - required dynamic params, - required int tabId, -}) => - ExternalExtensionMessage( - name: method, - tabId: tabId, - options: params, - ); - -// This message is used for cross-extension communication (eg, to send messages -// between this extension and the AngularDart DevTools extension.) -@JS() -@anonymous -class ExternalExtensionMessage { - external int get tabId; - external String get name; - external dynamic get options; - external factory ExternalExtensionMessage( - {required int tabId, required String name, required dynamic options}); -} - -@JS() -@anonymous -class DebugEvent { - external factory DebugEvent({String method, Object? params}); -} diff --git a/dwds/debug_extension_mv3/web/storage.dart b/dwds/debug_extension_mv3/web/storage.dart index 5ba186005..3773002fc 100644 --- a/dwds/debug_extension_mv3/web/storage.dart +++ b/dwds/debug_extension_mv3/web/storage.dart @@ -17,7 +17,8 @@ import 'logger.dart'; enum StorageObject { debugInfo, - devToolsOpener; + devToolsOpener, + encodedUri; String get keyName { switch (this) { @@ -25,6 +26,8 @@ enum StorageObject { return 'debugInfo'; case StorageObject.devToolsOpener: return 'devToolsOpener'; + case StorageObject.encodedUri: + return 'encodedUri'; } } @@ -34,6 +37,8 @@ enum StorageObject { return Persistance.sessionOnly; case StorageObject.devToolsOpener: return Persistance.acrossSessions; + case StorageObject.encodedUri: + return Persistance.sessionOnly; } } } From 18e567d68b29a0511c3e6259c1079242519eff6f Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Wed, 4 Jan 2023 17:17:09 -0800 Subject: [PATCH 3/9] forgot to save --- .../web/cross_extension_communication.dart | 52 ++++++++----------- 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/dwds/debug_extension_mv3/web/cross_extension_communication.dart b/dwds/debug_extension_mv3/web/cross_extension_communication.dart index 6eaeeb863..f7b4e0494 100644 --- a/dwds/debug_extension_mv3/web/cross_extension_communication.dart +++ b/dwds/debug_extension_mv3/web/cross_extension_communication.dart @@ -5,9 +5,6 @@ @JS() library cross_extension_communication; -import 'dart:convert'; - -import 'package:dwds/data/serializers.dart'; import 'package:js/js.dart'; import 'debug_session.dart'; @@ -15,7 +12,6 @@ import 'logger.dart'; import 'chrome_api.dart'; import 'web_api.dart'; import 'storage.dart'; -import 'debug_session.dart'; const _angularDartDevToolsId = 'nbkbficgbembimioedhceniahniffgpl'; @@ -28,12 +24,27 @@ final _eventsForExternalExtensions = { 'dwds.encodedUri': [_angularDartDevToolsId], }; -void handleMessagesFromExternalExtensions( +void handleMessagesFromAngularDartDevTools( dynamic jsRequest, MessageSender sender, Function sendResponse) async { if (jsRequest == null) return; final message = jsRequest as ExternalExtensionMessage; if (message.name == 'chrome.debugger.sendCommand') { - try { + _forwardCommandToChromeDebugger(message, sendResponse); + } else if (message.name == 'dwds.encodedUri') { + final encodedUri = await fetchStorageObject( + type: StorageObject.encodedUri, tabId: message.tabId); + sendResponse(encodedUri ?? ''); + } else if (message.name == 'dwds.startDebugging') { + attachDebugger(message.tabId); + sendResponse(true); + } else { + sendResponse( + ErrorResponse()..error = 'Unknown message name: ${message.name}'); + } +} + +void _forwardCommandToChromeDebugger(ExternalExtensionMessage message, Function sendResponse) { + try { final options = message.options as SendCommandOptions; chrome.debugger.sendCommand( Debuggee(tabId: message.tabId), @@ -52,17 +63,10 @@ void handleMessagesFromExternalExtensions( } catch (e) { sendResponse(ErrorResponse()..error = '$e'); } - } else if (message.name == 'dwds.encodedUri') { - final encodedUri = await fetchStorageObject( - type: StorageObject.encodedUri, tabId: message.tabId); - sendResponse(encodedUri ?? ''); - } else if (message.name == 'dwds.startDebugging') { - attachDebugger(message.tabId); - sendResponse(true); - } else { - sendResponse( - ErrorResponse()..error = 'Unknown message name: ${message.name}'); - } +} + +void _respondWithEncodedUri() { + } void maybeForwardMessageToExternalExtensions( @@ -150,20 +154,6 @@ class SendCommandOptions { external Object get commandParams; } -@JS() -@anonymous -class Request { - external String get dartAppId; - external String get sender; - external int get tabId; - external String get name; - external dynamic get options; - external String get warning; - external String get message; - external factory Request( - {required int tabId, required String name, required dynamic options}); -} - @JS() @anonymous class ErrorResponse { From 9201c2d9cbd5e0432b63cccd9144217b1a41992f Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Thu, 5 Jan 2023 10:49:25 -0800 Subject: [PATCH 4/9] Add cross-extension communication with AngularDart DevTools --- dwds/debug_extension_mv3/web/background.dart | 8 +- .../web/cross_extension_communication.dart | 125 +++++++++--------- .../web/data_serializers.g.dart | 1 + dwds/debug_extension_mv3/web/data_types.dart | 10 ++ .../debug_extension_mv3/web/data_types.g.dart | 118 +++++++++++++++++ .../web/debug_session.dart | 4 +- dwds/debug_extension_mv3/web/manifest.json | 5 + 7 files changed, 201 insertions(+), 70 deletions(-) diff --git a/dwds/debug_extension_mv3/web/background.dart b/dwds/debug_extension_mv3/web/background.dart index 754c55e71..43ff6ebc5 100644 --- a/dwds/debug_extension_mv3/web/background.dart +++ b/dwds/debug_extension_mv3/web/background.dart @@ -29,8 +29,12 @@ void main() { } void _registerListeners() { - chrome.runtime.onMessage.addListener(allowInterop(_handleRuntimeMessages)); - chrome.runtime.onMessageExternal.addListener(allowInterop(handleMessagesFromExternalExtensions)); + chrome.runtime.onMessage.addListener( + allowInterop(_handleRuntimeMessages), + ); + chrome.runtime.onMessageExternal.addListener( + allowInterop(handleMessagesFromAngularDartDevTools), + ); chrome.tabs.onRemoved .addListener(allowInterop((tabId, _) => maybeRemoveLifelinePort(tabId))); // Update the extension icon on tab navigation: diff --git a/dwds/debug_extension_mv3/web/cross_extension_communication.dart b/dwds/debug_extension_mv3/web/cross_extension_communication.dart index f7b4e0494..8be22576e 100644 --- a/dwds/debug_extension_mv3/web/cross_extension_communication.dart +++ b/dwds/debug_extension_mv3/web/cross_extension_communication.dart @@ -7,21 +7,19 @@ library cross_extension_communication; import 'package:js/js.dart'; +import 'chrome_api.dart'; +import 'data_types.dart'; import 'debug_session.dart'; import 'logger.dart'; -import 'chrome_api.dart'; -import 'web_api.dart'; import 'storage.dart'; +import 'web_api.dart'; const _angularDartDevToolsId = 'nbkbficgbembimioedhceniahniffgpl'; -// A map of events to the IDs of the external extensions that -// those events should be forwarded to. This is only used to forward outgoing -// messages. Incoming messages are restricted by externally_connectable in -// manifest.json. -final _eventsForExternalExtensions = { - 'Overlay.inspectNodeRequested': [_angularDartDevToolsId], - 'dwds.encodedUri': [_angularDartDevToolsId], +// A set of events to forward to the AngularDart DevTools extension. +final _eventsForAngularDartDevTools = { + 'Overlay.inspectNodeRequested', + 'dwds.encodedUri', }; void handleMessagesFromAngularDartDevTools( @@ -31,9 +29,7 @@ void handleMessagesFromAngularDartDevTools( if (message.name == 'chrome.debugger.sendCommand') { _forwardCommandToChromeDebugger(message, sendResponse); } else if (message.name == 'dwds.encodedUri') { - final encodedUri = await fetchStorageObject( - type: StorageObject.encodedUri, tabId: message.tabId); - sendResponse(encodedUri ?? ''); + _respondWithEncodedUri(message.tabId, sendResponse); } else if (message.name == 'dwds.startDebugging') { attachDebugger(message.tabId); sendResponse(true); @@ -43,67 +39,65 @@ void handleMessagesFromAngularDartDevTools( } } -void _forwardCommandToChromeDebugger(ExternalExtensionMessage message, Function sendResponse) { - try { - final options = message.options as SendCommandOptions; - chrome.debugger.sendCommand( - Debuggee(tabId: message.tabId), - options.method, - options.commandParams, - allowInterop(([e]) { - // No arguments indicate that an error occurred. - if (e == null) { - sendResponse(ErrorResponse() - ..error = JSON.stringify(chrome.runtime.lastError)); - } else { - sendResponse(e); - } - } - )); - } catch (e) { - sendResponse(ErrorResponse()..error = '$e'); - } -} - -void _respondWithEncodedUri() { - -} - -void maybeForwardMessageToExternalExtensions( +void maybeForwardMessageToAngularDartDevTools( {required String method, required dynamic params, required int tabId}) { - final extensionIds = _eventsForExternalExtensions[method]; - if (extensionIds == null) return; + if (!_eventsForAngularDartDevTools.contains(method)) return; final message = method.startsWith('dwds') ? _dwdsEventMessage(method: method, params: params, tabId: tabId) : _debugEventMessage(method: method, params: params, tabId: tabId); - for (final extensionId in extensionIds) { - _forwardMessageToExternalExtension(message, recipient: extensionId); - } + _forwardMessageToAngularDartDevTools(message); } -void _forwardMessageToExternalExtension( - ExternalExtensionMessage message, { - required String recipient, -}) { +void _forwardCommandToChromeDebugger( + ExternalExtensionMessage message, Function sendResponse) { try { - chrome.runtime.sendMessage( - recipient, - message, - /* options */ null, - allowInterop(([e]) { - if (e == null) { - // Error sending message: - final errorMessage = - chrome.runtime.lastError?.message ?? 'Unknown error.'; - debugWarn( - 'Error forwarding ${message.name} to $recipient: $errorMessage'); - } - }), + final options = message.options as SendCommandOptions; + chrome.debugger.sendCommand( + Debuggee(tabId: message.tabId), + options.method, + options.commandParams, + allowInterop( + ([result]) => _respondWithChromeResult(result, sendResponse)), ); - } catch (error) { - debugWarn('Error forwarding ${message.name} to $recipient: $error'); + } catch (e) { + sendResponse(ErrorResponse()..error = '$e'); + } +} + +void _respondWithChromeResult(Object? chromeResult, Function sendResponse) { + // No result indicates that an error occurred. + if (chromeResult == null) { + sendResponse(ErrorResponse() + ..error = JSON.stringify( + chrome.runtime.lastError ?? 'Unknown error.', + )); + } else { + sendResponse(chromeResult); + } +} + +void _respondWithEncodedUri(int tabId, Function sendResponse) async { + final encodedUri = await fetchStorageObject( + type: StorageObject.encodedUri, tabId: tabId); + sendResponse(encodedUri ?? ''); +} + +void _forwardMessageToAngularDartDevTools(ExternalExtensionMessage message) { + chrome.runtime.sendMessage( + _angularDartDevToolsId, + message, + /* options */ null, + allowInterop(([result]) => _checkForErrors(result, message.name)), + ); +} + +void _checkForErrors(Object? chromeResult, String messageName) { + // No result indicates that an error occurred. + if (chromeResult == null) { + final errorMessage = chrome.runtime.lastError?.message ?? 'Unknown error.'; + debugWarn('Error forwarding ${messageName}: $errorMessage'); } } @@ -129,8 +123,8 @@ ExternalExtensionMessage _dwdsEventMessage({ options: params, ); -// This message is used for cross-extension communication (eg, to send messages -// between this extension and the AngularDart DevTools extension.) +// This message is used for cross-extension communication between this extension +// and the AngularDart DevTools extension. @JS() @anonymous class ExternalExtensionMessage { @@ -159,4 +153,3 @@ class SendCommandOptions { class ErrorResponse { external set error(String error); } - diff --git a/dwds/debug_extension_mv3/web/data_serializers.g.dart b/dwds/debug_extension_mv3/web/data_serializers.g.dart index 5dc6bb9b1..b25e23b7c 100644 --- a/dwds/debug_extension_mv3/web/data_serializers.g.dart +++ b/dwds/debug_extension_mv3/web/data_serializers.g.dart @@ -11,6 +11,7 @@ Serializers _$serializers = (new Serializers().toBuilder() ..add(DebugInfo.serializer) ..add(DevToolsOpener.serializer) ..add(DevToolsRequest.serializer) + ..add(EncodedUri.serializer) ..add(ExtensionEvent.serializer) ..add(ExtensionRequest.serializer) ..add(ExtensionResponse.serializer) diff --git a/dwds/debug_extension_mv3/web/data_types.dart b/dwds/debug_extension_mv3/web/data_types.dart index 69df9d70a..120f62f4e 100644 --- a/dwds/debug_extension_mv3/web/data_types.dart +++ b/dwds/debug_extension_mv3/web/data_types.dart @@ -19,3 +19,13 @@ abstract class DevToolsOpener bool get newWindow; } + +abstract class EncodedUri implements Built { + static Serializer get serializer => _$encodedUriSerializer; + + factory EncodedUri([Function(EncodedUriBuilder) updates]) = _$EncodedUri; + + EncodedUri._(); + + String get uri; +} diff --git a/dwds/debug_extension_mv3/web/data_types.g.dart b/dwds/debug_extension_mv3/web/data_types.g.dart index cfdbbe5f5..bb19875ba 100644 --- a/dwds/debug_extension_mv3/web/data_types.g.dart +++ b/dwds/debug_extension_mv3/web/data_types.g.dart @@ -8,6 +8,7 @@ part of 'data_types.dart'; Serializer _$devToolsOpenerSerializer = new _$DevToolsOpenerSerializer(); +Serializer _$encodedUriSerializer = new _$EncodedUriSerializer(); class _$DevToolsOpenerSerializer implements StructuredSerializer { @@ -51,6 +52,45 @@ class _$DevToolsOpenerSerializer } } +class _$EncodedUriSerializer implements StructuredSerializer { + @override + final Iterable types = const [EncodedUri, _$EncodedUri]; + @override + final String wireName = 'EncodedUri'; + + @override + Iterable serialize(Serializers serializers, EncodedUri object, + {FullType specifiedType = FullType.unspecified}) { + final result = [ + 'uri', + serializers.serialize(object.uri, specifiedType: const FullType(String)), + ]; + + return result; + } + + @override + EncodedUri deserialize(Serializers serializers, Iterable serialized, + {FullType specifiedType = FullType.unspecified}) { + final result = new EncodedUriBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current! as String; + iterator.moveNext(); + final Object? value = iterator.current; + switch (key) { + case 'uri': + result.uri = serializers.deserialize(value, + specifiedType: const FullType(String))! as String; + break; + } + } + + return result.build(); + } +} + class _$DevToolsOpener extends DevToolsOpener { @override final bool newWindow; @@ -133,4 +173,82 @@ class DevToolsOpenerBuilder } } +class _$EncodedUri extends EncodedUri { + @override + final String uri; + + factory _$EncodedUri([void Function(EncodedUriBuilder)? updates]) => + (new EncodedUriBuilder()..update(updates))._build(); + + _$EncodedUri._({required this.uri}) : super._() { + BuiltValueNullFieldError.checkNotNull(uri, r'EncodedUri', 'uri'); + } + + @override + EncodedUri rebuild(void Function(EncodedUriBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + EncodedUriBuilder toBuilder() => new EncodedUriBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is EncodedUri && uri == other.uri; + } + + @override + int get hashCode { + return $jf($jc(0, uri.hashCode)); + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'EncodedUri')..add('uri', uri)) + .toString(); + } +} + +class EncodedUriBuilder implements Builder { + _$EncodedUri? _$v; + + String? _uri; + String? get uri => _$this._uri; + set uri(String? uri) => _$this._uri = uri; + + EncodedUriBuilder(); + + EncodedUriBuilder get _$this { + final $v = _$v; + if ($v != null) { + _uri = $v.uri; + _$v = null; + } + return this; + } + + @override + void replace(EncodedUri other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$EncodedUri; + } + + @override + void update(void Function(EncodedUriBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + EncodedUri build() => _build(); + + _$EncodedUri _build() { + final _$result = _$v ?? + new _$EncodedUri._( + uri: BuiltValueNullFieldError.checkNotNull( + uri, r'EncodedUri', 'uri')); + replace(_$result); + return _$result; + } +} + // ignore_for_file: always_put_control_body_on_new_line,always_specify_types,annotate_overrides,avoid_annotating_with_dynamic,avoid_as,avoid_catches_without_on_clauses,avoid_returning_this,deprecated_member_use_from_same_package,lines_longer_than_80_chars,no_leading_underscores_for_local_identifiers,omit_local_variable_types,prefer_expression_function_bodies,sort_constructors_first,test_types_in_equals,unnecessary_const,unnecessary_new,unnecessary_lambdas diff --git a/dwds/debug_extension_mv3/web/debug_session.dart b/dwds/debug_extension_mv3/web/debug_session.dart index 04e4109cc..71f71db4c 100644 --- a/dwds/debug_extension_mv3/web/debug_session.dart +++ b/dwds/debug_extension_mv3/web/debug_session.dart @@ -116,7 +116,7 @@ String _translateChromeError(String chromeErrorMessage) { Future _onDebuggerEvent( Debuggee source, String method, Object? params) async { - maybeForwardMessageToExternalExtensions( + maybeForwardMessageToAngularDartDevTools( method: method, params: params, tabId: source.tabId); if (method == 'Runtime.executionContextCreated') { @@ -200,7 +200,7 @@ void _routeDwdsEvent(String eventData, SocketClient client, int tabId) { _forwardDwdsEventToChromeDebugger(message, client, tabId); } if (message is ExtensionEvent) { - maybeForwardMessageToExternalExtensions( + maybeForwardMessageToAngularDartDevTools( method: message.method, params: message.params, tabId: tabId); if (message.method == 'dwds.devtoolsUri') { _openDevTools(message.params, dartTabId: tabId); diff --git a/dwds/debug_extension_mv3/web/manifest.json b/dwds/debug_extension_mv3/web/manifest.json index 5481fea40..e44ed5828 100644 --- a/dwds/debug_extension_mv3/web/manifest.json +++ b/dwds/debug_extension_mv3/web/manifest.json @@ -6,6 +6,11 @@ "action": { "default_icon": "static_assets/dart_dev.png" }, + "externally_connectable": { + "ids": [ + "nbkbficgbembimioedhceniahniffgpl" + ] + }, "permissions": [ "debugger", "notifications", From 306702d5f65adad45dce37971a06ec16c5c7322b Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Thu, 5 Jan 2023 10:52:36 -0800 Subject: [PATCH 5/9] Clean up --- dwds/debug_extension_mv3/web/background.dart | 2 +- dwds/debug_extension_mv3/web/messaging.dart | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/dwds/debug_extension_mv3/web/background.dart b/dwds/debug_extension_mv3/web/background.dart index 43ff6ebc5..12bece0aa 100644 --- a/dwds/debug_extension_mv3/web/background.dart +++ b/dwds/debug_extension_mv3/web/background.dart @@ -14,10 +14,10 @@ import 'package:js/js.dart'; import 'debug_session.dart'; import 'chrome_api.dart'; +import 'cross_extension_communication.dart'; import 'lifeline_ports.dart'; import 'logger.dart'; import 'messaging.dart'; -import 'cross_extension_communication.dart'; import 'storage.dart'; import 'utils.dart'; import 'web_api.dart'; diff --git a/dwds/debug_extension_mv3/web/messaging.dart b/dwds/debug_extension_mv3/web/messaging.dart index c34f8c3a3..8041e4ade 100644 --- a/dwds/debug_extension_mv3/web/messaging.dart +++ b/dwds/debug_extension_mv3/web/messaging.dart @@ -29,8 +29,6 @@ enum MessageType { } } -// This message is used for communication within the extension itself (eg, for -// the service worker to communicate with the content scripts). class Message { final Script to; final Script from; From 307cf5b3c7332280dc5c52dce617c1c6d2a638d9 Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Thu, 5 Jan 2023 10:56:50 -0800 Subject: [PATCH 6/9] Fix analyze error --- dwds/debug_extension_mv3/web/cross_extension_communication.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwds/debug_extension_mv3/web/cross_extension_communication.dart b/dwds/debug_extension_mv3/web/cross_extension_communication.dart index 8be22576e..6751c0bba 100644 --- a/dwds/debug_extension_mv3/web/cross_extension_communication.dart +++ b/dwds/debug_extension_mv3/web/cross_extension_communication.dart @@ -97,7 +97,7 @@ void _checkForErrors(Object? chromeResult, String messageName) { // No result indicates that an error occurred. if (chromeResult == null) { final errorMessage = chrome.runtime.lastError?.message ?? 'Unknown error.'; - debugWarn('Error forwarding ${messageName}: $errorMessage'); + debugWarn('Error forwarding $messageName: $errorMessage'); } } From e41820515d43f09c31df4f6e0e4a69f90a02d718 Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Fri, 6 Jan 2023 11:53:28 -0800 Subject: [PATCH 7/9] Respond to PR comments --- dwds/debug_extension_mv3/web/debug_session.dart | 3 +-- dwds/debug_extension_mv3/web/storage.dart | 15 ++------------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/dwds/debug_extension_mv3/web/debug_session.dart b/dwds/debug_extension_mv3/web/debug_session.dart index f806ebbae..1b7a00023 100644 --- a/dwds/debug_extension_mv3/web/debug_session.dart +++ b/dwds/debug_extension_mv3/web/debug_session.dart @@ -241,8 +241,7 @@ void _routeDwdsEvent(String eventData, SocketClient client, int tabId) { final message = serializers.deserialize(jsonDecode(eventData)); if (message is ExtensionRequest) { _forwardDwdsEventToChromeDebugger(message, client, tabId); - } - if (message is ExtensionEvent) { + } else if (message is ExtensionEvent) { maybeForwardMessageToAngularDartDevTools( method: message.method, params: message.params, tabId: tabId); if (message.method == 'dwds.devtoolsUri') { diff --git a/dwds/debug_extension_mv3/web/storage.dart b/dwds/debug_extension_mv3/web/storage.dart index 3773002fc..31a3fd228 100644 --- a/dwds/debug_extension_mv3/web/storage.dart +++ b/dwds/debug_extension_mv3/web/storage.dart @@ -20,17 +20,6 @@ enum StorageObject { devToolsOpener, encodedUri; - String get keyName { - switch (this) { - case StorageObject.debugInfo: - return 'debugInfo'; - case StorageObject.devToolsOpener: - return 'devToolsOpener'; - case StorageObject.encodedUri: - return 'encodedUri'; - } - } - Persistance get persistance { switch (this) { case StorageObject.debugInfo: @@ -108,6 +97,6 @@ StorageArea _getStorageArea(Persistance persistance) { } String _createStorageKey(StorageObject type, int? tabId) { - if (tabId == null) return type.keyName; - return '$tabId-${type.keyName}'; + if (tabId == null) return type.name; + return '$tabId-${type.name}'; } From 7c1e8cb45eda00826966fa472af8dfbf130c6a27 Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Fri, 6 Jan 2023 12:02:40 -0800 Subject: [PATCH 8/9] Add a couple comments --- dwds/debug_extension_mv3/web/background.dart | 3 +++ .../web/cross_extension_communication.dart | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/dwds/debug_extension_mv3/web/background.dart b/dwds/debug_extension_mv3/web/background.dart index 770bd3dcd..10436c63e 100644 --- a/dwds/debug_extension_mv3/web/background.dart +++ b/dwds/debug_extension_mv3/web/background.dart @@ -33,6 +33,9 @@ void _registerListeners() { chrome.runtime.onMessage.addListener( allowInterop(_handleRuntimeMessages), ); + // The only extension allowed to send messages to this extension is the + // AngularDart DevTools extension. Its permission is set in the manifest.json + // externally_connectable field. chrome.runtime.onMessageExternal.addListener( allowInterop(handleMessagesFromAngularDartDevTools), ); diff --git a/dwds/debug_extension_mv3/web/cross_extension_communication.dart b/dwds/debug_extension_mv3/web/cross_extension_communication.dart index dad86af2e..f62143bb4 100644 --- a/dwds/debug_extension_mv3/web/cross_extension_communication.dart +++ b/dwds/debug_extension_mv3/web/cross_extension_communication.dart @@ -14,6 +14,12 @@ import 'logger.dart'; import 'storage.dart'; import 'web_api.dart'; +// The only extension allowed to communicate with this extension is the +// AngularDart DevTools extension. +// +// This ID is used to send messages to AngularDart DevTools, while the +// externally_connectable field in the manifest.json allows AngularDart DevTools +// to send messages to this extension. const _angularDartDevToolsId = 'nbkbficgbembimioedhceniahniffgpl'; // A set of events to forward to the AngularDart DevTools extension. From 3a57763aacd114fed860562435922cbe0fa1b8ed Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Fri, 6 Jan 2023 15:07:45 -0800 Subject: [PATCH 9/9] Add TODO --- dwds/debug_extension_mv3/web/data_types.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dwds/debug_extension_mv3/web/data_types.dart b/dwds/debug_extension_mv3/web/data_types.dart index be4020f2a..5065e3e56 100644 --- a/dwds/debug_extension_mv3/web/data_types.dart +++ b/dwds/debug_extension_mv3/web/data_types.dart @@ -35,6 +35,8 @@ abstract class DevToolsOpener bool get newWindow; } +// TODO(elliette): Standardize on uri or url here and across DWDS, instead of a +// combination of both. abstract class EncodedUri implements Built { static Serializer get serializer => _$encodedUriSerializer;