Skip to content
Merged
57 changes: 57 additions & 0 deletions dwds/debug_extension_mv3/web/chrome_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// 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.

import 'dart:html';

import 'package:js/js.dart';

@JS()
Expand All @@ -12,6 +14,7 @@ external Chrome get chrome;
class Chrome {
external Action get action;
external Debugger get debugger;
external Devtools get devtools;
external Notifications get notifications;
external Runtime get runtime;
external Scripting get scripting;
Expand Down Expand Up @@ -87,6 +90,50 @@ class Debuggee {
external factory Debuggee({int tabId, String? extensionId, String? targetId});
}

/// chrome.devtools APIs:

@JS()
@anonymous
class Devtools {
// https://developer.chrome.com/docs/extensions/reference/devtools_inspectedWindow
external InspectedWindow get inspectedWindow;

// https://developer.chrome.com/docs/extensions/reference/devtools_panels/
external Panels get panels;
}

@JS()
@anonymous
class InspectedWindow {
external int get tabId;
}

@JS()
@anonymous
class Panels {
external void create(String title, String iconPath, String pagePath,
void Function(ExtensionPanel)? callback);
}

@JS()
@anonymous
class ExtensionPanel {
external OnHiddenHandler get onHidden;
external OnShownHandler get onShown;
}

@JS()
@anonymous
class OnHiddenHandler {
external void addListener(void Function() callback);
}

@JS()
@anonymous
class OnShownHandler {
external void addListener(void Function(Window window) callback);
}

/// chrome.notification APIs:
/// https://developer.chrome.com/docs/extensions/reference/notifications

Expand Down Expand Up @@ -211,6 +258,8 @@ class Storage {
external StorageArea get local;

external StorageArea get session;

external OnChangedHandler get onChanged;
}

@JS()
Expand All @@ -223,6 +272,14 @@ class StorageArea {
external Object remove(List<String> keys, void Function()? callback);
}

@JS()
@anonymous
class OnChangedHandler {
external void addListener(
void Function(Object changes, String areaName) callback,
);
}

/// chrome.tabs APIs
/// https://developer.chrome.com/docs/extensions/reference/tabs

Expand Down
72 changes: 72 additions & 0 deletions dwds/debug_extension_mv3/web/devtools.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (c) 2022, 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 devtools;

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

import 'chrome_api.dart';
import 'logger.dart';
import 'storage.dart';
import 'utils.dart';

bool panelsExist = false;

void main() async {
_registerListeners();
_maybeCreatePanels();
}

void _registerListeners() {
chrome.storage.onChanged.addListener(allowInterop((
Object _,
String storageArea,
) {
if (storageArea != 'session') return;
_maybeCreatePanels();
}));
}

void _maybeCreatePanels() async {
if (panelsExist) return;
final tabId = chrome.devtools.inspectedWindow.tabId;
final debugInfo = await fetchStorageObject<DebugInfo>(
type: StorageObject.debugInfo,
tabId: tabId,
);
if (debugInfo == null) return;
final isInternalBuild = debugInfo.isInternalBuild ?? false;
if (!isInternalBuild) return;
// Create a Debugger panel for all internal apps:
chrome.devtools.panels.create(
isDevMode() ? '[DEV] Dart Debugger' : 'Dart Debugger',
'',
'panel.html',
allowInterop((ExtensionPanel panel) => _onPanelAdded(panel, debugInfo)),
);
// Create an inspector panel for internal Flutter apps:
final isFlutterApp = debugInfo.isFlutterApp ?? false;
if (isFlutterApp) {
chrome.devtools.panels.create(
isDevMode() ? '[DEV] Flutter Inspector' : 'Flutter Inspector',
'',
'panel.html',
allowInterop((ExtensionPanel panel) => _onPanelAdded(panel, debugInfo)),
);
}
panelsExist = true;
}

void _onPanelAdded(ExtensionPanel panel, DebugInfo debugInfo) {
panel.onShown.addListener(allowInterop((Window window) {
if (window.origin != debugInfo.appOrigin) {
debugWarn('Page at ${window.origin} is no longer a Dart app.');
// TODO(elliette): Display banner that panel is not applicable. See:
// https://stackoverflow.com/questions/18927147/how-to-close-destroy-chrome-devtools-extensionpanel-programmatically
}
}));
}
11 changes: 11 additions & 0 deletions dwds/debug_extension_mv3/web/devtools.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>

<head>
</head>

<body>
<script src="devtools.dart.js"></script>
</body>

</html>
1 change: 1 addition & 0 deletions dwds/debug_extension_mv3/web/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "MV3 Dart Debug Extension",
"version": "1.0",
"manifest_version": 3,
"devtools_page": "devtools.html",
"action": {
"default_icon": "dart_dev.png"
},
Expand Down
11 changes: 11 additions & 0 deletions dwds/debug_extension_mv3/web/panel.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>

<head>
</head>

<body>
<h1>Panel</h1>
</body>

</html>
4 changes: 4 additions & 0 deletions dwds/test/fixtures/context.dart
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ class TestContext {
SdkConfigurationProvider? sdkConfigurationProvider,
bool useDebuggerModuleNames = false,
bool launchChrome = true,
bool isFlutterApp = false,
bool isInternalBuild = false,
}) async {
sdkConfigurationProvider ??= DefaultSdkConfigurationProvider();

Expand Down Expand Up @@ -370,6 +372,8 @@ class TestContext {
expressionCompiler,
spawnDds,
ddcService,
isFlutterApp,
isInternalBuild,
);

_appUrl = basePath.isEmpty
Expand Down
4 changes: 4 additions & 0 deletions dwds/test/fixtures/server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ class TestServer {
ExpressionCompiler? expressionCompiler,
bool spawnDds,
ExpressionCompilerService? ddcService,
bool isFlutterApp,
bool isInternalBuild,
) async {
var pipeline = const Pipeline();

Expand Down Expand Up @@ -113,6 +115,8 @@ class TestServer {
hostname: hostname,
urlEncoder: urlEncoder,
expressionCompiler: expressionCompiler,
isInternalBuild: isInternalBuild,
isFlutterApp: isFlutterApp,
devtoolsLauncher: serveDevTools
? (hostname) async {
final server = await DevToolsServer().serveDevTools(
Expand Down
Loading