Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
44 changes: 36 additions & 8 deletions dwds/debug_extension_mv3/web/background.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ void _registerListeners() {
allowInterop(handleMessagesFromAngularDartDevTools),
);
// Update the extension icon on tab navigation:
chrome.tabs.onActivated.addListener(allowInterop((ActiveInfo info) {
_updateIcon(info.tabId);
chrome.tabs.onActivated.addListener(allowInterop((ActiveInfo info) async {
await _updateIcon(info.tabId);
}));
chrome.windows.onFocusChanged.addListener(allowInterop((_) async {
final currentTab = await activeTab;
if (currentTab?.id != null) {
_updateIcon(currentTab!.id);
await _updateIcon(currentTab!.id);
}
}));
chrome.webNavigation.onCommitted
Expand Down Expand Up @@ -99,7 +99,7 @@ void _handleRuntimeMessages(
// Update the icon to show that a Dart app has been detected:
final currentTab = await activeTab;
if (currentTab?.id == dartTab.id) {
_setDebuggableIcon();
await _updateIcon(dartTab.id);
}
});

Expand All @@ -115,6 +115,26 @@ void _handleRuntimeMessages(
attachDebugger(tabId, trigger: Trigger.extensionPanel);
}
});

interceptMessage<String>(
message: jsRequest,
expectedType: MessageType.multipleAppsDetected,
expectedSender: Script.detector,
expectedRecipient: Script.background,
messageHandler: (String multipleAppsDetected) async {
final dartTab = sender.tab;
if (dartTab == null) {
debugWarn('Received multiple apps detected but tab is missing.');
return;
}
// Save the multiple apps info in storage:
await setStorageObject<String>(
type: StorageObject.multipleAppsDetected,
value: multipleAppsDetected,
tabId: dartTab.id,
);
_setWarningIcon();
});
}

void _detectNavigationAwayFromDartApp(NavigationInfo navigationInfo) async {
Expand All @@ -133,19 +153,27 @@ void _detectNavigationAwayFromDartApp(NavigationInfo navigationInfo) async {
}
}

void _updateIcon(int activeTabId) async {
Future<void> _updateIcon(int activeTabId) async {
final debugInfo = await _fetchDebugInfo(activeTabId);
if (debugInfo != null) {
_setDebuggableIcon();
} else {
if (debugInfo == null) {
_setDefaultIcon();
return;
}
final multipleApps = await fetchStorageObject<String>(
type: StorageObject.multipleAppsDetected,
tabId: activeTabId,
);
multipleApps == null ? _setDebuggableIcon() : _setWarningIcon();
}

void _setDebuggableIcon() {
setExtensionIcon(IconInfo(path: 'static_assets/dart.png'));
}

void _setWarningIcon() {
setExtensionIcon(IconInfo(path: 'static_assets/dart_warning.png'));
}

void _setDefaultIcon() {
final iconPath =
isDevMode ? 'static_assets/dart_dev.png' : 'static_assets/dart_grey.png';
Expand Down
27 changes: 23 additions & 4 deletions dwds/debug_extension_mv3/web/debug_session.dart
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,16 @@ void attachDebugger(int dartAppTabId, {required Trigger trigger}) async {
'Already debugging in ${existingDebuggerLocation.displayName}.',
);
}

// Determine if there are multiple apps in the tab:
final multipleApps = await fetchStorageObject<String>(
type: StorageObject.multipleAppsDetected,
tabId: dartAppTabId,
);
if (multipleApps != null) {
return _showWarningNotification(
'Dart debugging is not supported in a multi-app environment.',
);
}
// Verify that the user is authenticated:
final isAuthenticated = await _authenticateUser(dartAppTabId);
if (!isAuthenticated) return;
Expand Down Expand Up @@ -427,9 +436,19 @@ Future<void> _maybeCloseDevTools(int? devToolsTabId) async {
}

Future<void> _removeDebugSessionDataInStorage(int tabId) async {
// Remove the DevTools URI and encoded URI from storage:
await removeStorageObject(type: StorageObject.devToolsUri, tabId: tabId);
await removeStorageObject(type: StorageObject.encodedUri, tabId: tabId);
// Remove the DevTools URI, encoded URI, and multiple apps info from storage:
await removeStorageObject(
type: StorageObject.devToolsUri,
tabId: tabId,
);
await removeStorageObject(
type: StorageObject.encodedUri,
tabId: tabId,
);
await removeStorageObject(
type: StorageObject.multipleAppsDetected,
tabId: tabId,
);
}

void _removeDebugSession(_DebugSession debugSession) {
Expand Down
48 changes: 48 additions & 0 deletions dwds/debug_extension_mv3/web/detector.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import 'data_serializers.dart';
import 'logger.dart';
import 'messaging.dart';

const _multipleAppsAttribute = 'data-multiple-dart-apps';

void main() {
_registerListeners();
}
Expand All @@ -38,6 +40,7 @@ Future<void> _onDartAppReadyEvent(Event event) async {
body: debugInfo,
);
_sendAuthRequest(debugInfo);
_detectMultipleDartApps();
}
}

Expand All @@ -50,6 +53,51 @@ Future<void> _onDartAuthEvent(Event event) async {
);
}

void _detectMultipleDartApps() {
final documentElement = document.documentElement;
if (documentElement == null) return;

if (documentElement.hasAttribute(_multipleAppsAttribute)) {
_sendMessageToBackgroundScript(
type: MessageType.multipleAppsDetected,
body: 'true',
);
return;
}

final multipleAppsObserver =
MutationObserver(_detectMultipleDartAppsCallback);
multipleAppsObserver.observe(
documentElement,
attributeFilter: [_multipleAppsAttribute],
);
}

void _detectMultipleDartAppsCallback(
List<dynamic> mutations,
MutationObserver observer,
) {
for (var mutation in mutations) {
if (_isMultipleAppsMutation(mutation)) {
_sendMessageToBackgroundScript(
type: MessageType.multipleAppsDetected,
body: 'true',
);
observer.disconnect();
}
}
}

bool _isMultipleAppsMutation(dynamic mutation) {
final isAttributeMutation = hasProperty(mutation, 'type') &&
getProperty(mutation, 'type') == 'attributes';
if (isAttributeMutation) {
return hasProperty(mutation, 'attributeName') &&
getProperty(mutation, 'attributeName') == _multipleAppsAttribute;
}
return false;
}

// TODO(elliette): Remove once DWDS 17.0.0 is in Flutter stable. If we are on an
// older version of DWDS, then the debug info is not sent along with the ready
// event. Therefore we must read it from the Window object, which is slower.
Expand Down
3 changes: 2 additions & 1 deletion dwds/debug_extension_mv3/web/messaging.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ enum MessageType {
connectFailure,
debugInfo,
debugStateChange,
devToolsUrl;
devToolsUrl,
multipleAppsDetected;

factory MessageType.fromString(String value) {
return MessageType.values.byName(value);
Expand Down
32 changes: 29 additions & 3 deletions dwds/debug_extension_mv3/web/panel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,23 @@ const _lostConnectionMsg = 'Lost connection.';
const _connectionTimeoutMsg = 'Connection timed out.';
const _failedToConnectMsg = 'Failed to connect, please try again.';
const _pleaseAuthenticateMsg = 'Please re-authenticate and try again.';
const _multipleAppsMsg = 'Cannot debug multiple apps in a page.';

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

void main() {
void main() async {
unawaited(
_registerListeners().catchError((error) {
debugWarn('Error registering listeners in panel: $error');
}),
);
_setColorThemeToMatchChromeDevTools();
_maybeUpdateFileABugLink();
final multipleApps = await fetchStorageObject<String>(
type: StorageObject.multipleAppsDetected,
tabId: _tabId,
);
_maybeShowMultipleAppsWarning(multipleApps);
}

Future<void> _registerListeners() async {
Expand Down Expand Up @@ -120,6 +126,12 @@ void _handleStorageChanges(Object storageObj, String storageArea) {
tabId: _tabId,
changeHandler: _handleDevToolsUriChanges,
);
interceptStorageChange<String>(
storageObj: storageObj,
expectedType: StorageObject.multipleAppsDetected,
tabId: _tabId,
changeHandler: _maybeShowMultipleAppsWarning,
);
}

void _handleDebugInfoChanges(DebugInfo? debugInfo) async {
Expand All @@ -143,6 +155,16 @@ void _handleDevToolsUriChanges(String? devToolsUri) async {
}
}

void _maybeShowMultipleAppsWarning(String? multipleApps) async {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Future<void> (there are more like that below)

if (multipleApps != null) {
_showWarningBanner(_multipleAppsMsg);
} else {
if (_warningBannerIsVisible(message: _multipleAppsMsg)) {
_hideWarningBanner();
}
}
}

void _maybeUpdateFileABugLink() async {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Future<void>

final debugInfo = await fetchStorageObject<DebugInfo>(
type: StorageObject.debugInfo,
Expand Down Expand Up @@ -217,9 +239,13 @@ void _handleConnectFailure(ConnectFailureReason reason) {
_updateElementVisibility(_loadingSpinnerId, visible: false);
}

bool _warningBannerIsVisible() {
bool _warningBannerIsVisible({String? message}) {
final warningBanner = document.getElementById(_warningBannerId);
return warningBanner != null && warningBanner.classes.contains(_showClass);
final isVisible =
warningBanner != null && warningBanner.classes.contains(_showClass);
if (message == null || isVisible == false) return isVisible;
final warningMsg = document.getElementById(_warningMsgId);
return warningMsg?.innerHtml == message;
}

void _showWarningBanner(String message) {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 4 additions & 1 deletion dwds/debug_extension_mv3/web/storage.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ enum StorageObject {
devToolsOpener,
devToolsUri,
encodedUri,
isAuthenticated;
isAuthenticated,
multipleAppsDetected;

Persistance get persistance {
switch (this) {
Expand All @@ -35,6 +36,8 @@ enum StorageObject {
return Persistance.sessionOnly;
case StorageObject.isAuthenticated:
return Persistance.sessionOnly;
case StorageObject.multipleAppsDetected:
return Persistance.sessionOnly;
}
}
}
Expand Down
63 changes: 63 additions & 0 deletions dwds/test/puppeteer/extension_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,54 @@ void main() async {
expect(
queryParameters, containsPair('backgroundColor', isNotEmpty));
});

test('Trying to debug a page with multiple Dart apps shows warning',
() async {
final chromeDevToolsPage = await getChromeDevToolsPage(browser);
// There are no hooks for when a panel is added to Chrome DevTools,
// therefore we rely on a slight delay:
await Future.delayed(Duration(seconds: 1));
// Navigate to the Dart Debugger panel:
_tabLeft(chromeDevToolsPage);
if (isFlutterApp) {
_tabLeft(chromeDevToolsPage);
}
// Expect there to be no warning banner:
var warningMsg = await _evaluateInPanel<String>(browser,
panel: Panel.debugger,
jsExpression:
'document.querySelector("#warningMsg").innerHTML');
expect(warningMsg == 'Cannot debug multiple apps in a page.',
isFalse);
// Set the 'data-multiple-dart-apps' attribute on the DOM.
await appTab.evaluate(_setMultipleAppsAttributeJs);
final appTabId = await _getTabId(
context.appUrl,
worker: worker,
backgroundPage: backgroundPage,
);
// Expect multiple apps info to be saved in storage:
final storageKey = '$appTabId-multipleAppsDetected';
final multipleAppsDetected = await _fetchStorageObj<String>(
storageKey,
storageArea: 'session',
worker: worker,
backgroundPage: backgroundPage,
);
expect(multipleAppsDetected, equals('true'));
// Expect there to be a warning banner:
warningMsg = await _evaluateInPanel<String>(browser,
panel: Panel.debugger,
jsExpression:
'document.querySelector("#warningMsg").innerHTML');
await _takeScreenshot(
chromeDevToolsPage,
screenshotName:
'debuggerMultipleAppsDetected_${isFlutterApp ? 'flutterApp' : 'dartApp'}',
);
expect(
warningMsg, equals('Cannot debug multiple apps in a page.'));
});
});
}
});
Expand Down Expand Up @@ -813,6 +861,17 @@ Future<Page> _getPanelPage(
return await panelTarget.page;
}

Future<T> _evaluateInPanel<T>(
Browser browser, {
required Panel panel,
required String jsExpression,
}) async {
final panelPage = await _getPanelPage(browser, panel: panel);
final frames = panelPage.frames;
final mainFrame = frames[0];
return mainFrame.evaluate(jsExpression);
}

Future<ElementHandle?> _getPanelElement(
Browser browser, {
required Panel panel,
Expand Down Expand Up @@ -941,6 +1000,10 @@ String _getNotifications() {
''';
}

String _setMultipleAppsAttributeJs = '''
document.documentElement.setAttribute("data-multiple-dart-apps", true);
''';

// TODO(https://github.com/dart-lang/webdev/issues/1787): Compare to golden
// images. Currently golden comparison is not set up, since this is only run
// locally, not as part of our CI test suite.
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.