blob: 549007727b3639c5db51cb9f3dc5cca300729275 [file] [log] [blame]
// 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 detector;
import 'dart:convert';
import 'dart:html';
import 'dart:js_util';
import 'package:dwds/data/debug_info.dart';
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<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 (var 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 =
serializers.deserialize(jsonDecode(debugInfoJson)) as DebugInfo?;
final appOrigin = debugInfo?.appOrigin;
if (appOrigin != null) {
window.postMessage('dart-auth-request', appOrigin);
}
}