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
36 changes: 16 additions & 20 deletions dwds/debug_extension_mv3/web/debug_session.dart
Original file line number Diff line number Diff line change
Expand Up @@ -294,44 +294,52 @@ void _forwardChromeDebuggerEventToDwds(
}
}

void _openDevTools(String devToolsUrl, {required int dartAppTabId}) async {
if (devToolsUrl.isEmpty) {
debugError('DevTools URL is empty.');
void _openDevTools(String devToolsUri, {required int dartAppTabId}) async {
if (devToolsUri.isEmpty) {
debugError('DevTools URI is empty.');
return;
}
final debugSession = _debugSessionForTab(dartAppTabId, type: TabType.dartApp);
if (debugSession == null) {
debugError('Debug session not found.');
return;
}
// Send the DevTools URL to the extension panels:
_sendDevToolsUrlMessage(devToolsUrl, dartAppTabId: dartAppTabId);
// Save the DevTools URI so that the extension panels have access to it:
await setStorageObject(
type: StorageObject.devToolsUri,
value: devToolsUri,
tabId: dartAppTabId,
);
// Open a separate tab / window if triggered through the extension icon or
// through AngularDart DevTools:
if (debugSession.trigger == Trigger.extensionIcon ||
debugSession.trigger == Trigger.angularDartDevTools) {
final devToolsOpener = await fetchStorageObject<DevToolsOpener>(
type: StorageObject.devToolsOpener);
final devToolsTab = await createTab(
devToolsUrl,
devToolsUri,
inNewWindow: devToolsOpener?.newWindow ?? false,
);
debugSession.devToolsTabId = devToolsTab.id;
}
}

void _handleDebuggerDetach(Debuggee source, DetachReason reason) async {
final tabId = source.tabId;
debugLog(
'Debugger detached due to: $reason',
verbose: true,
prefix: '${source.tabId}',
prefix: '$tabId',
);
final debugSession = _debugSessionForTab(source.tabId, type: TabType.dartApp);
final debugSession = _debugSessionForTab(tabId, type: TabType.dartApp);
if (debugSession == null) return;
debugLog('Removing debug session...');
_removeDebugSession(debugSession);
// Notify the extension panels that the debug session has ended:
_sendStopDebuggingMessage(reason, dartAppTabId: source.tabId);
// Remove the DevTools URI and encoded URI from storage:
await removeStorageObject(type: StorageObject.devToolsUri, tabId: tabId);
await removeStorageObject(type: StorageObject.encodedUri, tabId: tabId);
// Maybe close the associated DevTools tab as well:
final devToolsTabId = debugSession.devToolsTabId;
if (devToolsTabId == null) return;
Expand Down Expand Up @@ -370,18 +378,6 @@ void sendConnectFailureMessage(ConnectFailureReason reason,
recipient: Script.debuggerPanel);
}

void _sendDevToolsUrlMessage(String devToolsUrl,
{required int dartAppTabId}) async {
final json = jsonEncode(serializers.serialize(DevToolsUrl((b) => b
..tabId = dartAppTabId
..url = devToolsUrl)));
sendRuntimeMessage(
type: MessageType.devToolsUrl,
body: json,
sender: Script.background,
recipient: Script.debuggerPanel);
}

void _sendStopDebuggingMessage(DetachReason reason,
{required int dartAppTabId}) async {
final json = jsonEncode(serializers.serialize(DebugStateChange((b) => b
Expand Down
72 changes: 44 additions & 28 deletions dwds/debug_extension_mv3/web/panel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,46 +39,35 @@ const showClass = 'show';
const warningBannerId = 'warningBanner';
const warningMsgId = 'warningMsg';

int get _tabId => chrome.devtools.inspectedWindow.tabId;

void main() {
_registerListeners();
_setColorThemeToMatchChromeDevTools();
_maybeUpdateFileABugLink();
}

void _registerListeners() {
chrome.storage.onChanged.addListener(allowInterop(_handleDebugInfoChanges));
chrome.storage.onChanged.addListener(allowInterop(_handleStorageChanges));
chrome.runtime.onMessage.addListener(allowInterop(_handleRuntimeMessages));
final launchDebugConnectionButton =
document.getElementById(launchDebugConnectionButtonId) as ButtonElement;
launchDebugConnectionButton.addEventListener('click', _launchDebugConnection);

_maybeInjectDevToolsIframe();
}

void _handleRuntimeMessages(
dynamic jsRequest, MessageSender sender, Function sendResponse) async {
if (jsRequest is! String) return;
final tabId = chrome.devtools.inspectedWindow.tabId;
interceptMessage<DevToolsUrl>(
message: jsRequest,
expectedType: MessageType.devToolsUrl,
expectedSender: Script.background,
expectedRecipient: Script.debuggerPanel,
messageHandler: (DevToolsUrl devToolsUrl) async {
if (devToolsUrl.tabId != tabId) {
debugWarn(
'Received DevTools URL, but Dart app tab does not match current tab.');
return;
}
connecting = false;
_injectDevToolsIframe(devToolsUrl.url);
});

interceptMessage<DebugStateChange>(
message: jsRequest,
expectedType: MessageType.debugStateChange,
expectedSender: Script.background,
expectedRecipient: Script.debuggerPanel,
messageHandler: (DebugStateChange debugStateChange) async {
if (debugStateChange.tabId != tabId) {
if (debugStateChange.tabId != _tabId) {
debugWarn(
'Received debug state change request, but Dart app tab does not match current tab.');
return;
Expand All @@ -95,8 +84,8 @@ void _handleRuntimeMessages(
expectedRecipient: Script.debuggerPanel,
messageHandler: (ConnectFailure connectFailure) async {
debugLog(
'Received connect failure for ${connectFailure.tabId} vs $tabId');
if (connectFailure.tabId != tabId) {
'Received connect failure for ${connectFailure.tabId} vs $_tabId');
if (connectFailure.tabId != _tabId) {
return;
}
connecting = false;
Expand All @@ -106,12 +95,25 @@ void _handleRuntimeMessages(
});
}

void _handleDebugInfoChanges(Object _, String storageArea) async {
void _handleStorageChanges(Object storageObj, String storageArea) {
// We only care about session storage objects:
if (storageArea != 'session') return;
final debugInfo = await fetchStorageObject<DebugInfo>(
type: StorageObject.debugInfo,
tabId: chrome.devtools.inspectedWindow.tabId,

interceptStorageChange<DebugInfo>(
storageObj: storageObj,
expectedType: StorageObject.debugInfo,
tabId: _tabId,
changeHandler: _handleDebugInfoChanges,
);
interceptStorageChange<String>(
storageObj: storageObj,
expectedType: StorageObject.devToolsUri,
tabId: _tabId,
changeHandler: _handleDevToolsUriChanges,
);
}

void _handleDebugInfoChanges(DebugInfo? debugInfo) async {
if (debugInfo == null && isDartApp) {
isDartApp = false;
_showWarningBanner('Dart app is no longer open.');
Expand All @@ -122,10 +124,16 @@ void _handleDebugInfoChanges(Object _, String storageArea) async {
}
}

void _handleDevToolsUriChanges(String? devToolsUri) async {
if (devToolsUri != null) {
_injectDevToolsIframe(devToolsUri);
}
}

void _maybeUpdateFileABugLink() async {
final debugInfo = await fetchStorageObject<DebugInfo>(
type: StorageObject.debugInfo,
tabId: chrome.devtools.inspectedWindow.tabId,
tabId: _tabId,
);
final isInternal = debugInfo?.isInternalBuild ?? false;
if (isInternal) {
Expand Down Expand Up @@ -204,9 +212,8 @@ void _hideWarningBanner() {
void _launchDebugConnection(Event _) async {
_updateElementVisibility(launchDebugConnectionButtonId, visible: false);
_updateElementVisibility(loadingSpinnerId, visible: true);
final dartAppTabId = chrome.devtools.inspectedWindow.tabId;
final json = jsonEncode(serializers.serialize(DebugStateChange((b) => b
..tabId = dartAppTabId
..tabId = _tabId
..newState = DebugStateChange.startDebugging)));
sendRuntimeMessage(
type: MessageType.debugStateChange,
Expand All @@ -224,15 +231,24 @@ void _maybeHandleConnectionTimeout() async {
}
}

void _injectDevToolsIframe(String devToolsUrl) {
void _maybeInjectDevToolsIframe() async {
final devToolsUri = await fetchStorageObject<String>(
type: StorageObject.devToolsUri, tabId: _tabId);
if (devToolsUri != null) {
_injectDevToolsIframe(devToolsUri);
}
}

void _injectDevToolsIframe(String devToolsUri) {
connecting = false;
final iframeContainer = document.getElementById(iframeContainerId);
if (iframeContainer == null) return;
final panelBody = document.getElementById(panelBodyId);
final panelType = panelBody?.getAttribute(panelAttribute) ?? 'debugger';
final iframe = document.createElement('iframe');
iframe.setAttribute(
'src',
'$devToolsUrl&embed=true&page=$panelType&backgroundColor=$devToolsBackgroundColor',
'$devToolsUri&embed=true&page=$panelType&backgroundColor=$devToolsBackgroundColor',
);
_hideWarningBanner();
_updateElementVisibility(landingPageId, visible: false);
Expand Down
30 changes: 30 additions & 0 deletions dwds/debug_extension_mv3/web/storage.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import 'logger.dart';
enum StorageObject {
debugInfo,
devToolsOpener,
devToolsUri,
encodedUri;

Persistance get persistance {
Expand All @@ -26,6 +27,8 @@ enum StorageObject {
return Persistance.sessionOnly;
case StorageObject.devToolsOpener:
return Persistance.acrossSessions;
case StorageObject.devToolsUri:
return Persistance.sessionOnly;
case StorageObject.encodedUri:
return Persistance.sessionOnly;
}
Expand Down Expand Up @@ -97,6 +100,33 @@ Future<bool> removeStorageObject<T>({required StorageObject type, int? tabId}) {
return completer.future;
}

void interceptStorageChange<T>({
required Object storageObj,
required StorageObject expectedType,
required void Function(T? storageObj) changeHandler,
int? tabId,
}) {
try {
final expectedStorageKey = _createStorageKey(expectedType, tabId);
final isExpected = hasProperty(storageObj, expectedStorageKey);
if (!isExpected) return;

final objProp = getProperty(storageObj, expectedStorageKey);
final json = getProperty(objProp, 'newValue') as String?;
T? decodedObj;
if (json == null || T == String) {
decodedObj = json as T?;
} else {
decodedObj = serializers.deserialize(jsonDecode(json)) as T?;
}
debugLog('Intercepted $expectedStorageKey change: $json');
return changeHandler(decodedObj);
} catch (error) {
debugError(
'Error intercepting storage object with type $expectedType: $error');
}
}

StorageArea _getStorageArea(Persistance persistance) {
switch (persistance) {
case Persistance.acrossSessions:
Expand Down
3 changes: 3 additions & 0 deletions dwds/test/puppeteer/extension_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,9 @@ void main() async {
serveDevTools: true,
isInternalBuild: true,
isFlutterApp: isFlutterApp,
// TODO(elliette): Figure out if there is a way to close and then
// re-open Chrome DevTools. That way we can test that a debug
// session lasts across Chrome DevTools being opened and closed.
openChromeDevTools: true,
);

Expand Down