Skip to content

[MV3 Debug Extension] Show a warning if multiple Dart apps are in a single page #1976

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 21, 2023
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
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
50 changes: 38 additions & 12 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() {
Future<void> main() async {
unawaited(
_registerListeners().catchError((error) {
debugWarn('Error registering listeners in panel: $error');
}),
);
_setColorThemeToMatchChromeDevTools();
_maybeUpdateFileABugLink();
await _maybeUpdateFileABugLink();
final multipleApps = await fetchStorageObject<String>(
type: StorageObject.multipleAppsDetected,
tabId: _tabId,
);
_maybeShowMultipleAppsWarning(multipleApps);
}

Future<void> _registerListeners() async {
Expand All @@ -70,7 +76,7 @@ Future<void> _registerListeners() async {
}

void _handleRuntimeMessages(
dynamic jsRequest, MessageSender sender, Function sendResponse) async {
dynamic jsRequest, MessageSender sender, Function sendResponse) {
if (jsRequest is! String) return;

interceptMessage<DebugStateChange>(
Expand Down Expand Up @@ -120,9 +126,15 @@ 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 {
void _handleDebugInfoChanges(DebugInfo? debugInfo) {
if (debugInfo == null && _isDartApp) {
_isDartApp = false;
if (!_warningBannerIsVisible()) {
Expand All @@ -137,13 +149,23 @@ void _handleDebugInfoChanges(DebugInfo? debugInfo) async {
}
}

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

void _maybeUpdateFileABugLink() async {
void _maybeShowMultipleAppsWarning(String? multipleApps) {
if (multipleApps != null) {
_showWarningBanner(_multipleAppsMsg);
} else {
if (_warningBannerIsVisible(message: _multipleAppsMsg)) {
_hideWarningBanner();
}
}
}

Future<void> _maybeUpdateFileABugLink() async {
final debugInfo = await fetchStorageObject<DebugInfo>(
type: StorageObject.debugInfo,
tabId: _tabId,
Expand All @@ -157,7 +179,7 @@ void _maybeUpdateFileABugLink() async {
}
}

void _setColorThemeToMatchChromeDevTools() async {
void _setColorThemeToMatchChromeDevTools() {
final chromeTheme = chrome.devtools.panels.themeName;
final panelBody = document.getElementById(_panelBodyId);
if (chromeTheme == 'dark') {
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 All @@ -234,7 +260,7 @@ void _hideWarningBanner() {
warningBanner?.classes.remove(_showClass);
}

void _launchDebugConnection(Event _) async {
Future<void> _launchDebugConnection(Event _) async {
_updateElementVisibility(_launchDebugConnectionButtonId, visible: false);
_updateElementVisibility(_loadingSpinnerId, visible: true);
final json = jsonEncode(serializers.serialize(DebugStateChange((b) => b
Expand All @@ -245,10 +271,10 @@ void _launchDebugConnection(Event _) async {
body: json,
sender: Script.debuggerPanel,
recipient: Script.background);
_maybeHandleConnectionTimeout();
unawaited(_maybeHandleConnectionTimeout().catchError((_) {}));
}

void _maybeHandleConnectionTimeout() async {
Future<void> _maybeHandleConnectionTimeout() async {
_connecting = true;
await Future.delayed(Duration(seconds: 10));
if (_connecting == true) {
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
Loading