Skip to content

Add popup for copying the app ID #2299

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 18 commits into from
Dec 9, 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
4 changes: 3 additions & 1 deletion dwds/debug_extension_mv3/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: mv3_extension
publish_to: none
version: 2.0.0
version: 2.1.0
homepage: https://github.com/dart-lang/webdev
description: >-
A Chrome extension for Dart debugging.
Expand All @@ -14,12 +14,14 @@ dependencies:
js: ^0.6.1+1

dev_dependencies:
args: ^2.3.1
build: ^2.0.0
build_runner: ^2.4.0
built_collection: ^5.0.0
built_value_generator: ^8.3.0
build_web_compilers: ^4.0.4
dwds: ^16.0.0
path: ^1.8.1
sse: ^4.1.2
web_socket_channel: ^2.2.0

Expand Down
50 changes: 32 additions & 18 deletions dwds/debug_extension_mv3/web/background.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,6 @@ void _registerListeners() {

chrome.commands.onCommand
.addListener(allowInterop(_maybeSendCopyAppIdRequest));

// Detect clicks on the Dart Debug Extension icon.
onExtensionIconClicked(
allowInterop(
(Tab tab) => attachDebugger(
tab.id,
trigger: Trigger.extensionIcon,
),
),
);
}

Future<void> _handleRuntimeMessages(
Expand Down Expand Up @@ -137,6 +127,20 @@ Future<void> _handleRuntimeMessages(
},
);

interceptMessage<DebugStateChange>(
message: jsRequest,
expectedType: MessageType.debugStateChange,
expectedSender: Script.popup,
expectedRecipient: Script.background,
messageHandler: (DebugStateChange debugStateChange) {
final newState = debugStateChange.newState;
final tabId = debugStateChange.tabId;
if (newState == DebugStateChange.startDebugging) {
attachDebugger(tabId, trigger: Trigger.extensionIcon);
}
},
);

interceptMessage<String>(
message: jsRequest,
expectedType: MessageType.multipleAppsDetected,
Expand All @@ -154,7 +158,7 @@ Future<void> _handleRuntimeMessages(
value: multipleAppsDetected,
tabId: dartTab.id,
);
_setWarningIcon();
_setWarningIcon(dartTab.id);
},
);

Expand All @@ -181,7 +185,7 @@ Future<void> _detectNavigationAwayFromDartApp(
final debugInfo = await _fetchDebugInfo(navigationInfo.tabId);
if (debugInfo == null) return;
if (debugInfo.tabUrl != navigationInfo.url) {
_setDefaultIcon();
_setDefaultIcon(navigationInfo.tabId);
await clearStaleDebugSession(tabId);
await removeStorageObject(type: StorageObject.debugInfo, tabId: tabId);
await detachDebugger(
Expand Down Expand Up @@ -240,28 +244,38 @@ Future<bool> _maybeSendCopyAppIdRequest(String command, [Tab? tab]) async {
Future<void> _updateIcon(int activeTabId) async {
final debugInfo = await _fetchDebugInfo(activeTabId);
if (debugInfo == null) {
_setDefaultIcon();
_setDefaultIcon(activeTabId);
return;
}
final multipleApps = await fetchStorageObject<String>(
type: StorageObject.multipleAppsDetected,
tabId: activeTabId,
);
multipleApps == null ? _setDebuggableIcon() : _setWarningIcon();
multipleApps == null
? _setDebuggableIcon(activeTabId)
: _setWarningIcon(activeTabId);
}

void _setDebuggableIcon() {
void _setDebuggableIcon(int tabId) {
setExtensionIcon(IconInfo(path: 'static_assets/dart.png'));
setExtensionPopup(
PopupDetails(popup: 'static_assets/popup.html', tabId: tabId),
);
}

void _setWarningIcon() {
setExtensionIcon(IconInfo(path: 'static_assets/dart_warning.png'));
void _setWarningIcon(int tabId) {
setExtensionPopup(
PopupDetails(popup: 'static_assets/popup.html', tabId: tabId),
);
}

void _setDefaultIcon() {
void _setDefaultIcon(int tabId) {
final iconPath =
isDevMode ? 'static_assets/dart_dev.png' : 'static_assets/dart_grey.png';
setExtensionIcon(IconInfo(path: iconPath));
setExtensionPopup(
PopupDetails(popup: '', tabId: tabId),
);
}

Future<DebugInfo?> _fetchDebugInfo(int tabId) {
Expand Down
5 changes: 2 additions & 3 deletions dwds/debug_extension_mv3/web/manifest_mv2.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "Dart Debug Extension",
"version": "2.0",
"version": "2.1",
"manifest_version": 2,
"devtools_page": "static_assets/devtools.html",
"browser_action": {
Expand Down Expand Up @@ -37,6 +37,5 @@
"description": "Copy the app ID"
}
},
"web_accessible_resources": ["debug_info.dart.js"],
"options_page": "static_assets/settings.html"
"web_accessible_resources": ["debug_info.dart.js"]
}
5 changes: 2 additions & 3 deletions dwds/debug_extension_mv3/web/manifest_mv3.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "Dart Debug Extension",
"version": "2.0",
"version": "2.1",
"manifest_version": 3,
"devtools_page": "static_assets/devtools.html",
"action": {
Expand Down Expand Up @@ -43,6 +43,5 @@
"matches": ["<all_urls>"],
"resources": ["debug_info.dart.js"]
}
],
"options_page": "static_assets/settings.html"
]
}
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 @@ -25,7 +25,8 @@ enum Script {
background,
copier,
debuggerPanel,
detector;
detector,
popup;

factory Script.fromString(String value) {
return Script.values.byName(value);
Expand Down
170 changes: 170 additions & 0 deletions dwds/debug_extension_mv3/web/popup.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// 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 popup;

import 'dart:async';
import 'dart:convert';
import 'dart:html';

import 'package:dwds/data/debug_info.dart';
import 'package:js/js.dart';

import 'data_serializers.dart';
import 'data_types.dart';
import 'messaging.dart';
import 'storage.dart';
import 'utils.dart';

const _appIdContainerId = 'appIdContainer';
const _appIdDividerId = 'appIdDivider';
const _appIdSpanId = 'appId';
const _copyIdButtonId = 'copyIdButton';
const _copiedSuccessId = 'copiedSuccess';
const _fileBugButtonId = 'fileBugButton';
const _hiddenClass = 'hidden';
const _launchDevToolsButtonId = 'launchDevToolsButton';
const _loadingSpinnerId = 'loadingSpinner';
const _windowOption = 'windowOption';
const _tabOption = 'tabOption';

Future<int?> get _tabId async {
final tab = await activeTab;
return tab?.id;
}

String? _appId;

Future<void> main() async {
_registerListeners();
await _loadUiAndHideSpinner();
}

void _registerListeners() {
final launchDevToolsButton =
document.getElementById(_launchDevToolsButtonId) as ButtonElement;
launchDevToolsButton.addEventListener('click', _launchDevTools);

final copyButton = document.getElementById(_copyIdButtonId) as ButtonElement;
copyButton.addEventListener('click', _copyAppId);

final fileABugButton =
document.getElementById(_fileBugButtonId) as ButtonElement;
fileABugButton.addEventListener('click', _openIssueTracker);

document.addEventListener('DOMContentLoaded', _updateSettingsFromStorage);

final windowOption = document.getElementById(_windowOption) as InputElement;
windowOption.addEventListener('change', (Event _) {
_saveSettingsToStorage(windowOption.value);
});

final tabOption = document.getElementById(_tabOption) as InputElement;
tabOption.addEventListener('change', (Event _) {
_saveSettingsToStorage(tabOption.value);
});
}

Future<void> _loadUiAndHideSpinner() async {
final inserted = await _insertAppId();
if (inserted) {
_updateElementVisibility(_appIdContainerId, visible: true);
_updateElementVisibility(_appIdDividerId, visible: true);
}
_updateElementVisibility(_loadingSpinnerId, visible: false);
}

Future<DebugInfo?> _fetchDebugInfo(int? tabId) async {
if (tabId == null) return null;
final debugInfo = await fetchStorageObject<DebugInfo>(
type: StorageObject.debugInfo,
tabId: tabId,
);
return debugInfo;
}

Future<bool> _insertAppId() async {
final tabId = await _tabId;
final debugInfo = await _fetchDebugInfo(tabId);
if (debugInfo == null) return false;
final isInternalBuild = debugInfo.isInternalBuild ?? false;
final workspaceName = debugInfo.workspaceName;
if (isInternalBuild && workspaceName != null) {
_appId = '$workspaceName-$tabId';
final appIdSpan = document.getElementById(_appIdSpanId) as SpanElement;
appIdSpan.setInnerHtml(_appId);
return true;
}
return false;
}

Future<void> _openIssueTracker(Event _) async {
final debugInfo = await _fetchDebugInfo(await _tabId);
final isInternalBuild = debugInfo?.isInternalBuild ?? false;
final issueTrackerLink = isInternalBuild
? 'http://b/issues/new?component=775375&template=1791321'
: 'https://github.com/dart-lang/webdev/issues/new?labels=dart-debug-extension&projects=&template=dart_debug_extension.md';
await createTab(issueTrackerLink);
}

Future<void> _launchDevTools(Event _) async {
final tabId = await _tabId;
final json = jsonEncode(
serializers.serialize(
DebugStateChange(
(b) => b
..tabId = tabId
..newState = DebugStateChange.startDebugging,
),
),
);
await sendRuntimeMessage(
type: MessageType.debugStateChange,
body: json,
sender: Script.popup, // change to popup
recipient: Script.background,
);
}

void _copyAppId(Event _) {
if (_appId == null) return;
final clipboard = window.navigator.clipboard;
if (clipboard == null) return;
clipboard.writeText(_appId!);
_updateElementVisibility(_copiedSuccessId, visible: true);
}

Future<void> _updateSettingsFromStorage(Event _) async {
final devToolsOpener = await fetchStorageObject<DevToolsOpener>(
type: StorageObject.devToolsOpener,
);
final openInNewWindow = devToolsOpener?.newWindow ?? false;
_getRadioButton(_windowOption).checked = openInNewWindow;
_getRadioButton(_tabOption).checked = !openInNewWindow;
}

Future<void> _saveSettingsToStorage(String? devToolsOpener) async {
if (devToolsOpener == null) return;
await setStorageObject<DevToolsOpener>(
type: StorageObject.devToolsOpener,
value: DevToolsOpener(
(b) => b..newWindow = devToolsOpener == 'window',
),
);
}

RadioButtonInputElement _getRadioButton(String id) {
return document.getElementById(id) as RadioButtonInputElement;
}

void _updateElementVisibility(String elementId, {required bool visible}) {
final element = document.getElementById(elementId);
if (element == null) return;
if (visible) {
element.classes.remove(_hiddenClass);
} else {
element.classes.add(_hiddenClass);
}
}
Loading