Skip to content

Commit

Permalink
Work towards migrating devtools_app to package:web (#6626)
Browse files Browse the repository at this point in the history
  • Loading branch information
kenzieschmoll authored Nov 13, 2023
1 parent d86ce7d commit 085050a
Show file tree
Hide file tree
Showing 58 changed files with 269 additions and 290 deletions.
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
// 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

0 comments on commit 085050a

Please sign in to comment.