Skip to content

Commit 085050a

Browse files
Work towards migrating devtools_app to package:web (#6626)
1 parent d86ce7d commit 085050a

File tree

58 files changed

+269
-290
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+269
-290
lines changed

packages/devtools_app/integration_test/test/live_connection/devtools_extensions_test.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
// Do not delete these arguments. They are parsed by test runner.
66
// test-argument:experimentsOn=true
77

8-
// import 'package:devtools_app/devtools_app.dart';
98
import 'package:devtools_app/devtools_app.dart';
109
import 'package:devtools_app/src/extensions/embedded/view.dart';
1110
import 'package:devtools_app/src/extensions/extension_screen.dart';

packages/devtools_app/lib/src/extensions/embedded/_controller_web.dart

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
// ignore_for_file: avoid_web_libraries_in_flutter, as designed
65
import 'dart:async';
7-
import 'dart:html' as html;
86
import 'dart:ui_web' as ui_web;
97

108
import 'package:devtools_app_shared/utils.dart';
119
import 'package:devtools_extensions/api.dart';
1210
import 'package:path/path.dart' as path;
11+
import 'package:web/helpers.dart';
1312

1413
import '../../shared/development_helpers.dart';
1514
import '../../shared/globals.dart';
@@ -44,7 +43,7 @@ class EmbeddedExtensionControllerImpl extends EmbeddedExtensionController
4443
}
4544

4645
final baseUri = path.join(
47-
html.window.location.origin,
46+
window.location.origin,
4847
'devtools_extensions',
4948
extensionConfig.name,
5049
'index.html',
@@ -58,9 +57,9 @@ class EmbeddedExtensionControllerImpl extends EmbeddedExtensionController
5857
return Uri.parse(baseUri).copyWith(queryParameters: queryParams).toString();
5958
}
6059

61-
html.IFrameElement get extensionIFrame => _extensionIFrame;
60+
HTMLIFrameElement get extensionIFrame => _extensionIFrame;
6261

63-
late final html.IFrameElement _extensionIFrame;
62+
late final HTMLIFrameElement _extensionIFrame;
6463

6564
final extensionPostEventStream =
6665
StreamController<DevToolsExtensionEvent>.broadcast();
@@ -75,7 +74,9 @@ class EmbeddedExtensionControllerImpl extends EmbeddedExtensionController
7574
);
7675
_initialized = true;
7776

78-
_extensionIFrame = html.IFrameElement()
77+
// TODO(kenz): replace with `createIFrameElement` when we upgrade to
78+
// package:web ^0.3.1.
79+
_extensionIFrame = createElementTag('iframe') as HTMLIFrameElement
7980
// This url is safe because we built it ourselves and it does not include
8081
// any user input.
8182
// ignore: unsafe_html

packages/devtools_app/lib/src/extensions/embedded/_view_web.dart

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
// found in the LICENSE file.
44

55
import 'dart:async';
6-
// ignore: avoid_web_libraries_in_flutter, as designed
7-
import 'dart:html' as html;
6+
import 'dart:js_interop';
87

98
import 'package:devtools_app_shared/ui.dart';
109
import 'package:devtools_app_shared/utils.dart';
1110
import 'package:devtools_extensions/api.dart';
11+
import 'package:devtools_extensions/utils.dart';
1212
import 'package:flutter/material.dart';
13+
import 'package:web/helpers.dart';
1314

1415
import '../../shared/banner_messages.dart';
1516
import '../../shared/common_widgets.dart';
@@ -96,7 +97,7 @@ class _ExtensionIFrameController extends DisposableController
9697
/// We need to store this in a variable so that the listener is properly
9798
/// removed in [dispose]. Otherwise, we will end up in a state where we are
9899
/// leaking listeners when an extension is disabled and re-enabled.
99-
html.EventListener? _handleMessageListener;
100+
EventListener? _handleMessageListener;
100101

101102
void init() {
102103
_iFrameReady = Completer<void>();
@@ -108,9 +109,9 @@ class _ExtensionIFrameController extends DisposableController
108109
}),
109110
);
110111

111-
html.window.addEventListener(
112+
window.addEventListener(
112113
'message',
113-
_handleMessageListener = _handleMessage,
114+
_handleMessageListener = _handleMessage.toJS,
114115
);
115116

116117
autoDisposeStreamSubscription(
@@ -148,6 +149,12 @@ class _ExtensionIFrameController extends DisposableController
148149
}
149150

150151
void _postMessage(DevToolsExtensionEvent event) async {
152+
// In [integrationTestMode] we are loading a placeholder url
153+
// (https://flutter.dev/) in the extension iFrame, so trying to post a
154+
// message causes a cross-origin security error. Return early when
155+
// [integrationTestMode] is true so that [_postMessage] calls are a no-op.
156+
if (integrationTestMode) return;
157+
151158
await _iFrameReady.future;
152159
final message = event.toJson();
153160
assert(
@@ -156,22 +163,20 @@ class _ExtensionIFrameController extends DisposableController
156163
' _iFrameReady future completed.',
157164
);
158165
embeddedExtensionController.extensionIFrame.contentWindow!.postMessage(
159-
message,
160-
embeddedExtensionController.extensionUrl,
166+
message.jsify(),
167+
embeddedExtensionController.extensionUrl.toJS,
161168
);
162169
}
163170

164-
void _handleMessage(html.Event e) {
165-
if (e is html.MessageEvent) {
166-
final extensionEvent = DevToolsExtensionEvent.tryParse(e.data);
167-
if (extensionEvent != null) {
168-
onEventReceived(
169-
extensionEvent,
170-
onUnknownEvent: () => notificationService.push(
171-
'Unknown event received from extension: ${e.data}',
172-
),
173-
);
174-
}
171+
void _handleMessage(Event e) {
172+
final extensionEvent = tryParseExtensionEvent(e);
173+
if (extensionEvent != null) {
174+
onEventReceived(
175+
extensionEvent,
176+
onUnknownEvent: () => notificationService.push(
177+
'Unknown event received from extension: $extensionEvent}',
178+
),
179+
);
175180
}
176181
}
177182

@@ -205,7 +210,7 @@ class _ExtensionIFrameController extends DisposableController
205210

206211
@override
207212
void dispose() {
208-
html.window.removeEventListener('message', _handleMessageListener);
213+
window.removeEventListener('message', _handleMessageListener);
209214
_handleMessageListener = null;
210215
_pollForExtensionHandlerReady?.cancel();
211216
super.dispose();

packages/devtools_app/lib/src/screens/memory/framework/connected/memory_controller.dart

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,11 @@ class MemoryFeatureControllers {
5757
}
5858
}
5959

60-
/// This class contains the business logic for memory screen, for a connected application.
60+
/// This class contains the business logic for memory screen, for a connected
61+
/// application.
6162
///
62-
/// This class must not have direct dependencies on dart:html. This allows tests
63-
/// of the complicated logic in this class to run on the VM.
63+
/// This class must not have direct dependencies on web-only libraries. This
64+
/// allows tests of the complicated logic in this class to run on the VM.
6465
///
6566
/// The controller should be recreated for every new connection.
6667
class MemoryController extends DisposableController

packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_controller_web.dart

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@
33
// found in the LICENSE file.
44

55
import 'dart:async';
6-
// ignore: avoid_web_libraries_in_flutter, as designed
7-
import 'dart:html' as html;
86
import 'dart:ui_web' as ui_web;
97

108
import 'package:flutter/foundation.dart';
9+
import 'package:web/helpers.dart';
1110

1211
import '../../../../../shared/globals.dart';
1312
import '../../../../../shared/primitives/trace_event.dart';
@@ -120,18 +119,18 @@ class PerfettoControllerImpl extends PerfettoController {
120119
return _debugPerfettoUrl;
121120
}
122121
final basePath = assetUrlHelper(
123-
origin: html.window.location.origin,
124-
path: html.window.location.pathname ?? '',
122+
origin: window.location.origin,
123+
path: window.location.pathname,
125124
);
126125
final indexFilePath = ui_web.assetManager
127126
.getAssetUrl(devToolsExtensionPoints.perfettoIndexLocation);
128127
final baseUrl = '$basePath/$indexFilePath';
129128
return '$baseUrl$_embeddedModeQuery';
130129
}
131130

132-
html.IFrameElement get perfettoIFrame => _perfettoIFrame;
131+
HTMLIFrameElement get perfettoIFrame => _perfettoIFrame;
133132

134-
late final html.IFrameElement _perfettoIFrame;
133+
late final HTMLIFrameElement _perfettoIFrame;
135134

136135
/// The set of trace events that should be shown in the Perfetto trace viewer.
137136
///
@@ -166,7 +165,9 @@ class PerfettoControllerImpl extends PerfettoController {
166165
);
167166
_initialized = true;
168167

169-
_perfettoIFrame = html.IFrameElement()
168+
// TODO(kenz): replace with `createIFrameElement` when we upgrade to
169+
// package:web ^0.3.1.
170+
_perfettoIFrame = createElementTag('iframe') as HTMLIFrameElement
170171
// This url is safe because we built it ourselves and it does not include
171172
// any user input.
172173
// ignore: unsafe_html

packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_web.dart

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44

55
import 'dart:async';
66
import 'dart:convert';
7-
// ignore: avoid_web_libraries_in_flutter, as designed
8-
import 'dart:html' as html;
7+
import 'dart:js_interop';
98

109
import 'package:devtools_app_shared/utils.dart';
10+
import 'package:devtools_app_shared/web_utils.dart';
1111
import 'package:flutter/foundation.dart';
1212
import 'package:flutter/material.dart';
13+
import 'package:web/helpers.dart';
1314

1415
import '../../../../../shared/analytics/analytics.dart' as ga;
1516
import '../../../../../shared/analytics/constants.dart' as gac;
@@ -119,12 +120,12 @@ class _PerfettoViewController extends DisposableController
119120

120121
static const _pollUntilReadyTimeout = Duration(seconds: 10);
121122

122-
/// The listener that is added to DevTools' [html.window] to receive messages
123+
/// The listener that is added to DevTools' [window] to receive messages
123124
/// from the Perfetto iFrame.
124125
///
125126
/// We need to store this in a variable so that the listener is properly
126127
/// removed in [dispose].
127-
html.EventListener? _handleMessageListener;
128+
EventListener? _handleMessageListener;
128129

129130
void init() {
130131
_perfettoIFrameReady = Completer<void>();
@@ -137,9 +138,9 @@ class _PerfettoViewController extends DisposableController
137138
}),
138139
);
139140

140-
html.window.addEventListener(
141+
window.addEventListener(
141142
'message',
142-
_handleMessageListener = _handleMessage,
143+
_handleMessageListener = _handleMessage.toJS,
143144
);
144145

145146
unawaited(_loadStyle(preferences.darkModeTheme.value));
@@ -244,8 +245,8 @@ class _PerfettoViewController extends DisposableController
244245
' _perfettoIFrameReady future completed.',
245246
);
246247
perfettoController.perfettoIFrame.contentWindow!.postMessage(
247-
message,
248-
perfettoController.perfettoUrl,
248+
message.jsify(),
249+
perfettoController.perfettoUrl.toJS,
249250
);
250251
}
251252

@@ -261,14 +262,15 @@ class _PerfettoViewController extends DisposableController
261262
_postMessage(message);
262263
}
263264

264-
void _handleMessage(html.Event e) {
265-
if (e is html.MessageEvent) {
266-
if (e.data == EmbeddedPerfettoEvent.pong.event &&
265+
void _handleMessage(Event e) {
266+
if (e.isMessageEvent) {
267+
final messageData = ((e as MessageEvent).data as JSString).toDart;
268+
if (messageData == EmbeddedPerfettoEvent.pong.event &&
267269
!_perfettoHandlerReady.isCompleted) {
268270
_perfettoHandlerReady.complete();
269271
}
270272

271-
if (e.data == EmbeddedPerfettoEvent.devtoolsThemePong.event &&
273+
if (messageData == EmbeddedPerfettoEvent.devtoolsThemePong.event &&
272274
!_devtoolsThemeHandlerReady.isCompleted) {
273275
_devtoolsThemeHandlerReady.complete();
274276
}
@@ -315,7 +317,7 @@ class _PerfettoViewController extends DisposableController
315317

316318
@override
317319
void dispose() {
318-
html.window.removeEventListener('message', _handleMessageListener);
320+
window.removeEventListener('message', _handleMessageListener);
319321
_handleMessageListener = null;
320322
_pollForPerfettoHandlerReady?.cancel();
321323
_pollForThemeHandlerReady?.cancel();

packages/devtools_app/lib/src/shared/analytics/analytics_common.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
// Code in this file should be able to be imported by both dart:html and
6-
// dart:io dependent libraries.
5+
// Code in this file should be able to be imported by both web and dart:io
6+
// dependent libraries.
77

88
/// Base class for all screen metrics classes.
99
///

packages/devtools_app/lib/src/shared/config_specific/drag_and_drop/_drag_and_drop_stub.dart

Lines changed: 0 additions & 12 deletions
This file was deleted.

packages/devtools_app/lib/src/shared/config_specific/drag_and_drop/_drag_and_drop_web.dart

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44

55
import 'dart:async';
66
import 'dart:convert';
7-
// ignore: avoid_web_libraries_in_flutter, as designed
8-
import 'dart:html';
7+
import 'package:web/helpers.dart';
98

109
import '../../globals.dart';
1110
import '../../primitives/utils.dart';
@@ -40,11 +39,11 @@ class DragAndDropManagerWeb extends DragAndDropManager {
4039
}
4140

4241
void _onDragOver(MouseEvent event) {
43-
dragOver(event.offset.x as double, event.offset.y as double);
42+
dragOver(event.offsetX as double, event.offsetY as double);
4443

4544
// This is necessary to allow us to drop.
4645
event.preventDefault();
47-
event.dataTransfer.dropEffect = 'move';
46+
(event as DragEvent).dataTransfer!.dropEffect = 'move';
4847
}
4948

5049
void _onDragLeave(MouseEvent _) {
@@ -61,28 +60,31 @@ class DragAndDropManagerWeb extends DragAndDropManager {
6160
// handler, return early.
6261
if (activeState?.widget.handleDrop == null) return;
6362

64-
final List<File> files = event.dataTransfer.files!;
63+
final FileList files = (event as DragEvent).dataTransfer!.files;
6564
if (files.length > 1) {
6665
notificationService.push('You cannot import more than one file.');
6766
return;
6867
}
6968

70-
final droppedFile = files.first;
71-
if (droppedFile.type != 'application/json') {
69+
final droppedFile = files.item(0);
70+
if (droppedFile?.type != 'application/json') {
7271
notificationService.push(
73-
'${droppedFile.type} is not a supported file type. Please import '
72+
'${droppedFile?.type} is not a supported file type. Please import '
7473
'a .json file that was exported from Dart DevTools.',
7574
);
7675
return;
7776
}
7877

7978
final FileReader reader = FileReader();
80-
reader.onLoad.listen((_) {
79+
(reader as Element).onLoad.listen((event) {
8180
try {
8281
final Object json = jsonDecode(reader.result as String);
8382
final devToolsJsonFile = DevToolsJsonFile(
84-
name: droppedFile.name,
85-
lastModifiedTime: droppedFile.lastModifiedDate,
83+
name: droppedFile!.name,
84+
lastModifiedTime: DateTime.fromMillisecondsSinceEpoch(
85+
droppedFile.lastModified,
86+
isUtc: true,
87+
),
8688
data: json,
8789
);
8890
activeState!.widget.handleDrop!(devToolsJsonFile);
@@ -97,7 +99,7 @@ class DragAndDropManagerWeb extends DragAndDropManager {
9799
});
98100

99101
try {
100-
reader.readAsText(droppedFile);
102+
reader.readAsText(droppedFile!);
101103
} catch (e) {
102104
notificationService.push('Could not import file: $e');
103105
}

0 commit comments

Comments
 (0)