| // 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 '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<void> _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<void> _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<dynamic> 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<void> _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 = DebugInfo.fromJson( | |
| jsonDecode(debugInfoJson) as List<dynamic>, | |
| ); | |
| final appOrigin = debugInfo.appOrigin; | |
| if (appOrigin != null) { | |
| window.postMessage('dart-auth-request', appOrigin); | |
| } | |
| } |