Skip to content

Commit 8375cd6

Browse files
authored
Add popup for copying the app ID (#2299)
1 parent 63e09e5 commit 8375cd6

File tree

13 files changed

+395
-295
lines changed

13 files changed

+395
-295
lines changed

dwds/debug_extension_mv3/pubspec.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: mv3_extension
22
publish_to: none
3-
version: 2.0.0
3+
version: 2.1.0
44
homepage: https://github.com/dart-lang/webdev
55
description: >-
66
A Chrome extension for Dart debugging.
@@ -14,12 +14,14 @@ dependencies:
1414
js: ^0.6.1+1
1515

1616
dev_dependencies:
17+
args: ^2.3.1
1718
build: ^2.0.0
1819
build_runner: ^2.4.0
1920
built_collection: ^5.0.0
2021
built_value_generator: ^8.3.0
2122
build_web_compilers: ^4.0.4
2223
dwds: ^16.0.0
24+
path: ^1.8.1
2325
sse: ^4.1.2
2426
web_socket_channel: ^2.2.0
2527

dwds/debug_extension_mv3/web/background.dart

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -55,16 +55,6 @@ void _registerListeners() {
5555

5656
chrome.commands.onCommand
5757
.addListener(allowInterop(_maybeSendCopyAppIdRequest));
58-
59-
// Detect clicks on the Dart Debug Extension icon.
60-
onExtensionIconClicked(
61-
allowInterop(
62-
(Tab tab) => attachDebugger(
63-
tab.id,
64-
trigger: Trigger.extensionIcon,
65-
),
66-
),
67-
);
6858
}
6959

7060
Future<void> _handleRuntimeMessages(
@@ -137,6 +127,20 @@ Future<void> _handleRuntimeMessages(
137127
},
138128
);
139129

130+
interceptMessage<DebugStateChange>(
131+
message: jsRequest,
132+
expectedType: MessageType.debugStateChange,
133+
expectedSender: Script.popup,
134+
expectedRecipient: Script.background,
135+
messageHandler: (DebugStateChange debugStateChange) {
136+
final newState = debugStateChange.newState;
137+
final tabId = debugStateChange.tabId;
138+
if (newState == DebugStateChange.startDebugging) {
139+
attachDebugger(tabId, trigger: Trigger.extensionIcon);
140+
}
141+
},
142+
);
143+
140144
interceptMessage<String>(
141145
message: jsRequest,
142146
expectedType: MessageType.multipleAppsDetected,
@@ -154,7 +158,7 @@ Future<void> _handleRuntimeMessages(
154158
value: multipleAppsDetected,
155159
tabId: dartTab.id,
156160
);
157-
_setWarningIcon();
161+
_setWarningIcon(dartTab.id);
158162
},
159163
);
160164

@@ -181,7 +185,7 @@ Future<void> _detectNavigationAwayFromDartApp(
181185
final debugInfo = await _fetchDebugInfo(navigationInfo.tabId);
182186
if (debugInfo == null) return;
183187
if (debugInfo.tabUrl != navigationInfo.url) {
184-
_setDefaultIcon();
188+
_setDefaultIcon(navigationInfo.tabId);
185189
await clearStaleDebugSession(tabId);
186190
await removeStorageObject(type: StorageObject.debugInfo, tabId: tabId);
187191
await detachDebugger(
@@ -240,28 +244,38 @@ Future<bool> _maybeSendCopyAppIdRequest(String command, [Tab? tab]) async {
240244
Future<void> _updateIcon(int activeTabId) async {
241245
final debugInfo = await _fetchDebugInfo(activeTabId);
242246
if (debugInfo == null) {
243-
_setDefaultIcon();
247+
_setDefaultIcon(activeTabId);
244248
return;
245249
}
246250
final multipleApps = await fetchStorageObject<String>(
247251
type: StorageObject.multipleAppsDetected,
248252
tabId: activeTabId,
249253
);
250-
multipleApps == null ? _setDebuggableIcon() : _setWarningIcon();
254+
multipleApps == null
255+
? _setDebuggableIcon(activeTabId)
256+
: _setWarningIcon(activeTabId);
251257
}
252258

253-
void _setDebuggableIcon() {
259+
void _setDebuggableIcon(int tabId) {
254260
setExtensionIcon(IconInfo(path: 'static_assets/dart.png'));
261+
setExtensionPopup(
262+
PopupDetails(popup: 'static_assets/popup.html', tabId: tabId),
263+
);
255264
}
256265

257-
void _setWarningIcon() {
258-
setExtensionIcon(IconInfo(path: 'static_assets/dart_warning.png'));
266+
void _setWarningIcon(int tabId) {
267+
setExtensionPopup(
268+
PopupDetails(popup: 'static_assets/popup.html', tabId: tabId),
269+
);
259270
}
260271

261-
void _setDefaultIcon() {
272+
void _setDefaultIcon(int tabId) {
262273
final iconPath =
263274
isDevMode ? 'static_assets/dart_dev.png' : 'static_assets/dart_grey.png';
264275
setExtensionIcon(IconInfo(path: iconPath));
276+
setExtensionPopup(
277+
PopupDetails(popup: '', tabId: tabId),
278+
);
265279
}
266280

267281
Future<DebugInfo?> _fetchDebugInfo(int tabId) {

dwds/debug_extension_mv3/web/manifest_mv2.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "Dart Debug Extension",
3-
"version": "2.0",
3+
"version": "2.1",
44
"manifest_version": 2,
55
"devtools_page": "static_assets/devtools.html",
66
"browser_action": {
@@ -37,6 +37,5 @@
3737
"description": "Copy the app ID"
3838
}
3939
},
40-
"web_accessible_resources": ["debug_info.dart.js"],
41-
"options_page": "static_assets/settings.html"
40+
"web_accessible_resources": ["debug_info.dart.js"]
4241
}

dwds/debug_extension_mv3/web/manifest_mv3.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "Dart Debug Extension",
3-
"version": "2.0",
3+
"version": "2.1",
44
"manifest_version": 3,
55
"devtools_page": "static_assets/devtools.html",
66
"action": {
@@ -43,6 +43,5 @@
4343
"matches": ["<all_urls>"],
4444
"resources": ["debug_info.dart.js"]
4545
}
46-
],
47-
"options_page": "static_assets/settings.html"
46+
]
4847
}

dwds/debug_extension_mv3/web/messaging.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ enum Script {
2525
background,
2626
copier,
2727
debuggerPanel,
28-
detector;
28+
detector,
29+
popup;
2930

3031
factory Script.fromString(String value) {
3132
return Script.values.byName(value);
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
@JS()
6+
library popup;
7+
8+
import 'dart:async';
9+
import 'dart:convert';
10+
import 'dart:html';
11+
12+
import 'package:dwds/data/debug_info.dart';
13+
import 'package:js/js.dart';
14+
15+
import 'data_serializers.dart';
16+
import 'data_types.dart';
17+
import 'messaging.dart';
18+
import 'storage.dart';
19+
import 'utils.dart';
20+
21+
const _appIdContainerId = 'appIdContainer';
22+
const _appIdDividerId = 'appIdDivider';
23+
const _appIdSpanId = 'appId';
24+
const _copyIdButtonId = 'copyIdButton';
25+
const _copiedSuccessId = 'copiedSuccess';
26+
const _fileBugButtonId = 'fileBugButton';
27+
const _hiddenClass = 'hidden';
28+
const _launchDevToolsButtonId = 'launchDevToolsButton';
29+
const _loadingSpinnerId = 'loadingSpinner';
30+
const _windowOption = 'windowOption';
31+
const _tabOption = 'tabOption';
32+
33+
Future<int?> get _tabId async {
34+
final tab = await activeTab;
35+
return tab?.id;
36+
}
37+
38+
String? _appId;
39+
40+
Future<void> main() async {
41+
_registerListeners();
42+
await _loadUiAndHideSpinner();
43+
}
44+
45+
void _registerListeners() {
46+
final launchDevToolsButton =
47+
document.getElementById(_launchDevToolsButtonId) as ButtonElement;
48+
launchDevToolsButton.addEventListener('click', _launchDevTools);
49+
50+
final copyButton = document.getElementById(_copyIdButtonId) as ButtonElement;
51+
copyButton.addEventListener('click', _copyAppId);
52+
53+
final fileABugButton =
54+
document.getElementById(_fileBugButtonId) as ButtonElement;
55+
fileABugButton.addEventListener('click', _openIssueTracker);
56+
57+
document.addEventListener('DOMContentLoaded', _updateSettingsFromStorage);
58+
59+
final windowOption = document.getElementById(_windowOption) as InputElement;
60+
windowOption.addEventListener('change', (Event _) {
61+
_saveSettingsToStorage(windowOption.value);
62+
});
63+
64+
final tabOption = document.getElementById(_tabOption) as InputElement;
65+
tabOption.addEventListener('change', (Event _) {
66+
_saveSettingsToStorage(tabOption.value);
67+
});
68+
}
69+
70+
Future<void> _loadUiAndHideSpinner() async {
71+
final inserted = await _insertAppId();
72+
if (inserted) {
73+
_updateElementVisibility(_appIdContainerId, visible: true);
74+
_updateElementVisibility(_appIdDividerId, visible: true);
75+
}
76+
_updateElementVisibility(_loadingSpinnerId, visible: false);
77+
}
78+
79+
Future<DebugInfo?> _fetchDebugInfo(int? tabId) async {
80+
if (tabId == null) return null;
81+
final debugInfo = await fetchStorageObject<DebugInfo>(
82+
type: StorageObject.debugInfo,
83+
tabId: tabId,
84+
);
85+
return debugInfo;
86+
}
87+
88+
Future<bool> _insertAppId() async {
89+
final tabId = await _tabId;
90+
final debugInfo = await _fetchDebugInfo(tabId);
91+
if (debugInfo == null) return false;
92+
final isInternalBuild = debugInfo.isInternalBuild ?? false;
93+
final workspaceName = debugInfo.workspaceName;
94+
if (isInternalBuild && workspaceName != null) {
95+
_appId = '$workspaceName-$tabId';
96+
final appIdSpan = document.getElementById(_appIdSpanId) as SpanElement;
97+
appIdSpan.setInnerHtml(_appId);
98+
return true;
99+
}
100+
return false;
101+
}
102+
103+
Future<void> _openIssueTracker(Event _) async {
104+
final debugInfo = await _fetchDebugInfo(await _tabId);
105+
final isInternalBuild = debugInfo?.isInternalBuild ?? false;
106+
final issueTrackerLink = isInternalBuild
107+
? 'http://b/issues/new?component=775375&template=1791321'
108+
: 'https://github.com/dart-lang/webdev/issues/new?labels=dart-debug-extension&projects=&template=dart_debug_extension.md';
109+
await createTab(issueTrackerLink);
110+
}
111+
112+
Future<void> _launchDevTools(Event _) async {
113+
final tabId = await _tabId;
114+
final json = jsonEncode(
115+
serializers.serialize(
116+
DebugStateChange(
117+
(b) => b
118+
..tabId = tabId
119+
..newState = DebugStateChange.startDebugging,
120+
),
121+
),
122+
);
123+
await sendRuntimeMessage(
124+
type: MessageType.debugStateChange,
125+
body: json,
126+
sender: Script.popup, // change to popup
127+
recipient: Script.background,
128+
);
129+
}
130+
131+
void _copyAppId(Event _) {
132+
if (_appId == null) return;
133+
final clipboard = window.navigator.clipboard;
134+
if (clipboard == null) return;
135+
clipboard.writeText(_appId!);
136+
_updateElementVisibility(_copiedSuccessId, visible: true);
137+
}
138+
139+
Future<void> _updateSettingsFromStorage(Event _) async {
140+
final devToolsOpener = await fetchStorageObject<DevToolsOpener>(
141+
type: StorageObject.devToolsOpener,
142+
);
143+
final openInNewWindow = devToolsOpener?.newWindow ?? false;
144+
_getRadioButton(_windowOption).checked = openInNewWindow;
145+
_getRadioButton(_tabOption).checked = !openInNewWindow;
146+
}
147+
148+
Future<void> _saveSettingsToStorage(String? devToolsOpener) async {
149+
if (devToolsOpener == null) return;
150+
await setStorageObject<DevToolsOpener>(
151+
type: StorageObject.devToolsOpener,
152+
value: DevToolsOpener(
153+
(b) => b..newWindow = devToolsOpener == 'window',
154+
),
155+
);
156+
}
157+
158+
RadioButtonInputElement _getRadioButton(String id) {
159+
return document.getElementById(id) as RadioButtonInputElement;
160+
}
161+
162+
void _updateElementVisibility(String elementId, {required bool visible}) {
163+
final element = document.getElementById(elementId);
164+
if (element == null) return;
165+
if (visible) {
166+
element.classes.remove(_hiddenClass);
167+
} else {
168+
element.classes.add(_hiddenClass);
169+
}
170+
}

0 commit comments

Comments
 (0)