diff --git a/packages/devtools_app/integration_test/test/live_connection/devtools_extensions_test.dart b/packages/devtools_app/integration_test/test/live_connection/devtools_extensions_test.dart index ed39ff01be8..0ebb2d2d9fb 100644 --- a/packages/devtools_app/integration_test/test/live_connection/devtools_extensions_test.dart +++ b/packages/devtools_app/integration_test/test/live_connection/devtools_extensions_test.dart @@ -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'; diff --git a/packages/devtools_app/lib/src/extensions/embedded/_controller_web.dart b/packages/devtools_app/lib/src/extensions/embedded/_controller_web.dart index 4ecfb6b0b32..57e7ba58ba1 100644 --- a/packages/devtools_app/lib/src/extensions/embedded/_controller_web.dart +++ b/packages/devtools_app/lib/src/extensions/embedded/_controller_web.dart @@ -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'; @@ -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', @@ -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.broadcast(); @@ -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 diff --git a/packages/devtools_app/lib/src/extensions/embedded/_view_web.dart b/packages/devtools_app/lib/src/extensions/embedded/_view_web.dart index f0ee7e2ef48..ce401d4f63d 100644 --- a/packages/devtools_app/lib/src/extensions/embedded/_view_web.dart +++ b/packages/devtools_app/lib/src/extensions/embedded/_view_web.dart @@ -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'; @@ -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(); @@ -108,9 +109,9 @@ class _ExtensionIFrameController extends DisposableController }), ); - html.window.addEventListener( + window.addEventListener( 'message', - _handleMessageListener = _handleMessage, + _handleMessageListener = _handleMessage.toJS, ); autoDisposeStreamSubscription( @@ -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( @@ -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}', + ), + ); } } @@ -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(); diff --git a/packages/devtools_app/lib/src/screens/memory/framework/connected/memory_controller.dart b/packages/devtools_app/lib/src/screens/memory/framework/connected/memory_controller.dart index 9bb46a9fcd6..4c26cf883c8 100644 --- a/packages/devtools_app/lib/src/screens/memory/framework/connected/memory_controller.dart +++ b/packages/devtools_app/lib/src/screens/memory/framework/connected/memory_controller.dart @@ -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 diff --git a/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_controller_web.dart b/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_controller_web.dart index b22d213b15b..31fd9e9b6a8 100644 --- a/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_controller_web.dart +++ b/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_controller_web.dart @@ -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'; @@ -120,8 +119,8 @@ 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); @@ -129,9 +128,9 @@ class PerfettoControllerImpl extends PerfettoController { 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. /// @@ -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 diff --git a/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_web.dart b/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_web.dart index a2496aae908..a16296a5981 100644 --- a/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_web.dart +++ b/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_web.dart @@ -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; @@ -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(); @@ -137,9 +138,9 @@ class _PerfettoViewController extends DisposableController }), ); - html.window.addEventListener( + window.addEventListener( 'message', - _handleMessageListener = _handleMessage, + _handleMessageListener = _handleMessage.toJS, ); unawaited(_loadStyle(preferences.darkModeTheme.value)); @@ -244,8 +245,8 @@ class _PerfettoViewController extends DisposableController ' _perfettoIFrameReady future completed.', ); perfettoController.perfettoIFrame.contentWindow!.postMessage( - message, - perfettoController.perfettoUrl, + message.jsify(), + perfettoController.perfettoUrl.toJS, ); } @@ -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(); } @@ -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(); diff --git a/packages/devtools_app/lib/src/shared/analytics/analytics_common.dart b/packages/devtools_app/lib/src/shared/analytics/analytics_common.dart index df5645c65ac..bdabb36bddb 100644 --- a/packages/devtools_app/lib/src/shared/analytics/analytics_common.dart +++ b/packages/devtools_app/lib/src/shared/analytics/analytics_common.dart @@ -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. /// diff --git a/packages/devtools_app/lib/src/shared/config_specific/drag_and_drop/_drag_and_drop_stub.dart b/packages/devtools_app/lib/src/shared/config_specific/drag_and_drop/_drag_and_drop_stub.dart deleted file mode 100644 index d3e3960a91a..00000000000 --- a/packages/devtools_app/lib/src/shared/config_specific/drag_and_drop/_drag_and_drop_stub.dart +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'drag_and_drop.dart'; - -// ignore: avoid-unused-parameters, method is used from a conditional import -DragAndDropManager createDragAndDropManager(int viewId) { - throw Exception( - 'Attempting to create DragAndDrop for unrecognized platform.', - ); -} diff --git a/packages/devtools_app/lib/src/shared/config_specific/drag_and_drop/_drag_and_drop_web.dart b/packages/devtools_app/lib/src/shared/config_specific/drag_and_drop/_drag_and_drop_web.dart index be251f5bf4e..7b816e52caf 100644 --- a/packages/devtools_app/lib/src/shared/config_specific/drag_and_drop/_drag_and_drop_web.dart +++ b/packages/devtools_app/lib/src/shared/config_specific/drag_and_drop/_drag_and_drop_web.dart @@ -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'; @@ -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 _) { @@ -61,28 +60,31 @@ class DragAndDropManagerWeb extends DragAndDropManager { // handler, return early. if (activeState?.widget.handleDrop == null) return; - final List 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); @@ -97,7 +99,7 @@ class DragAndDropManagerWeb extends DragAndDropManager { }); try { - reader.readAsText(droppedFile); + reader.readAsText(droppedFile!); } catch (e) { notificationService.push('Could not import file: $e'); } diff --git a/packages/devtools_app/lib/src/shared/config_specific/drag_and_drop/drag_and_drop.dart b/packages/devtools_app/lib/src/shared/config_specific/drag_and_drop/drag_and_drop.dart index ba2b45e38c3..25eb106de48 100644 --- a/packages/devtools_app/lib/src/shared/config_specific/drag_and_drop/drag_and_drop.dart +++ b/packages/devtools_app/lib/src/shared/config_specific/drag_and_drop/drag_and_drop.dart @@ -6,9 +6,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import '../../primitives/utils.dart'; -import '_drag_and_drop_stub.dart' - if (dart.library.js_interop) '_drag_and_drop_web.dart' - if (dart.library.io) '_drag_and_drop_desktop.dart'; +import '_drag_and_drop_desktop.dart' + if (dart.library.js_interop) '_drag_and_drop_web.dart'; abstract class DragAndDropManager { factory DragAndDropManager(int viewId) => createDragAndDropManager(viewId); diff --git a/packages/devtools_app/lib/src/shared/config_specific/file/_file_stub.dart b/packages/devtools_app/lib/src/shared/config_specific/file/_file_stub.dart deleted file mode 100644 index 749f8ddfdcf..00000000000 --- a/packages/devtools_app/lib/src/shared/config_specific/file/_file_stub.dart +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'file.dart'; - -FileIO createFileSystem() { - throw Exception('Attempting to create FileSystem for unrecognized platform.'); -} diff --git a/packages/devtools_app/lib/src/shared/config_specific/file/file.dart b/packages/devtools_app/lib/src/shared/config_specific/file/file.dart index 72e153e2abd..777ac053e77 100644 --- a/packages/devtools_app/lib/src/shared/config_specific/file/file.dart +++ b/packages/devtools_app/lib/src/shared/config_specific/file/file.dart @@ -2,9 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import '_file_stub.dart' - if (dart.library.js_interop) '_file_web.dart' - if (dart.library.io) '_file_desktop.dart'; +import '_file_desktop.dart' if (dart.library.js_interop) '_file_web.dart'; abstract class FileIO { factory FileIO() { diff --git a/packages/devtools_app/lib/src/shared/config_specific/framework_initialize/_framework_initialize_stub.dart b/packages/devtools_app/lib/src/shared/config_specific/framework_initialize/_framework_initialize_stub.dart deleted file mode 100644 index f85df93f1da..00000000000 --- a/packages/devtools_app/lib/src/shared/config_specific/framework_initialize/_framework_initialize_stub.dart +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/// Return the url the application is launched from. -Future initializePlatform() { - throw UnimplementedError( - 'Attempting to initialize framework for unrecognized platform', - ); -} diff --git a/packages/devtools_app/lib/src/shared/config_specific/framework_initialize/_framework_initialize_web.dart b/packages/devtools_app/lib/src/shared/config_specific/framework_initialize/_framework_initialize_web.dart index 9fd3412882d..632b2ec3f40 100644 --- a/packages/devtools_app/lib/src/shared/config_specific/framework_initialize/_framework_initialize_web.dart +++ b/packages/devtools_app/lib/src/shared/config_specific/framework_initialize/_framework_initialize_web.dart @@ -2,10 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore: avoid_web_libraries_in_flutter, as designed -import 'dart:html' hide Storage; +import 'dart:js_interop'; import 'package:devtools_app_shared/utils.dart'; +import 'package:devtools_app_shared/web_utils.dart'; +import 'package:web/helpers.dart' hide Storage; import '../../../service/service_manager.dart'; import '../../globals.dart'; @@ -15,9 +16,13 @@ import '../../server/server_api_client.dart'; /// Return the url the application is launched from. Future initializePlatform() async { // Clear out the unneeded HTML from index.html. - for (var element in document.body!.querySelectorAll('.legacy-dart')) { - element.remove(); - } + document.body!.querySelectorAll('.legacy-dart').forEach( + (Node element) { + if (element.parentNode != null) { + element.parentNode!.removeChild(element); + } + }.toJS, + ); // Here, we try and initialize the connection between the DevTools web app and // its local server. DevTools can be launched without the server however, so @@ -77,7 +82,10 @@ void _sendKeyPressToParent(KeyboardEvent event) { 'repeat': event.repeat, 'shiftKey': event.shiftKey, }; - window.parent?.postMessage({'command': 'keydown', 'data': data}, '*'); + window.parent?.postMessage( + {'command': 'keydown', 'data': data}.jsify(), + '*'.toJS, + ); } class ServerConnectionStorage implements Storage { diff --git a/packages/devtools_app/lib/src/shared/config_specific/framework_initialize/framework_initialize.dart b/packages/devtools_app/lib/src/shared/config_specific/framework_initialize/framework_initialize.dart index d8a2d33a925..c315afb4ca5 100644 --- a/packages/devtools_app/lib/src/shared/config_specific/framework_initialize/framework_initialize.dart +++ b/packages/devtools_app/lib/src/shared/config_specific/framework_initialize/framework_initialize.dart @@ -3,9 +3,8 @@ // found in the LICENSE file. import '../../../framework/framework_core.dart'; -import '_framework_initialize_stub.dart' - if (dart.library.js_interop) '_framework_initialize_web.dart' - if (dart.library.io) '_framework_initialize_desktop.dart'; +import '_framework_initialize_desktop.dart' + if (dart.library.js_interop) '_framework_initialize_web.dart'; Future initializeFramework() async { FrameworkCore.initGlobals(); diff --git a/packages/devtools_app/lib/src/shared/config_specific/host_platform/host_platform_desktop.dart b/packages/devtools_app/lib/src/shared/config_specific/host_platform/_host_platform_desktop.dart similarity index 100% rename from packages/devtools_app/lib/src/shared/config_specific/host_platform/host_platform_desktop.dart rename to packages/devtools_app/lib/src/shared/config_specific/host_platform/_host_platform_desktop.dart diff --git a/packages/devtools_app/lib/src/shared/config_specific/host_platform/host_platform_web.dart b/packages/devtools_app/lib/src/shared/config_specific/host_platform/_host_platform_web.dart similarity index 84% rename from packages/devtools_app/lib/src/shared/config_specific/host_platform/host_platform_web.dart rename to packages/devtools_app/lib/src/shared/config_specific/host_platform/_host_platform_web.dart index e812706d52f..607b7184e82 100644 --- a/packages/devtools_app/lib/src/shared/config_specific/host_platform/host_platform_web.dart +++ b/packages/devtools_app/lib/src/shared/config_specific/host_platform/_host_platform_web.dart @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be found // in the LICENSE file. -// ignore: avoid_web_libraries_in_flutter, as designed -import 'dart:html'; +import 'package:web/helpers.dart'; class HostPlatform { HostPlatform._() { diff --git a/packages/devtools_app/lib/src/shared/config_specific/host_platform/host_platform.dart b/packages/devtools_app/lib/src/shared/config_specific/host_platform/host_platform.dart index fedcbef91b7..41808488cf1 100644 --- a/packages/devtools_app/lib/src/shared/config_specific/host_platform/host_platform.dart +++ b/packages/devtools_app/lib/src/shared/config_specific/host_platform/host_platform.dart @@ -2,6 +2,5 @@ // Use of this source code is governed by a BSD-style license that can be found // in the LICENSE file. -export 'host_platform_stub.dart' - if (dart.library.js_interop) 'host_platform_web.dart' - if (dart.library.io) 'host_platform_desktop.dart'; +export '_host_platform_desktop.dart' + if (dart.library.js_interop) '_host_platform_web.dart'; diff --git a/packages/devtools_app/lib/src/shared/config_specific/host_platform/host_platform_stub.dart b/packages/devtools_app/lib/src/shared/config_specific/host_platform/host_platform_stub.dart deleted file mode 100644 index 827c678b088..00000000000 --- a/packages/devtools_app/lib/src/shared/config_specific/host_platform/host_platform_stub.dart +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be found -// in the LICENSE file. - -class HostPlatform { - static final HostPlatform instance = HostPlatform(); - - bool get isMacOS => throw UnimplementedError(); -} diff --git a/packages/devtools_app/lib/src/shared/config_specific/import_export/_export_stub.dart b/packages/devtools_app/lib/src/shared/config_specific/import_export/_export_stub.dart deleted file mode 100644 index a3d8e4dbdb2..00000000000 --- a/packages/devtools_app/lib/src/shared/config_specific/import_export/_export_stub.dart +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'import_export.dart'; - -ExportController createExportController() { - throw Exception( - 'Attempting to create ExportController for unrecognized platform.', - ); -} diff --git a/packages/devtools_app/lib/src/shared/config_specific/import_export/_export_web.dart b/packages/devtools_app/lib/src/shared/config_specific/import_export/_export_web.dart index 3296ac88ced..bf176119336 100644 --- a/packages/devtools_app/lib/src/shared/config_specific/import_export/_export_web.dart +++ b/packages/devtools_app/lib/src/shared/config_specific/import_export/_export_web.dart @@ -2,8 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore: avoid_web_libraries_in_flutter, as designed -import 'dart:html'; +import 'dart:js_interop'; + +import 'package:web/helpers.dart' hide NodeGlue; import 'import_export.dart'; @@ -19,11 +20,14 @@ class ExportControllerWeb extends ExportController { required String content, required String fileName, }) { - final element = document.createElement('a'); - element.setAttribute('href', Url.createObjectUrl(Blob([content]))); + final element = document.createElement('a') as HTMLAnchorElement; + element.setAttribute( + 'href', + URL.createObjectURL(Blob([content.toJS].toJS)), + ); element.setAttribute('download', fileName); element.style.display = 'none'; - document.body!.append(element); + (document.body as HTMLBodyElement).append(element); element.click(); element.remove(); } diff --git a/packages/devtools_app/lib/src/shared/config_specific/import_export/import_export.dart b/packages/devtools_app/lib/src/shared/config_specific/import_export/import_export.dart index 34e5cdfe61e..283e9b96cb2 100644 --- a/packages/devtools_app/lib/src/shared/config_specific/import_export/import_export.dart +++ b/packages/devtools_app/lib/src/shared/config_specific/import_export/import_export.dart @@ -12,9 +12,7 @@ import '../../globals.dart'; import '../../primitives/simple_items.dart'; import '../../primitives/utils.dart'; import '../../screen.dart'; -import '_export_stub.dart' - if (dart.library.js_interop) '_export_web.dart' - if (dart.library.io) '_export_desktop.dart'; +import '_export_desktop.dart' if (dart.library.js_interop) '_export_web.dart'; const nonDevToolsFileMessage = 'The imported file is not a Dart DevTools file.' ' At this time, DevTools only supports importing files that were originally' diff --git a/packages/devtools_app/lib/src/shared/config_specific/launch_url/_launch_url_stub.dart b/packages/devtools_app/lib/src/shared/config_specific/launch_url/_launch_url_stub.dart deleted file mode 100644 index 8b2e8553d8a..00000000000 --- a/packages/devtools_app/lib/src/shared/config_specific/launch_url/_launch_url_stub.dart +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be found -// in the LICENSE file. - -void launchUrlVSCode(String _) { - // Do nothing -} diff --git a/packages/devtools_app/lib/src/shared/config_specific/launch_url/_launch_url_web.dart b/packages/devtools_app/lib/src/shared/config_specific/launch_url/_launch_url_web.dart index 49deb3f4029..d18fb01d847 100644 --- a/packages/devtools_app/lib/src/shared/config_specific/launch_url/_launch_url_web.dart +++ b/packages/devtools_app/lib/src/shared/config_specific/launch_url/_launch_url_web.dart @@ -2,15 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be found // in the LICENSE file. -// ignore: avoid_web_libraries_in_flutter, as designed -import 'dart:html'; +import 'dart:js_interop'; + +import 'package:web/helpers.dart'; void launchUrlVSCode(String url) { window.parent?.postMessage( { 'command': 'launchUrl', 'data': {'url': url}, - }, - '*', + }.jsify(), + '*'.toJS, ); } diff --git a/packages/devtools_app/lib/src/shared/config_specific/launch_url/launch_url.dart b/packages/devtools_app/lib/src/shared/config_specific/launch_url/launch_url.dart index 2da925b87dc..5e871d0050d 100644 --- a/packages/devtools_app/lib/src/shared/config_specific/launch_url/launch_url.dart +++ b/packages/devtools_app/lib/src/shared/config_specific/launch_url/launch_url.dart @@ -5,9 +5,8 @@ import 'package:url_launcher/url_launcher.dart' as url_launcher; import '../../globals.dart'; -import '_launch_url_stub.dart' - if (dart.library.js_interop) '_launch_url_web.dart' - if (dart.library.io) '_launch_url_desktop.dart'; +import '_launch_url_desktop.dart' + if (dart.library.js_interop) '_launch_url_web.dart'; Future launchUrl(String url) async { final parsedUrl = Uri.tryParse(url); diff --git a/packages/devtools_app/lib/src/shared/config_specific/logger/allowed_error_default.dart b/packages/devtools_app/lib/src/shared/config_specific/logger/_allowed_error_default.dart similarity index 100% rename from packages/devtools_app/lib/src/shared/config_specific/logger/allowed_error_default.dart rename to packages/devtools_app/lib/src/shared/config_specific/logger/_allowed_error_default.dart diff --git a/packages/devtools_app/lib/src/shared/config_specific/logger/allowed_error_html.dart b/packages/devtools_app/lib/src/shared/config_specific/logger/_allowed_error_web.dart similarity index 69% rename from packages/devtools_app/lib/src/shared/config_specific/logger/allowed_error_html.dart rename to packages/devtools_app/lib/src/shared/config_specific/logger/_allowed_error_web.dart index 050dab422e1..818531ad32f 100644 --- a/packages/devtools_app/lib/src/shared/config_specific/logger/allowed_error_html.dart +++ b/packages/devtools_app/lib/src/shared/config_specific/logger/_allowed_error_web.dart @@ -2,8 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore: avoid_web_libraries_in_flutter, as designed -import 'dart:html'; +import 'dart:js_interop'; + +import 'package:web/helpers.dart'; /// Catch and print errors from the given future. These errors are part of /// normal operation for an app, and don't need to be reported to analytics @@ -12,10 +13,9 @@ Future allowedError(Future future, {bool logError = true}) { return future.catchError((Object error) { if (logError) { final errorLines = error.toString().split('\n'); - window.console - .groupCollapsed('[${error.runtimeType}] ${errorLines.first}'); - window.console.log(errorLines.skip(1).join('\n')); - window.console.groupEnd(); + console.groupCollapsed('[${error.runtimeType}] ${errorLines.first}'.toJS); + console.log(errorLines.skip(1).join('\n').toJS); + console.groupEnd(); } return Future.value(); }); diff --git a/packages/devtools_app/lib/src/shared/config_specific/logger/logger_io.dart b/packages/devtools_app/lib/src/shared/config_specific/logger/_logger_io.dart similarity index 100% rename from packages/devtools_app/lib/src/shared/config_specific/logger/logger_io.dart rename to packages/devtools_app/lib/src/shared/config_specific/logger/_logger_io.dart diff --git a/packages/devtools_app/lib/src/shared/config_specific/logger/logger_default.dart b/packages/devtools_app/lib/src/shared/config_specific/logger/_logger_web.dart similarity index 67% rename from packages/devtools_app/lib/src/shared/config_specific/logger/logger_default.dart rename to packages/devtools_app/lib/src/shared/config_specific/logger/_logger_web.dart index c02ff06f7ca..4e26fa88247 100644 --- a/packages/devtools_app/lib/src/shared/config_specific/logger/logger_default.dart +++ b/packages/devtools_app/lib/src/shared/config_specific/logger/_logger_web.dart @@ -2,19 +2,22 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: avoid_print +import 'dart:js_interop'; + +import 'package:web/helpers.dart'; import 'logger.dart'; void printToConsole(Object message, [LogLevel level = LogLevel.debug]) { + final jsMessage = message.jsify(); switch (level) { case LogLevel.debug: - print(message); + console.log(jsMessage); break; case LogLevel.warning: - print('[WARNING]: $message'); + console.warn(jsMessage); break; case LogLevel.error: - print('[ERROR]: $message'); + console.error(jsMessage); } } diff --git a/packages/devtools_app/lib/src/shared/config_specific/logger/allowed_error.dart b/packages/devtools_app/lib/src/shared/config_specific/logger/allowed_error.dart index 06ef9959aa1..4595f5e0492 100644 --- a/packages/devtools_app/lib/src/shared/config_specific/logger/allowed_error.dart +++ b/packages/devtools_app/lib/src/shared/config_specific/logger/allowed_error.dart @@ -2,5 +2,5 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -export 'allowed_error_default.dart' - if (dart.library.js_interop) 'allowed_error_html.dart'; +export '_allowed_error_default.dart' + if (dart.library.js_interop) '_allowed_error_web.dart'; diff --git a/packages/devtools_app/lib/src/shared/config_specific/logger/logger.dart b/packages/devtools_app/lib/src/shared/config_specific/logger/logger.dart index fc26be5b5b1..5af1b879253 100644 --- a/packages/devtools_app/lib/src/shared/config_specific/logger/logger.dart +++ b/packages/devtools_app/lib/src/shared/config_specific/logger/logger.dart @@ -2,9 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -export 'logger_default.dart' - if (dart.library.js_interop) 'logger_html.dart' - if (dart.library.io) 'logger_io.dart'; +export '_logger_io.dart' if (dart.library.js_interop) '_logger_web.dart'; enum LogLevel { debug, diff --git a/packages/devtools_app/lib/src/shared/config_specific/logger/logger_html.dart b/packages/devtools_app/lib/src/shared/config_specific/logger/logger_html.dart deleted file mode 100644 index cb8ebe55469..00000000000 --- a/packages/devtools_app/lib/src/shared/config_specific/logger/logger_html.dart +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// ignore: avoid_web_libraries_in_flutter, as designed -import 'dart:html'; - -import 'logger.dart'; - -void printToConsole(Object message, [LogLevel level = LogLevel.debug]) { - switch (level) { - case LogLevel.debug: - window.console.log(message); - break; - case LogLevel.warning: - window.console.warn(message); - break; - case LogLevel.error: - window.console.error(message); - } -} diff --git a/packages/devtools_app/lib/src/shared/config_specific/notifications/notifications_desktop.dart b/packages/devtools_app/lib/src/shared/config_specific/notifications/_notifications_desktop.dart similarity index 100% rename from packages/devtools_app/lib/src/shared/config_specific/notifications/notifications_desktop.dart rename to packages/devtools_app/lib/src/shared/config_specific/notifications/_notifications_desktop.dart diff --git a/packages/devtools_app/lib/src/shared/config_specific/notifications/_notifications_web.dart b/packages/devtools_app/lib/src/shared/config_specific/notifications/_notifications_web.dart new file mode 100644 index 00000000000..36e52d18fd9 --- /dev/null +++ b/packages/devtools_app/lib/src/shared/config_specific/notifications/_notifications_web.dart @@ -0,0 +1,27 @@ +// Copyright 2020 The Chromium Authors. 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:js_util'; + +import 'package:web/helpers.dart' as web_helpers; +import 'package:web/web.dart'; + +class Notification { + Notification(String title, {String body = ''}) { + _impl = web_helpers.Notification( + title, + NotificationOptions(body: body), + ); + } + + late final web_helpers.Notification _impl; + + static Future requestPermission() { + return promiseToFuture(web_helpers.Notification.requestPermission()); + } + + void close() { + _impl.close(); + } +} diff --git a/packages/devtools_app/lib/src/shared/config_specific/notifications/notifications.dart b/packages/devtools_app/lib/src/shared/config_specific/notifications/notifications.dart index 764a6db3e58..6c533fa0335 100644 --- a/packages/devtools_app/lib/src/shared/config_specific/notifications/notifications.dart +++ b/packages/devtools_app/lib/src/shared/config_specific/notifications/notifications.dart @@ -2,6 +2,5 @@ // Use of this source code is governed by a BSD-style license that can be found // in the LICENSE file. -export 'notifications_stub.dart' - if (dart.library.js_interop) 'notifications_web.dart' - if (dart.library.io) 'notifications_desktop.dart'; +export '_notifications_desktop.dart' + if (dart.library.js_interop) '_notifications_web.dart'; diff --git a/packages/devtools_app/lib/src/shared/config_specific/notifications/notifications_stub.dart b/packages/devtools_app/lib/src/shared/config_specific/notifications/notifications_stub.dart deleted file mode 100644 index 9af62a0e77a..00000000000 --- a/packages/devtools_app/lib/src/shared/config_specific/notifications/notifications_stub.dart +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be found -// in the LICENSE file. - -class Notification { - Notification(String title, {String? body}) { - throw Exception('unsupported platform'); - } - - static Future requestPermission() { - throw Exception('unsupported platform'); - } - - void close() { - throw Exception('unsupported platform'); - } -} diff --git a/packages/devtools_app/lib/src/shared/config_specific/notifications/notifications_web.dart b/packages/devtools_app/lib/src/shared/config_specific/notifications/notifications_web.dart deleted file mode 100644 index f05e8cef3cc..00000000000 --- a/packages/devtools_app/lib/src/shared/config_specific/notifications/notifications_web.dart +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be found -// in the LICENSE file. - -// ignore: avoid_web_libraries_in_flutter, as designed -import 'dart:html' as html; - -class Notification { - Notification(String title, {String? body}) { - _impl = html.Notification(title, body: body); - } - - late final html.Notification _impl; - - static Future requestPermission() { - return html.Notification.requestPermission(); - } - - void close() { - _impl.close(); - } -} diff --git a/packages/devtools_app/lib/src/shared/config_specific/post_message/post_message_stub.dart b/packages/devtools_app/lib/src/shared/config_specific/post_message/_post_message_stub.dart similarity index 100% rename from packages/devtools_app/lib/src/shared/config_specific/post_message/post_message_stub.dart rename to packages/devtools_app/lib/src/shared/config_specific/post_message/_post_message_stub.dart diff --git a/packages/devtools_app/lib/src/shared/config_specific/post_message/post_message_web.dart b/packages/devtools_app/lib/src/shared/config_specific/post_message/_post_message_web.dart similarity index 52% rename from packages/devtools_app/lib/src/shared/config_specific/post_message/post_message_web.dart rename to packages/devtools_app/lib/src/shared/config_specific/post_message/_post_message_web.dart index 7630660cd01..a3a9ae0b520 100644 --- a/packages/devtools_app/lib/src/shared/config_specific/post_message/post_message_web.dart +++ b/packages/devtools_app/lib/src/shared/config_specific/post_message/_post_message_web.dart @@ -2,7 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be found // in the LICENSE file. +// TODO(https://github.com/flutter/devtools/issues/6606): remove this import. +// This is the final dart:html import in DevTools. In order to remove it, we +// need to bump the `package:web` version in DevTools to > 0.3.1, but we are +// blocked on `package:web` rolling into the Flutter SDK. import 'dart:html' as html; +import 'dart:js_interop'; + +import 'package:web/helpers.dart'; import 'post_message.dart'; @@ -16,4 +23,4 @@ Stream get onPostMessage { } void postMessage(Object? message, String targetOrigin) => - html.window.parent?.postMessage(message, targetOrigin); + window.parent?.postMessage(message.jsify(), targetOrigin.toJS); diff --git a/packages/devtools_app/lib/src/shared/config_specific/post_message/post_message.dart b/packages/devtools_app/lib/src/shared/config_specific/post_message/post_message.dart index 8ffd6a5ca37..a13c74d73a5 100644 --- a/packages/devtools_app/lib/src/shared/config_specific/post_message/post_message.dart +++ b/packages/devtools_app/lib/src/shared/config_specific/post_message/post_message.dart @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be found // in the LICENSE file. -export 'post_message_stub.dart' - if (dart.library.js_interop) 'post_message_web.dart'; +export '_post_message_stub.dart' + if (dart.library.js_interop) '_post_message_web.dart'; class PostMessageEvent { PostMessageEvent({ diff --git a/packages/devtools_app/lib/src/shared/console/eval/inspector_tree.dart b/packages/devtools_app/lib/src/shared/console/eval/inspector_tree.dart index 17fd2108025..d2e29788216 100644 --- a/packages/devtools_app/lib/src/shared/console/eval/inspector_tree.dart +++ b/packages/devtools_app/lib/src/shared/console/eval/inspector_tree.dart @@ -2,13 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -/// Inspector specific tree rendering support designed to be extendable to work -/// either directly with dart:html or with Hummingbird. +/// Inspector specific tree rendering support. /// -/// This library must not have direct dependencies on dart:html. +/// This library must not have direct dependencies on web-only libraries. /// -/// This allows tests of the complicated logic in this class to run on the VM -/// and will help simplify porting this code to work with Hummingbird. +/// This allows tests of the complicated logic in this class to run on the VM. library inspector_tree; diff --git a/packages/devtools_app/test/shared/visible_screens_test.dart b/packages/devtools_app/test/shared/visible_screens_test.dart index fdd2938707d..eaa2f0dced7 100644 --- a/packages/devtools_app/test/shared/visible_screens_test.dart +++ b/packages/devtools_app/test/shared/visible_screens_test.dart @@ -46,7 +46,7 @@ void main() { }) { if (web) { fakeServiceConnection.serviceManager.availableLibraries - .add('dart:html'); + .add('dart:js_interop'); } mockConnectedApp( fakeServiceConnection.serviceManager.connectedApp!, diff --git a/packages/devtools_app_shared/CHANGELOG.md b/packages/devtools_app_shared/CHANGELOG.md index 96404c697de..f0bbb81b9d9 100644 --- a/packages/devtools_app_shared/CHANGELOG.md +++ b/packages/devtools_app_shared/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.0.8 +* Migrate from `dart:html` to `package:web`. + ## 0.0.7 * Bump minimum Dart SDK version to `3.3.0-91.0.dev` and minimum Flutter SDK version to `3.17.0-0.0.pre`. * Bump `package:vm_service` dependency to ^13.0.0. diff --git a/packages/devtools_app_shared/lib/src/service/connected_app.dart b/packages/devtools_app_shared/lib/src/service/connected_app.dart index 81505961b8c..118e47cf4cf 100644 --- a/packages/devtools_app_shared/lib/src/service/connected_app.dart +++ b/packages/devtools_app_shared/lib/src/service/connected_app.dart @@ -14,6 +14,9 @@ import 'service_manager.dart'; final _log = Logger('connected_app'); const flutterLibraryUri = 'package:flutter/src/widgets/binding.dart'; + +// TODO(kenz): if we want to support debugging dart2wasm web apps, we will need +// to check for the presence of a different library. const dartHtmlLibraryUri = 'dart:html'; // TODO(https://github.com/flutter/devtools/issues/6239): try to remove this. diff --git a/packages/devtools_app_shared/lib/src/ui/theme/_ide_theme_web.dart b/packages/devtools_app_shared/lib/src/ui/theme/_ide_theme_web.dart index 3e143d6e93e..48aa88a2893 100644 --- a/packages/devtools_app_shared/lib/src/ui/theme/_ide_theme_web.dart +++ b/packages/devtools_app_shared/lib/src/ui/theme/_ide_theme_web.dart @@ -2,11 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be found // in the LICENSE file. -// ignore: avoid_web_libraries_in_flutter, as designed -import 'dart:html'; - import 'package:flutter/widgets.dart'; import 'package:logging/logging.dart'; +import 'package:web/helpers.dart'; import '../../utils/url/url.dart'; import '../../utils/utils.dart'; diff --git a/packages/devtools_app_shared/lib/src/utils/url/_url_web.dart b/packages/devtools_app_shared/lib/src/utils/url/_url_web.dart index 2329882f28b..cc240f0f754 100644 --- a/packages/devtools_app_shared/lib/src/utils/url/_url_web.dart +++ b/packages/devtools_app_shared/lib/src/utils/url/_url_web.dart @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be found // in the LICENSE file. -// ignore: avoid_web_libraries_in_flutter, as designed -import 'dart:html'; +import 'package:web/helpers.dart'; Map loadQueryParams({String Function(String)? urlModifier}) { var url = getWebUrl()!; diff --git a/packages/devtools_app_shared/lib/src/utils/web_utils.dart b/packages/devtools_app_shared/lib/src/utils/web_utils.dart new file mode 100644 index 00000000000..cab67a71fd6 --- /dev/null +++ b/packages/devtools_app_shared/lib/src/utils/web_utils.dart @@ -0,0 +1,15 @@ +// Copyright 2020 The Chromium Authors. 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:js_interop'; + +import 'package:web/helpers.dart'; + +extension MessageExtension on Event { + bool get isMessageEvent => instanceOfString('MessageEvent'); +} + +extension NodeListExtension on NodeList { + external void forEach(JSFunction callback); +} diff --git a/packages/devtools_app_shared/lib/web_utils.dart b/packages/devtools_app_shared/lib/web_utils.dart new file mode 100644 index 00000000000..65e4a695f8d --- /dev/null +++ b/packages/devtools_app_shared/lib/web_utils.dart @@ -0,0 +1,5 @@ +// Copyright 2023 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +export 'src/utils/web_utils.dart'; diff --git a/packages/devtools_app_shared/pubspec.yaml b/packages/devtools_app_shared/pubspec.yaml index 4253c82e13a..369c67e6bcd 100644 --- a/packages/devtools_app_shared/pubspec.yaml +++ b/packages/devtools_app_shared/pubspec.yaml @@ -16,6 +16,7 @@ dependencies: meta: ^1.9.1 pointer_interceptor: ^0.9.3+3 vm_service: ^13.0.0 + web: ^0.3.0 dev_dependencies: flutter_lints: ^2.0.3 diff --git a/packages/devtools_extensions/CHANGELOG.md b/packages/devtools_extensions/CHANGELOG.md index ab003abbb40..3daf296f316 100644 --- a/packages/devtools_extensions/CHANGELOG.md +++ b/packages/devtools_extensions/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.11 +* Migrate from `dart:html` to `package:web`. +* Add `utils.dart` library with helper for message event parsing. + ## 0.0.10 * Bump minimum Dart SDK version to `3.3.0-91.0.dev` and minimum Flutter SDK version to `3.17.0-0.0.pre`. * Add a test target to the `app_that_uses_foo` example that can also be debugged diff --git a/packages/devtools_extensions/lib/src/template/_simulated_devtools_environment/_simulated_devtools_controller.dart b/packages/devtools_extensions/lib/src/template/_simulated_devtools_environment/_simulated_devtools_controller.dart index 47d940f76ea..76cfb594c9f 100644 --- a/packages/devtools_extensions/lib/src/template/_simulated_devtools_environment/_simulated_devtools_controller.dart +++ b/packages/devtools_extensions/lib/src/template/_simulated_devtools_environment/_simulated_devtools_controller.dart @@ -17,12 +17,12 @@ class SimulatedDevToolsController extends DisposableController /// /// We need to store this in a variable so that the listener is properly /// removed in [dispose]. - html.EventListener? _handleMessageListener; + EventListener? _handleMessageListener; void init() { - html.window.addEventListener( + window.addEventListener( 'message', - _handleMessageListener = _handleMessage, + _handleMessageListener = _handleMessage.toJS, ); addAutoDisposeListener(serviceManager.connectedState, () { if (!serviceManager.connectedState.value.connected) { @@ -32,22 +32,20 @@ class SimulatedDevToolsController extends DisposableController }); } - void _handleMessage(html.Event e) { - if (e is html.MessageEvent) { - final extensionEvent = DevToolsExtensionEvent.tryParse(e.data); - if (extensionEvent != null) { - // Do not handle messages that come from the - // [_SimulatedDevToolsController] itself. - if (extensionEvent.source == '$SimulatedDevToolsController') return; + void _handleMessage(Event e) { + final extensionEvent = tryParseExtensionEvent(e); + if (extensionEvent != null) { + // Do not handle messages that come from the + // [_SimulatedDevToolsController] itself. + if (extensionEvent.source == '$SimulatedDevToolsController') return; - onEventReceived(extensionEvent); - } + onEventReceived(extensionEvent); } } @override void dispose() { - html.window.removeEventListener('message', _handleMessageListener); + window.removeEventListener('message', _handleMessageListener); _handleMessageListener = null; super.dispose(); } @@ -106,12 +104,12 @@ class SimulatedDevToolsController extends DisposableController void _postMessageToExtension(DevToolsExtensionEvent event) { final eventJson = event.toJson(); - html.window.postMessage( + window.postMessage( { ...eventJson, DevToolsExtensionEvent.sourceKey: '$SimulatedDevToolsController', - }, - html.window.origin!, + }.jsify(), + window.origin.toJS, ); messageLogs.add( MessageLogEntry( diff --git a/packages/devtools_extensions/lib/src/template/_simulated_devtools_environment/_simulated_devtools_environment.dart b/packages/devtools_extensions/lib/src/template/_simulated_devtools_environment/_simulated_devtools_environment.dart index d2f5a4c5e78..62fce86fe2c 100644 --- a/packages/devtools_extensions/lib/src/template/_simulated_devtools_environment/_simulated_devtools_environment.dart +++ b/packages/devtools_extensions/lib/src/template/_simulated_devtools_environment/_simulated_devtools_environment.dart @@ -3,17 +3,18 @@ // 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/service.dart'; import 'package:devtools_app_shared/ui.dart'; import 'package:devtools_app_shared/utils.dart'; import 'package:devtools_shared/devtools_shared.dart'; import 'package:flutter/material.dart'; +import 'package:web/helpers.dart' hide Text; import '../../api/api.dart'; import '../../api/model.dart'; +import '../../utils.dart'; import '../devtools_extension.dart'; part '_connect_ui.dart'; diff --git a/packages/devtools_extensions/lib/src/template/devtools_extension.dart b/packages/devtools_extensions/lib/src/template/devtools_extension.dart index e4bcea0f5df..232392fdb26 100644 --- a/packages/devtools_extensions/lib/src/template/devtools_extension.dart +++ b/packages/devtools_extensions/lib/src/template/devtools_extension.dart @@ -3,8 +3,7 @@ // 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/service.dart'; import 'package:devtools_app_shared/ui.dart'; @@ -13,10 +12,12 @@ import 'package:devtools_shared/service.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; -import 'package:vm_service/vm_service.dart'; +import 'package:vm_service/vm_service.dart' hide Event; +import 'package:web/helpers.dart' hide Text; import '../api/api.dart'; import '../api/model.dart'; +import '../utils.dart'; import '_simulated_devtools_environment/_simulated_devtools_environment.dart'; part 'extension_manager.dart'; diff --git a/packages/devtools_extensions/lib/src/template/extension_manager.dart b/packages/devtools_extensions/lib/src/template/extension_manager.dart index 63c8ba2ce6b..6f642438bdf 100644 --- a/packages/devtools_extensions/lib/src/template/extension_manager.dart +++ b/packages/devtools_extensions/lib/src/template/extension_manager.dart @@ -39,13 +39,13 @@ class ExtensionManager { /// /// We need to store this in a variable so that the listener is properly /// removed in [dispose]. - html.EventListener? _handleMessageListener; + EventListener? _handleMessageListener; // ignore: unused_element, false positive due to part files void _init({required bool connectToVmService}) { - html.window.addEventListener( + window.addEventListener( 'message', - _handleMessageListener = _handleMessage, + _handleMessageListener = _handleMessage.toJS, ); // TODO(kenz): handle the ide theme that may be part of the query params. @@ -73,22 +73,20 @@ class ExtensionManager { // ignore: unused_element, false positive due to part files void _dispose() { _registeredEventHandlers.clear(); - html.window.removeEventListener('message', _handleMessageListener); + window.removeEventListener('message', _handleMessageListener); _handleMessageListener = null; } - void _handleMessage(html.Event e) { - if (e is html.MessageEvent) { - final extensionEvent = DevToolsExtensionEvent.tryParse(e.data); - if (extensionEvent != null) { - _handleExtensionEvent(extensionEvent, e); - } + void _handleMessage(Event e) { + final extensionEvent = tryParseExtensionEvent(e); + if (extensionEvent != null) { + _handleExtensionEvent(extensionEvent, e as MessageEvent); } } void _handleExtensionEvent( DevToolsExtensionEvent extensionEvent, - html.MessageEvent e, + MessageEvent e, ) { // Ignore events that come from the [ExtensionManager] itself. if (extensionEvent.source == '$ExtensionManager') return; @@ -118,7 +116,7 @@ class ExtensionManager { _setThemeForValue(value); break; case DevToolsExtensionEventType.forceReload: - html.window.location.reload(); + window.location.reload(); default: _log.warning( 'Unrecognized event received by extension: ' @@ -139,14 +137,13 @@ class ExtensionManager { DevToolsExtensionEvent event, { String? targetOrigin, }) { - final postWindow = - _useSimulatedEnvironment ? html.window : html.window.parent; + final postWindow = _useSimulatedEnvironment ? window : window.parent; postWindow?.postMessage( { ...event.toJson(), DevToolsExtensionEvent.sourceKey: '$ExtensionManager', - }, - targetOrigin ?? html.window.origin!, + }.jsify(), + (targetOrigin ?? window.origin).toJS, ); } @@ -264,10 +261,10 @@ class ExtensionManager { } else { newQueryParams[key] = value; } - final newUri = Uri.parse(html.window.location.toString()) + final newUri = Uri.parse(window.location.toString()) .replace(queryParameters: newQueryParams); - html.window.history.replaceState( - html.window.history.state, + window.history.replaceState( + window.history.state, '', newUri.toString(), ); diff --git a/packages/devtools_extensions/lib/src/utils.dart b/packages/devtools_extensions/lib/src/utils.dart new file mode 100644 index 00000000000..8fd868910e0 --- /dev/null +++ b/packages/devtools_extensions/lib/src/utils.dart @@ -0,0 +1,18 @@ +// Copyright 2023 The Chromium Authors. 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:js_interop'; + +import 'package:devtools_app_shared/web_utils.dart'; +import 'package:web/helpers.dart'; + +import '../api.dart'; + +DevToolsExtensionEvent? tryParseExtensionEvent(Event e) { + if (e.isMessageEvent) { + final messageData = (e as MessageEvent).data.dartify()!; + return DevToolsExtensionEvent.tryParse(messageData); + } + return null; +} diff --git a/packages/devtools_extensions/lib/utils.dart b/packages/devtools_extensions/lib/utils.dart new file mode 100644 index 00000000000..f7c25035326 --- /dev/null +++ b/packages/devtools_extensions/lib/utils.dart @@ -0,0 +1,7 @@ +// Copyright 2023 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +library utils; + +export 'src/utils.dart'; diff --git a/packages/devtools_extensions/pubspec.yaml b/packages/devtools_extensions/pubspec.yaml index a9ed24c681a..810d2892b41 100644 --- a/packages/devtools_extensions/pubspec.yaml +++ b/packages/devtools_extensions/pubspec.yaml @@ -21,6 +21,7 @@ dependencies: path: ^1.8.0 logging: ^1.1.1 vm_service: ^13.0.0 + web: ^0.3.0 dev_dependencies: flutter_driver: diff --git a/packages/devtools_shared/lib/src/sse/_fake_sse.dart b/packages/devtools_shared/lib/src/sse/_fake_sse.dart index 97743ee3edb..f33cb885647 100644 --- a/packages/devtools_shared/lib/src/sse/_fake_sse.dart +++ b/packages/devtools_shared/lib/src/sse/_fake_sse.dart @@ -6,8 +6,7 @@ import 'dart:async'; /// A shim that imitates the interface of SseClient from package:sse. /// -/// This allows us to run DevTools in environments that don't have dart:html -/// available, like the Flutter desktop embedder. +/// This allows us to run DevTools with Flutter Desktop. // TODO(https://github.com/flutter/devtools/issues/1122): Make SSE work without dart:html. class SseClient { SseClient(String endpoint, {String? debugKey});