// Copyright (c) 2019, 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.

import 'dart:js_interop';
import 'package:web/web.dart';

// According to the CSP3 spec a nonce must be a valid base64 string.
final _noncePattern = RegExp('^[\\w+/_-]+[=]{0,2}\$');

/// Returns CSP nonce, if set for any script tag.
String? _findNonce() {
  final elements = window.document.querySelectorAll('script');

  for (var i = 0; i < elements.length; i++) {
    final element = elements.item(i);
    final nonceValue = (element as HTMLElement).nonce;
    if (_noncePattern.hasMatch(nonceValue)) {
      return nonceValue;
    }
  }
  return null;
}

/// Creates a script that will run properly when strict CSP is enforced.
///
/// More specifically, the script has the correct `nonce` value set.
final HTMLElement Function() _createScript =
    (() {
      final nonce = _findNonce();

      if (nonce == null) {
        return () => document.createElement('script') as HTMLElement;
      }
      return () {
        final scriptElement = document.createElement('script') as HTMLElement;
        return scriptElement..setAttribute('nonce', nonce);
      };
    })();

/// Runs `window.$dartRunMain()` by injecting a script tag.
///
/// We do this so that we don't see user exceptions bubble up in our own error
/// handling zone.
void runMain() {
  final scriptElement =
      _createScript()..innerHTML = r'window.$dartRunMain();'.toJS;
  document.body!.append(scriptElement.jsify()!);
  // External tear-offs are not allowed.
  // ignore: unnecessary_lambdas
  Future.microtask(() => scriptElement.remove());
}
