Skip to content

Work towards migrating devtools_app to package:web #6626

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 23 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
3079752
Work towards migrating `devtools_app` to `package:web`
kenzieschmoll Oct 31, 2023
33e89b4
console
kenzieschmoll Oct 31, 2023
e6f3ea2
Merge branch 'master' of github.com:flutter/devtools into pkg-web
kenzieschmoll Oct 31, 2023
071fe25
formatting and imports
kenzieschmoll Oct 31, 2023
ecbc973
review comments
kenzieschmoll Oct 31, 2023
1979f03
remove cast
kenzieschmoll Oct 31, 2023
3041189
fixes
kenzieschmoll Oct 31, 2023
4fcf18e
Fix _framework_initialize_web.dart
kenzieschmoll Nov 1, 2023
8c531c3
Merge branch 'master' of github.com:flutter/devtools into pkg-web
kenzieschmoll Nov 3, 2023
a47ed6e
Merge branch 'master' of github.com:flutter/devtools into pkg-web
kenzieschmoll Nov 3, 2023
93f34da
Merge branch 'master' of github.com:flutter/devtools into pkg-web
kenzieschmoll Nov 3, 2023
b21367d
Merge branch 'master' of github.com:flutter/devtools into pkg-web
kenzieschmoll Nov 9, 2023
8d6b3a6
fix serve tool
kenzieschmoll Nov 9, 2023
0ab80e3
Fix cross origin error
kenzieschmoll Nov 10, 2023
920f303
Merge branch 'master' of github.com:flutter/devtools into pkg-web
kenzieschmoll Nov 10, 2023
9b40ccd
Fix remaining issues
kenzieschmoll Nov 10, 2023
429af6f
fix connected app
kenzieschmoll Nov 13, 2023
a296596
Merge branch 'master' of github.com:flutter/devtools into pkg-web
kenzieschmoll Nov 13, 2023
46743af
fix extensions with dartify
kenzieschmoll Nov 13, 2023
36fea05
add `tryParseExtensionEvent`
kenzieschmoll Nov 13, 2023
f896b98
remove unused import
kenzieschmoll Nov 13, 2023
e9bc026
fix some comments
kenzieschmoll Nov 13, 2023
fc71186
formatting
kenzieschmoll Nov 13, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
// Do not delete these arguments. They are parsed by test runner.
// test-argument:experimentsOn=true

// import 'package:devtools_app/devtools_app.dart';
import 'package:devtools_app/devtools_app.dart';
import 'package:devtools_app/src/extensions/embedded/view.dart';
import 'package:devtools_app/src/extensions/extension_screen.dart';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// ignore_for_file: avoid_web_libraries_in_flutter, as designed
import 'dart:async';
import 'dart:html' as html;
import 'dart:ui_web' as ui_web;

import 'package:devtools_app_shared/utils.dart';
import 'package:devtools_extensions/api.dart';
import 'package:path/path.dart' as path;
import 'package:web/helpers.dart';

import '../../shared/development_helpers.dart';
import '../../shared/globals.dart';
Expand Down Expand Up @@ -44,7 +43,7 @@ class EmbeddedExtensionControllerImpl extends EmbeddedExtensionController
}

final baseUri = path.join(
html.window.location.origin,
window.location.origin,
'devtools_extensions',
extensionConfig.name,
'index.html',
Expand All @@ -58,9 +57,9 @@ class EmbeddedExtensionControllerImpl extends EmbeddedExtensionController
return Uri.parse(baseUri).copyWith(queryParameters: queryParams).toString();
}

html.IFrameElement get extensionIFrame => _extensionIFrame;
HTMLIFrameElement get extensionIFrame => _extensionIFrame;

late final html.IFrameElement _extensionIFrame;
late final HTMLIFrameElement _extensionIFrame;

final extensionPostEventStream =
StreamController<DevToolsExtensionEvent>.broadcast();
Expand All @@ -75,7 +74,9 @@ class EmbeddedExtensionControllerImpl extends EmbeddedExtensionController
);
_initialized = true;

_extensionIFrame = html.IFrameElement()
// TODO(kenz): replace with `createIFrameElement` when we upgrade to
// package:web ^0.3.1.
_extensionIFrame = createElementTag('iframe') as HTMLIFrameElement
// This url is safe because we built it ourselves and it does not include
// any user input.
// ignore: unsafe_html
Expand Down
43 changes: 24 additions & 19 deletions packages/devtools_app/lib/src/extensions/embedded/_view_web.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
// found in the LICENSE file.

import 'dart:async';
// ignore: avoid_web_libraries_in_flutter, as designed
import 'dart:html' as html;
import 'dart:js_interop';

import 'package:devtools_app_shared/ui.dart';
import 'package:devtools_app_shared/utils.dart';
import 'package:devtools_extensions/api.dart';
import 'package:devtools_extensions/utils.dart';
import 'package:flutter/material.dart';
import 'package:web/helpers.dart';

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

void init() {
_iFrameReady = Completer<void>();
Expand All @@ -108,9 +109,9 @@ class _ExtensionIFrameController extends DisposableController
}),
);

html.window.addEventListener(
window.addEventListener(
'message',
_handleMessageListener = _handleMessage,
_handleMessageListener = _handleMessage.toJS,
);

autoDisposeStreamSubscription(
Expand Down Expand Up @@ -148,6 +149,12 @@ class _ExtensionIFrameController extends DisposableController
}

void _postMessage(DevToolsExtensionEvent event) async {
// In [integrationTestMode] we are loading a placeholder url
// (https://flutter.dev/) in the extension iFrame, so trying to post a
// message causes a cross-origin security error. Return early when
// [integrationTestMode] is true so that [_postMessage] calls are a no-op.
if (integrationTestMode) return;

await _iFrameReady.future;
final message = event.toJson();
assert(
Expand All @@ -156,22 +163,20 @@ class _ExtensionIFrameController extends DisposableController
' _iFrameReady future completed.',
);
embeddedExtensionController.extensionIFrame.contentWindow!.postMessage(
message,
embeddedExtensionController.extensionUrl,
message.jsify(),
embeddedExtensionController.extensionUrl.toJS,
);
}

void _handleMessage(html.Event e) {
if (e is html.MessageEvent) {
final extensionEvent = DevToolsExtensionEvent.tryParse(e.data);
if (extensionEvent != null) {
onEventReceived(
extensionEvent,
onUnknownEvent: () => notificationService.push(
'Unknown event received from extension: ${e.data}',
),
);
}
void _handleMessage(Event e) {
final extensionEvent = tryParseExtensionEvent(e);
if (extensionEvent != null) {
onEventReceived(
extensionEvent,
onUnknownEvent: () => notificationService.push(
'Unknown event received from extension: $extensionEvent}',
),
);
}
}

Expand Down Expand Up @@ -205,7 +210,7 @@ class _ExtensionIFrameController extends DisposableController

@override
void dispose() {
html.window.removeEventListener('message', _handleMessageListener);
window.removeEventListener('message', _handleMessageListener);
_handleMessageListener = null;
_pollForExtensionHandlerReady?.cancel();
super.dispose();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,11 @@ class MemoryFeatureControllers {
}
}

/// This class contains the business logic for memory screen, for a connected application.
/// This class contains the business logic for memory screen, for a connected
/// application.
///
/// This class must not have direct dependencies on dart:html. This allows tests
/// of the complicated logic in this class to run on the VM.
/// This class must not have direct dependencies on web-only libraries. This
/// allows tests of the complicated logic in this class to run on the VM.
///
/// The controller should be recreated for every new connection.
class MemoryController extends DisposableController
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@
// found in the LICENSE file.

import 'dart:async';
// ignore: avoid_web_libraries_in_flutter, as designed
import 'dart:html' as html;
import 'dart:ui_web' as ui_web;

import 'package:flutter/foundation.dart';
import 'package:web/helpers.dart';

import '../../../../../shared/globals.dart';
import '../../../../../shared/primitives/trace_event.dart';
Expand Down Expand Up @@ -120,18 +119,18 @@ class PerfettoControllerImpl extends PerfettoController {
return _debugPerfettoUrl;
}
final basePath = assetUrlHelper(
origin: html.window.location.origin,
path: html.window.location.pathname ?? '',
origin: window.location.origin,
path: window.location.pathname,
);
final indexFilePath = ui_web.assetManager
.getAssetUrl(devToolsExtensionPoints.perfettoIndexLocation);
final baseUrl = '$basePath/$indexFilePath';
return '$baseUrl$_embeddedModeQuery';
}

html.IFrameElement get perfettoIFrame => _perfettoIFrame;
HTMLIFrameElement get perfettoIFrame => _perfettoIFrame;

late final html.IFrameElement _perfettoIFrame;
late final HTMLIFrameElement _perfettoIFrame;

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

_perfettoIFrame = html.IFrameElement()
// TODO(kenz): replace with `createIFrameElement` when we upgrade to
// package:web ^0.3.1.
_perfettoIFrame = createElementTag('iframe') as HTMLIFrameElement
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this cast is okay in contrast with the cast above. You're casting between interop types and you know it's that type. I think this isn't a lint in the future with extension types.

// This url is safe because we built it ourselves and it does not include
// any user input.
// ignore: unsafe_html
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@

import 'dart:async';
import 'dart:convert';
// ignore: avoid_web_libraries_in_flutter, as designed
import 'dart:html' as html;
import 'dart:js_interop';

import 'package:devtools_app_shared/utils.dart';
import 'package:devtools_app_shared/web_utils.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:web/helpers.dart';

import '../../../../../shared/analytics/analytics.dart' as ga;
import '../../../../../shared/analytics/constants.dart' as gac;
Expand Down Expand Up @@ -119,12 +120,12 @@ class _PerfettoViewController extends DisposableController

static const _pollUntilReadyTimeout = Duration(seconds: 10);

/// The listener that is added to DevTools' [html.window] to receive messages
/// The listener that is added to DevTools' [window] to receive messages
/// from the Perfetto iFrame.
///
/// We need to store this in a variable so that the listener is properly
/// removed in [dispose].
html.EventListener? _handleMessageListener;
EventListener? _handleMessageListener;

void init() {
_perfettoIFrameReady = Completer<void>();
Expand All @@ -137,9 +138,9 @@ class _PerfettoViewController extends DisposableController
}),
);

html.window.addEventListener(
window.addEventListener(
'message',
_handleMessageListener = _handleMessage,
_handleMessageListener = _handleMessage.toJS,
);

unawaited(_loadStyle(preferences.darkModeTheme.value));
Expand Down Expand Up @@ -244,8 +245,8 @@ class _PerfettoViewController extends DisposableController
' _perfettoIFrameReady future completed.',
);
perfettoController.perfettoIFrame.contentWindow!.postMessage(
message,
perfettoController.perfettoUrl,
message.jsify(),
perfettoController.perfettoUrl.toJS,
);
}

Expand All @@ -261,14 +262,15 @@ class _PerfettoViewController extends DisposableController
_postMessage(message);
}

void _handleMessage(html.Event e) {
if (e is html.MessageEvent) {
if (e.data == EmbeddedPerfettoEvent.pong.event &&
void _handleMessage(Event e) {
if (e.isMessageEvent) {
final messageData = ((e as MessageEvent).data as JSString).toDart;
if (messageData == EmbeddedPerfettoEvent.pong.event &&
!_perfettoHandlerReady.isCompleted) {
_perfettoHandlerReady.complete();
}

if (e.data == EmbeddedPerfettoEvent.devtoolsThemePong.event &&
if (messageData == EmbeddedPerfettoEvent.devtoolsThemePong.event &&
!_devtoolsThemeHandlerReady.isCompleted) {
_devtoolsThemeHandlerReady.complete();
}
Expand Down Expand Up @@ -315,7 +317,7 @@ class _PerfettoViewController extends DisposableController

@override
void dispose() {
html.window.removeEventListener('message', _handleMessageListener);
window.removeEventListener('message', _handleMessageListener);
_handleMessageListener = null;
_pollForPerfettoHandlerReady?.cancel();
_pollForThemeHandlerReady?.cancel();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

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

/// Base class for all screen metrics classes.
///
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@

import 'dart:async';
import 'dart:convert';
// ignore: avoid_web_libraries_in_flutter, as designed
import 'dart:html';
import 'package:web/helpers.dart';

import '../../globals.dart';
import '../../primitives/utils.dart';
Expand Down Expand Up @@ -40,11 +39,11 @@ class DragAndDropManagerWeb extends DragAndDropManager {
}

void _onDragOver(MouseEvent event) {
dragOver(event.offset.x as double, event.offset.y as double);
dragOver(event.offsetX as double, event.offsetY as double);

// This is necessary to allow us to drop.
event.preventDefault();
event.dataTransfer.dropEffect = 'move';
(event as DragEvent).dataTransfer!.dropEffect = 'move';
}

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

final List<File> files = event.dataTransfer.files!;
final FileList files = (event as DragEvent).dataTransfer!.files;
if (files.length > 1) {
notificationService.push('You cannot import more than one file.');
return;
}

final droppedFile = files.first;
if (droppedFile.type != 'application/json') {
final droppedFile = files.item(0);
if (droppedFile?.type != 'application/json') {
notificationService.push(
'${droppedFile.type} is not a supported file type. Please import '
'${droppedFile?.type} is not a supported file type. Please import '
'a .json file that was exported from Dart DevTools.',
);
return;
}

final FileReader reader = FileReader();
reader.onLoad.listen((_) {
(reader as Element).onLoad.listen((event) {
try {
final Object json = jsonDecode(reader.result as String);
final devToolsJsonFile = DevToolsJsonFile(
name: droppedFile.name,
lastModifiedTime: droppedFile.lastModifiedDate,
name: droppedFile!.name,
lastModifiedTime: DateTime.fromMillisecondsSinceEpoch(
droppedFile.lastModified,
isUtc: true,
),
data: json,
);
activeState!.widget.handleDrop!(devToolsJsonFile);
Expand All @@ -97,7 +99,7 @@ class DragAndDropManagerWeb extends DragAndDropManager {
});

try {
reader.readAsText(droppedFile);
reader.readAsText(droppedFile!);
} catch (e) {
notificationService.push('Could not import file: $e');
}
Expand Down
Loading