blob: da867791720794a5c458f2f085008720ab4b4f1f [file] [log] [blame]
// 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;
// TODO: https://github.com/dart-lang/webdev/issues/2508
// ignore: deprecated_member_use
import 'package:js/js.dart';
import 'chrome_api.dart';
import 'debug_session.dart';
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.
final _eventsForAngularDartDevTools = {
'Overlay.inspectNodeRequested',
'dwds.encodedUri',
};
Future<void> handleMessagesFromAngularDartDevTools(
dynamic jsRequest,
// ignore: avoid-unused-parameters
MessageSender sender,
Function sendResponse,
) async {
if (jsRequest == null) return;
final message = jsRequest as ExternalExtensionMessage;
if (message.name == 'chrome.debugger.sendCommand') {
_forwardCommandToChromeDebugger(message, sendResponse);
} else if (message.name == 'dwds.encodedUri') {
await _respondWithEncodedUri(message.tabId, sendResponse);
} else if (message.name == 'dwds.startDebugging') {
await attachDebugger(message.tabId, trigger: Trigger.angularDartDevTools);
sendResponse(true);
} else {
sendResponse(
ErrorResponse()..error = 'Unknown message name: ${message.name}',
);
}
}
void maybeForwardMessageToAngularDartDevTools({
required String method,
required dynamic params,
required int tabId,
}) {
if (!_eventsForAngularDartDevTools.contains(method)) return;
final message =
method.startsWith('dwds')
? _dwdsEventMessage(method: method, params: params, tabId: tabId)
: _debugEventMessage(method: method, params: params, tabId: tabId);
_forwardMessageToAngularDartDevTools(message);
}
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(
([result]) => _respondWithChromeResult(result, sendResponse),
),
);
} 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);
}
}
Future<void> _respondWithEncodedUri(int tabId, Function sendResponse) async {
final encodedUri = await fetchStorageObject<String>(
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');
}
}
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 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 ErrorResponse {
external set error(String error);
}