// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. @JS() library; import 'dart:convert'; // TODO: https://github.com/dart-lang/webdev/issues/2508 // ignore: deprecated_member_use import 'dart:html'; // TODO: https://github.com/dart-lang/webdev/issues/2508 // ignore: deprecated_member_use import 'dart:js_util'; import 'package:dwds/data/debug_info.dart'; // TODO: https://github.com/dart-lang/webdev/issues/2508 // ignore: deprecated_member_use import 'package:js/js.dart'; import 'chrome_api.dart'; import 'data_serializers.dart'; import 'logger.dart'; import 'messaging.dart'; const _multipleAppsAttribute = 'data-multiple-dart-apps'; void main() { _registerListeners(); } void _registerListeners() { document.addEventListener('dart-app-ready', _onDartAppReadyEvent); document.addEventListener('dart-auth-response', _onDartAuthEvent); } Future _onDartAppReadyEvent(Event event) async { final debugInfo = getProperty(event, 'detail') as String?; if (debugInfo == null) { debugWarn( 'No debug info sent with ready event, instead reading from Window.', ); _injectDebugInfoScript(); } else { await _sendMessageToBackgroundScript( type: MessageType.debugInfo, body: debugInfo, ); _sendAuthRequest(debugInfo); _detectMultipleDartApps(); } } Future _onDartAuthEvent(Event event) async { final isAuthenticated = getProperty(event, 'detail') as String?; if (isAuthenticated == null) return; await _sendMessageToBackgroundScript( type: MessageType.isAuthenticated, body: isAuthenticated, ); } void _detectMultipleDartApps() { final documentElement = document.documentElement; if (documentElement == null) return; if (documentElement.hasAttribute(_multipleAppsAttribute)) { _sendMessageToBackgroundScript( type: MessageType.multipleAppsDetected, body: 'true', ); return; } final multipleAppsObserver = MutationObserver( _detectMultipleDartAppsCallback, ); multipleAppsObserver.observe( documentElement, attributeFilter: [_multipleAppsAttribute], ); } void _detectMultipleDartAppsCallback( List mutations, MutationObserver observer, ) { for (final mutation in mutations) { if (_isMultipleAppsMutation(mutation)) { _sendMessageToBackgroundScript( type: MessageType.multipleAppsDetected, body: 'true', ); observer.disconnect(); } } } bool _isMultipleAppsMutation(dynamic mutation) { final isAttributeMutation = hasProperty(mutation, 'type') && getProperty(mutation, 'type') == 'attributes'; if (isAttributeMutation) { return hasProperty(mutation, 'attributeName') && getProperty(mutation, 'attributeName') == _multipleAppsAttribute; } return false; } // TODO(elliette): Remove once DWDS 17.0.0 is in Flutter stable. If we are on an // older version of DWDS, then the debug info is not sent along with the ready // event. Therefore we must read it from the Window object, which is slower. void _injectDebugInfoScript() { final script = document.createElement('script'); final scriptSrc = chrome.runtime.getURL('debug_info.dart.js'); script.setAttribute('src', scriptSrc); script.setAttribute('defer', true); document.head?.append(script); } Future _sendMessageToBackgroundScript({ required MessageType type, required String body, }) async { await sendRuntimeMessage( type: type, body: body, sender: Script.detector, recipient: Script.background, ); } void _sendAuthRequest(String debugInfoJson) { final debugInfo = serializers.deserialize(jsonDecode(debugInfoJson)) as DebugInfo?; final appOrigin = debugInfo?.appOrigin; if (appOrigin != null) { window.postMessage('dart-auth-request', appOrigin); } }