[MV3 Debug Extension] Update Chrome APIs to be backwards compatible with MV2 (#1951)
diff --git a/dwds/debug_extension_mv3/web/background.dart b/dwds/debug_extension_mv3/web/background.dart
index cc31a42..36410a5 100644
--- a/dwds/debug_extension_mv3/web/background.dart
+++ b/dwds/debug_extension_mv3/web/background.dart
@@ -6,7 +6,6 @@
library background;
import 'dart:async';
-import 'dart:html';
import 'package:dwds/data/debug_info.dart';
import 'package:js/js.dart';
@@ -39,7 +38,7 @@
_updateIcon(info.tabId);
}));
chrome.windows.onFocusChanged.addListener(allowInterop((_) async {
- final currentTab = await _getTab();
+ final currentTab = await activeTab;
if (currentTab?.id != null) {
_updateIcon(currentTab!.id);
}
@@ -48,7 +47,7 @@
.addListener(allowInterop(_detectNavigationAwayFromDartApp));
// Detect clicks on the Dart Debug Extension icon.
- chrome.action.onClicked.addListener(allowInterop(
+ onExtensionIconClicked(allowInterop(
(Tab tab) => attachDebugger(
tab.id,
trigger: Trigger.extensionIcon,
@@ -94,7 +93,7 @@
await setStorageObject<DebugInfo>(
type: StorageObject.debugInfo, value: debugInfo, tabId: dartTab.id);
// Update the icon to show that a Dart app has been detected:
- final currentTab = await _getTab();
+ final currentTab = await activeTab;
if (currentTab?.id == dartTab.id) {
_setDebuggableIcon();
}
@@ -139,15 +138,14 @@
}
void _setDebuggableIcon() {
- chrome.action
- .setIcon(IconInfo(path: 'static_assets/dart.png'), /*callback*/ null);
+ setExtensionIcon(IconInfo(path: 'static_assets/dart.png'));
}
void _setDefaultIcon() {
final iconPath = isDevMode()
? 'static_assets/dart_dev.png'
: 'static_assets/dart_grey.png';
- chrome.action.setIcon(IconInfo(path: iconPath), /*callback*/ null);
+ setExtensionIcon(IconInfo(path: iconPath));
}
Future<DebugInfo?> _fetchDebugInfo(int tabId) {
@@ -156,9 +154,3 @@
tabId: tabId,
);
}
-
-Future<Tab?> _getTab() async {
- final query = QueryInfo(active: true, currentWindow: true);
- final tabs = List<Tab>.from(await promiseToFuture(chrome.tabs.query(query)));
- return tabs.isNotEmpty ? tabs.first : null;
-}
diff --git a/dwds/debug_extension_mv3/web/chrome_api.dart b/dwds/debug_extension_mv3/web/chrome_api.dart
index 77fc960..5039db5 100644
--- a/dwds/debug_extension_mv3/web/chrome_api.dart
+++ b/dwds/debug_extension_mv3/web/chrome_api.dart
@@ -292,13 +292,14 @@
@JS()
@anonymous
class Tabs {
- external Object query(QueryInfo queryInfo);
+ external dynamic query(
+ QueryInfo queryInfo, void Function(List<Tab>) callback);
- external Object create(TabInfo tabInfo);
+ external dynamic create(TabInfo tabInfo, void Function(Tab) callback);
- external Object get(int tabId);
+ external dynamic get(int tabId, void Function(Tab?) callback);
- external Object remove(int tabId);
+ external dynamic remove(int tabId, void Function()? callback);
external OnActivatedHandler get onActivated;
@@ -378,7 +379,7 @@
@JS()
@anonymous
class Windows {
- external Object create(WindowInfo? createData);
+ external dynamic create(WindowInfo? createData, Function(WindowObj) callback);
external OnFocusChangedHandler get onFocusChanged;
}
diff --git a/dwds/debug_extension_mv3/web/debug_session.dart b/dwds/debug_extension_mv3/web/debug_session.dart
index 54de63d..378f18c 100644
--- a/dwds/debug_extension_mv3/web/debug_session.dart
+++ b/dwds/debug_extension_mv3/web/debug_session.dart
@@ -267,7 +267,7 @@
);
_debugSessions.add(debugSession);
// Create a connection with the lifeline port to keep the debug session alive:
- maybeCreateLifelinePort(dartAppTabId);
+ await maybeCreateLifelinePort(dartAppTabId);
// Send a DevtoolsRequest to the event stream:
final tabUrl = await _getTabUrl(dartAppTabId);
debugSession.sendEvent(DevToolsRequest((b) => b
@@ -401,7 +401,7 @@
final devToolsTab = await getTab(devToolsTabId);
if (devToolsTab != null) {
debugLog('Closing DevTools tab...');
- chrome.tabs.remove(devToolsTabId);
+ await removeTab(devToolsTabId);
}
}
diff --git a/dwds/debug_extension_mv3/web/lifeline_ports.dart b/dwds/debug_extension_mv3/web/lifeline_ports.dart
index 0f4ee73..b082002 100644
--- a/dwds/debug_extension_mv3/web/lifeline_ports.dart
+++ b/dwds/debug_extension_mv3/web/lifeline_ports.dart
@@ -14,11 +14,12 @@
import 'chrome_api.dart';
import 'debug_session.dart';
import 'logger.dart';
+import 'utils.dart';
Port? _lifelinePort;
int? _lifelineTab;
-void maybeCreateLifelinePort(int tabId) {
+Future<void> maybeCreateLifelinePort(int tabId) async {
// Don't create a lifeline port if we already have one (meaning another Dart
// app is currently being debugged):
if (_lifelinePort != null) {
@@ -31,13 +32,7 @@
// will connect to the port:
debugLog('Creating lifeline port.');
_lifelineTab = tabId;
- chrome.scripting.executeScript(
- InjectDetails(
- target: Target(tabId: tabId),
- files: ['lifeline_connection.dart.js'],
- ),
- /*callback*/ null,
- );
+ await injectScript('lifeline_connection.dart.js', tabId: tabId);
}
void maybeRemoveLifelinePort(int removedTabId) {
diff --git a/dwds/debug_extension_mv3/web/utils.dart b/dwds/debug_extension_mv3/web/utils.dart
index 5504e7f..d122140 100644
--- a/dwds/debug_extension_mv3/web/utils.dart
+++ b/dwds/debug_extension_mv3/web/utils.dart
@@ -12,29 +12,80 @@
import 'chrome_api.dart';
-Future<Tab> createTab(String url, {bool inNewWindow = false}) async {
+Future<Tab> createTab(String url, {bool inNewWindow = false}) {
+ final completer = Completer<Tab>();
if (inNewWindow) {
- final windowPromise = chrome.windows.create(
+ chrome.windows.create(
WindowInfo(focused: true, url: url),
+ allowInterop(
+ (WindowObj windowObj) {
+ completer.complete(windowObj.tabs.first);
+ },
+ ),
);
- final windowObj = await promiseToFuture<WindowObj>(windowPromise);
- return windowObj.tabs.first;
+ } else {
+ chrome.tabs.create(
+ TabInfo(
+ active: true,
+ url: url,
+ ),
+ allowInterop(
+ (Tab tab) {
+ completer.complete(tab);
+ },
+ ),
+ );
}
- final tabPromise = chrome.tabs.create(TabInfo(
- active: true,
- url: url,
- ));
- return promiseToFuture<Tab>(tabPromise);
+ return completer.future;
}
Future<Tab?> getTab(int tabId) {
- return promiseToFuture<Tab?>(chrome.tabs.get(tabId));
+ final completer = Completer<Tab?>();
+ chrome.tabs.get(tabId, allowInterop((tab) {
+ completer.complete(tab);
+ }));
+ return completer.future;
}
-Future<Tab?> getActiveTab() async {
+Future<Tab?> get activeTab async {
+ final completer = Completer<Tab?>();
final query = QueryInfo(active: true, currentWindow: true);
- final tabs = List<Tab>.from(await promiseToFuture(chrome.tabs.query(query)));
- return tabs.isNotEmpty ? tabs.first : null;
+ chrome.tabs.query(query, allowInterop((List tabs) {
+ if (tabs.isNotEmpty) {
+ completer.complete(tabs.first as Tab);
+ } else {
+ completer.complete(null);
+ }
+ }));
+ return completer.future;
+}
+
+Future<bool> removeTab(int tabId) {
+ final completer = Completer<bool>();
+ chrome.tabs.remove(tabId, allowInterop(() {
+ completer.complete(true);
+ }));
+ return completer.future;
+}
+
+Future<bool> injectScript(String scriptName, {required int tabId}) {
+ final completer = Completer<bool>();
+ chrome.scripting.executeScript(
+ InjectDetails(
+ target: Target(tabId: tabId),
+ files: [scriptName],
+ ), allowInterop(() {
+ completer.complete(true);
+ }));
+ return completer.future;
+}
+
+void onExtensionIconClicked(void Function(Tab) callback) {
+ chrome.action.onClicked.addListener(callback);
+}
+
+void setExtensionIcon(IconInfo info) {
+ chrome.action.setIcon(info, /*callback*/ null);
}
bool? _isDevMode;