From 3568f00e126c99fbf7984d3bc9ae1e5ba933caa2 Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Wed, 13 Dec 2023 15:51:46 +0100 Subject: [PATCH 1/9] feat(core, web): migrate web to js_interop to be compatible with WASM --- .../lib/firebase_core_web.dart | 5 +- .../lib/src/firebase_core_web.dart | 43 +++-- .../lib/src/interop/app_interop.dart | 6 +- .../lib/src/interop/core_interop.dart | 16 +- .../firebase_core_web/lib/src/interop/js.dart | 141 ---------------- .../lib/src/interop/package_web_tweaks.dart | 38 +++++ .../lib/src/interop/utils/es6_interop.dart | 1 + .../lib/src/interop/utils/utils.dart | 156 +++++++++--------- .../firebase_core_web/pubspec.yaml | 3 +- 9 files changed, 166 insertions(+), 243 deletions(-) delete mode 100644 packages/firebase_core/firebase_core_web/lib/src/interop/js.dart create mode 100644 packages/firebase_core/firebase_core_web/lib/src/interop/package_web_tweaks.dart diff --git a/packages/firebase_core/firebase_core_web/lib/firebase_core_web.dart b/packages/firebase_core/firebase_core_web/lib/firebase_core_web.dart index 6b9b24f0c378..a1d697a68781 100644 --- a/packages/firebase_core/firebase_core_web/lib/firebase_core_web.dart +++ b/packages/firebase_core/firebase_core_web/lib/firebase_core_web.dart @@ -6,15 +6,16 @@ library firebase_core_web; import 'dart:async'; -import 'dart:html'; import 'dart:js'; +import 'dart:js_interop'; import 'dart:js_util'; import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart'; -import 'package:firebase_core_web/src/interop/js.dart'; +import 'package:firebase_core_web/src/interop/package_web_tweaks.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:js/js_util.dart' as js_util; import 'package:meta/meta.dart'; +import 'package:web/web.dart' as web; import 'src/interop/core.dart' as firebase; diff --git a/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart b/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart index 9d8744d51601..c8402cfc95e0 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart @@ -104,27 +104,26 @@ class FirebaseCoreWeb extends FirebasePlatform { /// document. @visibleForTesting Future injectSrcScript(String src, String windowVar) async { - DomTrustedScriptUrl? trustedUrl; - final trustedPolicyName = _defaultTrustedPolicyName + windowVar; - if (trustedTypes != null) { - console.debug( - 'TrustedTypes available. Creating policy:', - trustedPolicyName, + web.TrustedScriptURL? trustedUrl; + final trustedTypePolicyName = _defaultTrustedPolicyName + windowVar; + if (web.window.nullableTrustedTypes != null) { + web.console.debug( + 'TrustedTypes available. Creating policy: $trustedTypePolicyName'.toJS, ); - final DomTrustedTypePolicyFactory factory = trustedTypes!; try { - final DomTrustedTypePolicy policy = factory.createPolicy( - trustedPolicyName, - DomTrustedTypePolicyOptions( - createScriptURL: allowInterop((String url) => src), + final web.TrustedTypePolicy policy = + web.window.trustedTypes.createPolicy( + trustedTypePolicyName, + web.TrustedTypePolicyOptions( + createScriptURL: ((JSString url) => src).toJS, ), ); - trustedUrl = policy.createScriptURL(src); + trustedUrl = policy.createScriptURLNoArgs(src); } catch (e) { - rethrow; + throw TrustedTypesException(e.toString()); } } - ScriptElement script = ScriptElement(); + web.HTMLScriptElement script = web.HTMLScriptElement(); script.type = 'text/javascript'; script.crossOrigin = 'anonymous'; script.text = ''' @@ -134,8 +133,8 @@ class FirebaseCoreWeb extends FirebasePlatform { }; '''; - assert(document.head != null); - document.head!.append(script); + assert(web.document.head != null); + web.document.head!.append(script); Completer completer = Completer(); @@ -367,3 +366,15 @@ R guardNotInitialized(R Function() cb) { _handleException(error, stackTrace); } } + +/// Exception thrown if the Trusted Types feature is supported, enabled, and it +/// has prevented this loader from injecting the JS SDK. +class TrustedTypesException implements Exception { + /// + TrustedTypesException(this.message); + + /// The message of the exception + final String message; + @override + String toString() => 'TrustedTypesException: $message'; +} diff --git a/packages/firebase_core/firebase_core_web/lib/src/interop/app_interop.dart b/packages/firebase_core/firebase_core_web/lib/src/interop/app_interop.dart index 0f01b0696124..a815b464aa9d 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/interop/app_interop.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/interop/app_interop.dart @@ -9,10 +9,14 @@ library firebase_interop.core.app; import 'package:js/js.dart'; + import 'core_interop.dart'; @JS('FirebaseApp') -abstract class AppJsImpl { +@staticInterop +abstract class AppJsImpl {} + +extension AppJsImplExtension on AppJsImpl { external String get name; external FirebaseOptions get options; } diff --git a/packages/firebase_core/firebase_core_web/lib/src/interop/core_interop.dart b/packages/firebase_core/firebase_core_web/lib/src/interop/core_interop.dart index f8bde927eb86..bbf5b208d3b2 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/interop/core_interop.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/interop/core_interop.dart @@ -8,11 +8,13 @@ @JS('firebase_core') library firebase_interop.core; +import 'dart:js_interop'; + import 'package:firebase_core_web/firebase_core_web_interop.dart'; -import 'package:js/js.dart'; @JS() -external List getApps(); +// TODO FIX +List getApps() => throw UnimplementedError(); /// The current SDK version. /// @@ -27,7 +29,7 @@ external AppJsImpl initializeApp(FirebaseOptions options, [String? name]); external AppJsImpl getApp([String? name]); @JS() -external PromiseJsImpl deleteApp(AppJsImpl app); +external JSPromise deleteApp(AppJsImpl app); /// FirebaseError is a subclass of the standard Error object. /// In addition to a message string, it contains a string-valued code. @@ -35,7 +37,10 @@ external PromiseJsImpl deleteApp(AppJsImpl app); /// See: . @JS() @anonymous -abstract class FirebaseError { +@staticInterop +abstract class FirebaseError {} + +extension FirebaseErrorExtension on FirebaseError { external String get code; external String get message; external String get name; @@ -48,6 +53,7 @@ abstract class FirebaseError { /// A structure for options provided to Firebase. @JS() @anonymous +@staticInterop class FirebaseOptions { external factory FirebaseOptions({ String? apiKey, @@ -59,7 +65,9 @@ class FirebaseOptions { String? measurementId, String? appId, }); +} +extension FirebaseOptionsExtension on FirebaseOptions { external String get apiKey; external set apiKey(String s); external String get authDomain; diff --git a/packages/firebase_core/firebase_core_web/lib/src/interop/js.dart b/packages/firebase_core/firebase_core_web/lib/src/interop/js.dart deleted file mode 100644 index 0a720243449d..000000000000 --- a/packages/firebase_core/firebase_core_web/lib/src/interop/js.dart +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/* -// DOM shim. This file contains everything we need from the DOM API written as -// @staticInterop, so we don't need dart:html -// https://developer.mozilla.org/en-US/docs/Web/API/ -*/ - -import 'package:js/js.dart'; - -/// console interface -@JS() -@staticInterop -@anonymous -abstract class DomConsole {} - -/// The interface of window.console -extension DomConsoleExtension on DomConsole { - /// console.debug - external DomConsoleDumpFn get debug; - - /// console.info - external DomConsoleDumpFn get info; - - /// console.log - external DomConsoleDumpFn get log; - - /// console.warn - external DomConsoleDumpFn get warn; - - /// console.error - external DomConsoleDumpFn get error; -} - -/// Fakey variadic-type for console-dumping methods (like console.log or info). -typedef DomConsoleDumpFn = void Function( - Object? arg, [ - Object? arg2, - Object? arg3, - Object? arg4, - Object? arg5, - Object? arg6, - Object? arg7, - Object? arg8, - Object? arg9, - Object? arg10, -]); - -/// Error object -@JS('Error') -@staticInterop -abstract class DomError {} - -/// Methods on the error object -extension DomErrorExtension on DomError { - /// Error message. - external String? get message; - - /// Stack trace. - external String? get stack; - - /// Error name. This is determined by the constructor function. - external String get name; - - /// Error cause indicating the reason why the current error is thrown. - /// - /// This is usually another caught error, or the value provided as the `cause` - /// property of the Error constructor's second argument. - external Object? get cause; -} - -/* -// Trusted Types API (TrustedTypePolicy, TrustedScript, TrustedScriptURL) -// https://developer.mozilla.org/en-US/docs/Web/API/TrustedTypesAPI -*/ - -/// A factory to create `TrustedTypePolicy` objects. -@JS() -@staticInterop -@anonymous -abstract class DomTrustedTypePolicyFactory {} - -/// (Some) methods of the [DomTrustedTypePolicyFactory]: -extension DomTrustedTypePolicyFactoryExtension on DomTrustedTypePolicyFactory { - /// createPolicy - external DomTrustedTypePolicy createPolicy( - String policyName, - DomTrustedTypePolicyOptions? policyOptions, - ); -} - -/// Options to create a trusted type policy. -@JS() -@staticInterop -@anonymous -abstract class DomTrustedTypePolicyOptions { - /// Constructs a TrustedPolicyOptions object in JavaScript. - /// - /// The following properties need to be manually wrapped in [allowInterop] - /// before being passed to this constructor: [createScriptURL]. - external factory DomTrustedTypePolicyOptions({ - DomCreateScriptUrlOptionFn? createScriptURL, - }); -} - -/// Type of the function to configure createScriptURL -typedef DomCreateScriptUrlOptionFn = String Function(String input); - -/// An instance of a TrustedTypePolicy -@JS() -@staticInterop -@anonymous -abstract class DomTrustedTypePolicy {} - -/// (Some) methods of the [DomTrustedTypePolicy] -extension DomTrustedTypePolicyExtension on DomTrustedTypePolicy { - /// Create a `TrustedScriptURL` for the given [input]. - external DomTrustedScriptUrl createScriptURL(String input); -} - -/// An instance of a DomTrustedScriptUrl -@JS() -@staticInterop -@anonymous -abstract class DomTrustedScriptUrl {} - -// Getters - -/// window.trustedTypes (may or may not be supported by the browser) -@JS() -@staticInterop -@anonymous -external DomTrustedTypePolicyFactory? get trustedTypes; - -/// window.console -@JS() -@staticInterop -@anonymous -external DomConsole get console; diff --git a/packages/firebase_core/firebase_core_web/lib/src/interop/package_web_tweaks.dart b/packages/firebase_core/firebase_core_web/lib/src/interop/package_web_tweaks.dart new file mode 100644 index 000000000000..7d9e2bd612f7 --- /dev/null +++ b/packages/firebase_core/firebase_core_web/lib/src/interop/package_web_tweaks.dart @@ -0,0 +1,38 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Copied from https://github.com/flutter/packages/google_identity_services_web/lib/src/js_interop/package_web_tweaks.dart + +/// Provides some useful tweaks to `package:web`. +library package_web_tweaks; + +import 'dart:js_interop'; + +import 'package:web/web.dart' as web; + +/// This extension gives web.window a nullable getter to the `trustedTypes` +/// property, which needs to be used to check for feature support. +extension NullableTrustedTypesGetter on web.Window { + /// + @JS('trustedTypes') + external web.TrustedTypePolicyFactory? get nullableTrustedTypes; +} + +/// This extension allows a trusted type policy to create a script URL without +/// the `args` parameter (which in Chrome currently fails). +extension CreateScriptUrlWithoutArgs on web.TrustedTypePolicy { + /// + @JS('createScriptURL') + external web.TrustedScriptURL createScriptURLNoArgs( + String input, + ); +} + +/// This extension allows setting a TrustedScriptURL as the src of a script element, +/// which currently only accepts a string. +extension TrustedTypeSrcAttribute on web.HTMLScriptElement { + /// + @JS('src') + external set srcTT(web.TrustedScriptURL value); +} diff --git a/packages/firebase_core/firebase_core_web/lib/src/interop/utils/es6_interop.dart b/packages/firebase_core/firebase_core_web/lib/src/interop/utils/es6_interop.dart index 3e8aaecce6a2..036ddb6e5b35 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/interop/utils/es6_interop.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/interop/utils/es6_interop.dart @@ -13,6 +13,7 @@ import 'package:js/js.dart'; import 'func.dart'; @JS('Promise') +@staticInterop class PromiseJsImpl { external PromiseJsImpl(Function resolver); external PromiseJsImpl then([Func1? onResolve, Func1? onReject]); diff --git a/packages/firebase_core/firebase_core_web/lib/src/interop/utils/utils.dart b/packages/firebase_core/firebase_core_web/lib/src/interop/utils/utils.dart index d90fbcf91e8e..d102e1f11f5a 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/interop/utils/utils.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/interop/utils/utils.dart @@ -6,101 +6,100 @@ // ignore_for_file: public_member_api_docs import 'dart:async'; +import 'dart:js_interop/js_interop.dart' as js_interop; import 'package:js/js.dart'; -import 'package:js/js_util.dart' as util; import 'es6_interop.dart'; import 'func.dart'; -import 'js_interop.dart' as js; /// Returns Dart representation from JS Object. /// /// The optional [customDartify] function may return `null` to indicate, /// that it could not handle the given JS Object. -dynamic dartify( - Object? jsObject, [ - Object? Function(Object? object)? customDartify, -]) { - if (_isBasicType(jsObject)) { - return jsObject; - } - - // Handle list - if (jsObject is Iterable) { - return jsObject.map((item) => dartify(item, customDartify)).toList(); - } - - var jsDate = js.dartifyDate(jsObject!); - if (jsDate != null) { - return jsDate; - } - - Object? value = customDartify?.call(jsObject); - - if (value == null) { - var keys = js.objectKeys(jsObject); - var map = {}; - for (final key in keys) { - map[key] = dartify(util.getProperty(jsObject, key), customDartify); - } - return map; - } - - return value; -} +// dynamic dartify( +// Object? jsObject, [ +// Object? Function(Object? object)? customDartify, +// ]) { +// if (_isBasicType(jsObject)) { +// return jsObject; +// } + +// // Handle list +// if (jsObject is Iterable) { +// return jsObject.map((item) => dartify(item, customDartify)).toList(); +// } + +// var jsDate = js.dartifyDate(jsObject!); +// if (jsDate != null) { +// return jsDate; +// } + +// Object? value = customDartify?.call(jsObject); + +// if (value == null) { +// var keys = js.objectKeys(jsObject); +// var map = {}; +// for (final key in keys) { +// map[key] = dartify(util.getProperty(jsObject, key), customDartify); +// } +// return map; +// } + +// return value; +// } // Converts an Iterable into a JS Array -dynamic jsifyList( - Iterable list, [ - Object? Function(Object? object)? customJsify, -]) { - return js.toJSArray(list.map((item) => jsify(item, customJsify)).toList()); -} +// dynamic jsifyList( +// Iterable list, [ +// Object? Function(Object? object)? customJsify, +// ]) { +// return js.toJSArray(list.map((item) => jsify(item, customJsify)).toList()); +// } /// Returns the JS implementation from Dart Object. /// /// The optional [customJsify] function may return `null` to indicate, /// that it could not handle the given Dart Object. -dynamic jsify( - Object? dartObject, [ - Object? Function(Object? object)? customJsify, -]) { - if (_isBasicType(dartObject)) { - if (dartObject == null) { - return null; - } - return dartObject; - } - - if (dartObject is Iterable) { - return jsifyList(dartObject, customJsify); - } - - if (dartObject is Map) { - var jsMap = util.newObject(); - dartObject.forEach((key, value) { - util.setProperty(jsMap, key, jsify(value, customJsify)); - }); - return jsMap; - } - - if (dartObject is Function) { - return allowInterop(dartObject); - } - - Object? value = customJsify?.call(dartObject); - - if (value == null) { - throw ArgumentError.value(dartObject, 'dartObject', 'Could not convert'); - } - - return value; -} +// dynamic jsify( +// Object? dartObject, [ +// Object? Function(Object? object)? customJsify, +// ]) { +// if (_isBasicType(dartObject)) { +// if (dartObject == null) { +// return null; +// } +// return dartObject; +// } + +// if (dartObject is Iterable) { +// return jsifyList(dartObject, customJsify); +// } + +// if (dartObject is Map) { +// var jsMap = util.newObject(); +// dartObject.forEach((key, value) { +// util.setProperty(jsMap, key, jsify(value, customJsify)); +// }); +// return jsMap; +// } + +// if (dartObject is Function) { +// return allowInterop(dartObject); +// } + +// Object? value = customJsify?.call(dartObject); + +// if (value == null) { +// throw ArgumentError.value(dartObject, 'dartObject', 'Could not convert'); +// } + +// return value; +// } /// Calls [method] on JavaScript object [jsObject]. -dynamic callMethod(Object jsObject, String method, List args) => - util.callMethod(jsObject, method, args); +// dynamic callMethod(Object jsObject, String method, List args) => +// util.callMethod(jsObject, method, args); /// Returns `true` if the [value] is a very basic built-in type - e.g. /// `null`, [num], [bool] or [String]. It returns `false` in the other case. @@ -112,8 +111,9 @@ bool _isBasicType(Object? value) { } /// Handles the [PromiseJsImpl] object. -Future handleThenable(PromiseJsImpl thenable) async { - return util.promiseToFuture(thenable); +Future handleThenable( + js_interop.JSPromise thenable) async { + return thenable.toDart; } /// Handles the [Future] object with the provided [mapper] function. diff --git a/packages/firebase_core/firebase_core_web/pubspec.yaml b/packages/firebase_core/firebase_core_web/pubspec.yaml index a931532f4664..d4d4837d8e6d 100644 --- a/packages/firebase_core/firebase_core_web/pubspec.yaml +++ b/packages/firebase_core/firebase_core_web/pubspec.yaml @@ -5,7 +5,7 @@ repository: https://github.com/firebase/flutterfire/tree/master/packages/firebas version: 2.10.0 environment: - sdk: '>=2.18.0 <4.0.0' + sdk: '>=3.2.0 <4.0.0' flutter: '>=3.3.0' dependencies: @@ -16,6 +16,7 @@ dependencies: sdk: flutter js: ^0.6.3 meta: ^1.8.0 + web: '>=0.3.0 <0.5.0' dev_dependencies: flutter_test: From 0fbb4b4c673a2411834dcf02a8cdc6b921310ba6 Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Wed, 13 Dec 2023 16:16:35 +0100 Subject: [PATCH 2/9] running --- .../firebase_core/example/web/index.html | 4 +-- .../lib/src/firebase_core_web.dart | 7 ++-- .../lib/src/interop/core_interop.dart | 2 +- .../lib/src/interop/utils/es6_interop.dart | 14 ++++---- .../lib/src/interop/utils/utils.dart | 35 ++++++++----------- 5 files changed, 28 insertions(+), 34 deletions(-) diff --git a/packages/firebase_core/firebase_core/example/web/index.html b/packages/firebase_core/firebase_core/example/web/index.html index 091baec82002..d2a55de8e48a 100644 --- a/packages/firebase_core/firebase_core/example/web/index.html +++ b/packages/firebase_core/firebase_core/example/web/index.html @@ -19,10 +19,10 @@ - + /> --> Firebase Core Example diff --git a/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart b/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart index c8402cfc95e0..1b5296bbe0f3 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart @@ -123,7 +123,9 @@ class FirebaseCoreWeb extends FirebasePlatform { throw TrustedTypesException(e.toString()); } } - web.HTMLScriptElement script = web.HTMLScriptElement(); + + final web.HTMLScriptElement script = + web.document.createElement('script') as web.HTMLScriptElement; script.type = 'text/javascript'; script.crossOrigin = 'anonymous'; script.text = ''' @@ -133,8 +135,7 @@ class FirebaseCoreWeb extends FirebasePlatform { }; '''; - assert(web.document.head != null); - web.document.head!.append(script); + web.document.head!.appendChild(script); Completer completer = Completer(); diff --git a/packages/firebase_core/firebase_core_web/lib/src/interop/core_interop.dart b/packages/firebase_core/firebase_core_web/lib/src/interop/core_interop.dart index bbf5b208d3b2..16bd03c38369 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/interop/core_interop.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/interop/core_interop.dart @@ -47,7 +47,7 @@ extension FirebaseErrorExtension on FirebaseError { external String get stack; /// Not part of the core JS API, but occasionally exposed in error objects. - external Object get serverResponse; + external JSAny get serverResponse; } /// A structure for options provided to Firebase. diff --git a/packages/firebase_core/firebase_core_web/lib/src/interop/utils/es6_interop.dart b/packages/firebase_core/firebase_core_web/lib/src/interop/utils/es6_interop.dart index 036ddb6e5b35..3dfa85ca8ba3 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/interop/utils/es6_interop.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/interop/utils/es6_interop.dart @@ -10,11 +10,9 @@ library firebase_interop.core.es6; import 'package:js/js.dart'; -import 'func.dart'; - -@JS('Promise') -@staticInterop -class PromiseJsImpl { - external PromiseJsImpl(Function resolver); - external PromiseJsImpl then([Func1? onResolve, Func1? onReject]); -} +// @JS('Promise') +// @staticInterop +// class PromiseJsImpl { +// external PromiseJsImpl(Function resolver); +// external PromiseJsImpl then([Func1? onResolve, Func1? onReject]); +// } diff --git a/packages/firebase_core/firebase_core_web/lib/src/interop/utils/utils.dart b/packages/firebase_core/firebase_core_web/lib/src/interop/utils/utils.dart index d102e1f11f5a..f63c03be419c 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/interop/utils/utils.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/interop/utils/utils.dart @@ -6,12 +6,7 @@ // ignore_for_file: public_member_api_docs import 'dart:async'; -import 'dart:js_interop/js_interop.dart' as js_interop; - -import 'package:js/js.dart'; - -import 'es6_interop.dart'; -import 'func.dart'; +import 'dart:js_interop' as js_interop; /// Returns Dart representation from JS Object. /// @@ -117,17 +112,17 @@ Future handleThenable( } /// Handles the [Future] object with the provided [mapper] function. -PromiseJsImpl handleFutureWithMapper( - Future future, - Func1 mapper, -) { - return PromiseJsImpl(allowInterop(( - Function(S) resolve, - Function(Object) reject, - ) { - future.then((value) { - var mappedValue = mapper(value); - resolve(mappedValue); - }).catchError((error) => reject(error)); - })); -} +// PromiseJsImpl handleFutureWithMapper( +// Future future, +// Func1 mapper, +// ) { +// return PromiseJsImpl(allowInterop(( +// Function(S) resolve, +// Function(Object) reject, +// ) { +// future.then((value) { +// var mappedValue = mapper(value); +// resolve(mappedValue); +// }).catchError((error) => reject(error)); +// })); +// } From d69614547f365112de72cdb459f53dd8562a1e07 Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Wed, 13 Dec 2023 16:21:06 +0100 Subject: [PATCH 3/9] everything running --- .../firebase_core_web/lib/src/interop/core.dart | 11 ++++++----- .../lib/src/interop/core_interop.dart | 4 ++-- .../firebase_core/firebase_core_web/test/tools.dart | 1 - 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/firebase_core/firebase_core_web/lib/src/interop/core.dart b/packages/firebase_core/firebase_core_web/lib/src/interop/core.dart index d7bce00f06ad..4b1a14b3ce16 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/interop/core.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/interop/core.dart @@ -5,9 +5,11 @@ // ignore_for_file: public_member_api_docs +import 'dart:js_interop'; + import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart'; +import 'package:firebase_core_web/firebase_core_web_interop.dart'; -import 'app.dart'; import 'core_interop.dart' as firebase_interop; export 'app.dart'; @@ -16,10 +18,9 @@ export 'core_interop.dart'; List get apps => firebase_interop .getApps() - // explicitly typing the param as dynamic to work-around - // https://github.com/dart-lang/sdk/issues/33537 - // ignore: unnecessary_lambdas - .map((dynamic e) => App.getInstance(e)) + .toDart + .cast() + .map(App.getInstance) .toList(); App initializeApp({ diff --git a/packages/firebase_core/firebase_core_web/lib/src/interop/core_interop.dart b/packages/firebase_core/firebase_core_web/lib/src/interop/core_interop.dart index 16bd03c38369..2b3528abad1b 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/interop/core_interop.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/interop/core_interop.dart @@ -13,8 +13,8 @@ import 'dart:js_interop'; import 'package:firebase_core_web/firebase_core_web_interop.dart'; @JS() -// TODO FIX -List getApps() => throw UnimplementedError(); +// List +external JSArray getApps(); /// The current SDK version. /// diff --git a/packages/firebase_core/firebase_core_web/test/tools.dart b/packages/firebase_core/firebase_core_web/test/tools.dart index c6982ff84443..d0f21c11605d 100644 --- a/packages/firebase_core/firebase_core_web/test/tools.dart +++ b/packages/firebase_core/firebase_core_web/test/tools.dart @@ -4,7 +4,6 @@ import 'dart:html'; -import 'package:firebase_core_web/src/interop/js.dart' as dom; import 'package:js/js_util.dart' as js_util; /// Injects a `` tag with the provided [attributes] into the [dom.document]. From 5c50c6c78540612ab27045239a0773e0727dc1cb Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Wed, 13 Dec 2023 16:30:42 +0100 Subject: [PATCH 4/9] done? --- .../lib/firebase_core_web.dart | 3 +- .../lib/firebase_core_web_interop.dart | 3 - .../lib/src/firebase_core_web.dart | 23 ++-- .../lib/src/interop/utils/es6_interop.dart | 18 --- .../lib/src/interop/utils/func.dart | 13 -- .../lib/src/interop/utils/js_interop.dart | 39 ------ .../lib/src/interop/utils/utils.dart | 113 ------------------ 7 files changed, 13 insertions(+), 199 deletions(-) delete mode 100644 packages/firebase_core/firebase_core_web/lib/src/interop/utils/es6_interop.dart delete mode 100644 packages/firebase_core/firebase_core_web/lib/src/interop/utils/func.dart delete mode 100644 packages/firebase_core/firebase_core_web/lib/src/interop/utils/js_interop.dart diff --git a/packages/firebase_core/firebase_core_web/lib/firebase_core_web.dart b/packages/firebase_core/firebase_core_web/lib/firebase_core_web.dart index a1d697a68781..332739867bfc 100644 --- a/packages/firebase_core/firebase_core_web/lib/firebase_core_web.dart +++ b/packages/firebase_core/firebase_core_web/lib/firebase_core_web.dart @@ -6,9 +6,8 @@ library firebase_core_web; import 'dart:async'; -import 'dart:js'; import 'dart:js_interop'; -import 'dart:js_util'; +import 'dart:js_interop_unsafe'; import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart'; import 'package:firebase_core_web/src/interop/package_web_tweaks.dart'; diff --git a/packages/firebase_core/firebase_core_web/lib/firebase_core_web_interop.dart b/packages/firebase_core/firebase_core_web/lib/firebase_core_web_interop.dart index d7a819627628..854382c11d1d 100644 --- a/packages/firebase_core/firebase_core_web/lib/firebase_core_web_interop.dart +++ b/packages/firebase_core/firebase_core_web/lib/firebase_core_web_interop.dart @@ -6,8 +6,5 @@ library firebase_core_web_interop; export 'src/interop/core.dart'; -export 'src/interop/utils/es6_interop.dart'; -export 'src/interop/utils/func.dart'; export 'src/interop/utils/js.dart'; -export 'src/interop/utils/js_interop.dart'; export 'src/interop/utils/utils.dart'; diff --git a/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart b/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart index 1b5296bbe0f3..ee4364253435 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart @@ -67,7 +67,7 @@ class FirebaseCoreWeb extends FirebasePlatform { /// own risk as the version might be unsupported or untested against. @visibleForTesting String get firebaseSDKVersion { - return context['flutterfire_web_sdk_version'] ?? + return globalContext.getProperty('flutterfire_web_sdk_version'.toJS) ?? supportedFirebaseJsSdkVersion; } @@ -83,8 +83,8 @@ class FirebaseCoreWeb extends FirebasePlatform { /// You must ensure the Firebase script is injected before using the service. List get _ignoredServiceScripts { try { - JsObject ignored = - JsObject.fromBrowserObject(context['flutterfire_ignore_scripts']); + JSObject ignored = + globalContext.getProperty('flutterfire_ignore_scripts'.toJS); if (ignored is Iterable) { return (ignored as Iterable) @@ -131,7 +131,7 @@ class FirebaseCoreWeb extends FirebasePlatform { script.text = ''' window.ff_trigger_$windowVar = async (callback) => { console.debug("Initializing Firebase $windowVar"); - callback(await import("${trustedUrl != null ? callMethod(trustedUrl, 'toString', []) : src}")); + callback(await import("${trustedUrl != null ? trustedUrl.callMethod('toString'.toJS) : src}")); }; '''; @@ -139,13 +139,14 @@ class FirebaseCoreWeb extends FirebasePlatform { Completer completer = Completer(); - context.callMethod('ff_trigger_$windowVar', [ - (module) { - context[windowVar] = module; - context.deleteProperty('ff_trigger_$windowVar'); + globalContext.callMethod( + 'ff_trigger_$windowVar'.toJS, + (JSAny module) { + globalContext[windowVar] = module; + globalContext.delete('ff_trigger_$windowVar'.toJS); completer.complete(); - } - ]); + }.toJS, + ); await completer.future; } @@ -155,7 +156,7 @@ class FirebaseCoreWeb extends FirebasePlatform { Future _initializeCore() async { // If Firebase is already available, core has already been initialized // (or the user has added the scripts to their html file). - if (context['firebase_core'] != null) { + if (globalContext.getProperty('firebase_core'.toJS) != null) { return; } diff --git a/packages/firebase_core/firebase_core_web/lib/src/interop/utils/es6_interop.dart b/packages/firebase_core/firebase_core_web/lib/src/interop/utils/es6_interop.dart deleted file mode 100644 index 3dfa85ca8ba3..000000000000 --- a/packages/firebase_core/firebase_core_web/lib/src/interop/utils/es6_interop.dart +++ /dev/null @@ -1,18 +0,0 @@ -// ignore_for_file: require_trailing_commas -// 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_for_file: public_member_api_docs - -@JS() -library firebase_interop.core.es6; - -import 'package:js/js.dart'; - -// @JS('Promise') -// @staticInterop -// class PromiseJsImpl { -// external PromiseJsImpl(Function resolver); -// external PromiseJsImpl then([Func1? onResolve, Func1? onReject]); -// } diff --git a/packages/firebase_core/firebase_core_web/lib/src/interop/utils/func.dart b/packages/firebase_core/firebase_core_web/lib/src/interop/utils/func.dart deleted file mode 100644 index 3c91df4a58bd..000000000000 --- a/packages/firebase_core/firebase_core_web/lib/src/interop/utils/func.dart +++ /dev/null @@ -1,13 +0,0 @@ -// ignore_for_file: require_trailing_commas -// Copyright (c) 2018, 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. - -// ignore: public_member_api_docs -typedef Func0 = R Function(); -// ignore: public_member_api_docs -typedef Func1 = R Function(A a); -// ignore: public_member_api_docs -typedef Func3 = R Function(A a, B b, C c); -// ignore: public_member_api_docs -typedef Func2Opt1 = R Function(A a, [B? b]); diff --git a/packages/firebase_core/firebase_core_web/lib/src/interop/utils/js_interop.dart b/packages/firebase_core/firebase_core_web/lib/src/interop/utils/js_interop.dart deleted file mode 100644 index 95783431cae9..000000000000 --- a/packages/firebase_core/firebase_core_web/lib/src/interop/utils/js_interop.dart +++ /dev/null @@ -1,39 +0,0 @@ -// ignore_for_file: require_trailing_commas -// 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_for_file: public_member_api_docs - -@JS() -library firebase_interop.core.js; - -import 'package:js/js.dart'; -import 'package:js/js_util.dart' as util; - -// TODO(ehesp): Break into own package? -@JS('JSON.stringify') -external String stringify(Object obj); - -@JS('Object.keys') -external List objectKeys(Object obj); - -@JS('Array.from') -external Object toJSArray(List source); - -DateTime? dartifyDate(Object jsObject) { - if (util.hasProperty(jsObject, 'toDateString')) { - try { - var date = jsObject as dynamic; - // ignore: avoid_dynamic_calls - return DateTime.fromMillisecondsSinceEpoch(date.getTime()); - } - // TODO(rrousselGit): document why try/catch is needed here or find an alternative - // ignore: avoid_catching_errors - on NoSuchMethodError { - // so it's not a JsDate! - return null; - } - } - return null; -} diff --git a/packages/firebase_core/firebase_core_web/lib/src/interop/utils/utils.dart b/packages/firebase_core/firebase_core_web/lib/src/interop/utils/utils.dart index f63c03be419c..423e297255d1 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/interop/utils/utils.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/interop/utils/utils.dart @@ -8,121 +8,8 @@ import 'dart:async'; import 'dart:js_interop' as js_interop; -/// Returns Dart representation from JS Object. -/// -/// The optional [customDartify] function may return `null` to indicate, -/// that it could not handle the given JS Object. -// dynamic dartify( -// Object? jsObject, [ -// Object? Function(Object? object)? customDartify, -// ]) { -// if (_isBasicType(jsObject)) { -// return jsObject; -// } - -// // Handle list -// if (jsObject is Iterable) { -// return jsObject.map((item) => dartify(item, customDartify)).toList(); -// } - -// var jsDate = js.dartifyDate(jsObject!); -// if (jsDate != null) { -// return jsDate; -// } - -// Object? value = customDartify?.call(jsObject); - -// if (value == null) { -// var keys = js.objectKeys(jsObject); -// var map = {}; -// for (final key in keys) { -// map[key] = dartify(util.getProperty(jsObject, key), customDartify); -// } -// return map; -// } - -// return value; -// } - -// Converts an Iterable into a JS Array -// dynamic jsifyList( -// Iterable list, [ -// Object? Function(Object? object)? customJsify, -// ]) { -// return js.toJSArray(list.map((item) => jsify(item, customJsify)).toList()); -// } - -/// Returns the JS implementation from Dart Object. -/// -/// The optional [customJsify] function may return `null` to indicate, -/// that it could not handle the given Dart Object. -// dynamic jsify( -// Object? dartObject, [ -// Object? Function(Object? object)? customJsify, -// ]) { -// if (_isBasicType(dartObject)) { -// if (dartObject == null) { -// return null; -// } -// return dartObject; -// } - -// if (dartObject is Iterable) { -// return jsifyList(dartObject, customJsify); -// } - -// if (dartObject is Map) { -// var jsMap = util.newObject(); -// dartObject.forEach((key, value) { -// util.setProperty(jsMap, key, jsify(value, customJsify)); -// }); -// return jsMap; -// } - -// if (dartObject is Function) { -// return allowInterop(dartObject); -// } - -// Object? value = customJsify?.call(dartObject); - -// if (value == null) { -// throw ArgumentError.value(dartObject, 'dartObject', 'Could not convert'); -// } - -// return value; -// } - -/// Calls [method] on JavaScript object [jsObject]. -// dynamic callMethod(Object jsObject, String method, List args) => -// util.callMethod(jsObject, method, args); - -/// Returns `true` if the [value] is a very basic built-in type - e.g. -/// `null`, [num], [bool] or [String]. It returns `false` in the other case. -bool _isBasicType(Object? value) { - if (value == null || value is num || value is bool || value is String) { - return true; - } - return false; -} - /// Handles the [PromiseJsImpl] object. Future handleThenable( js_interop.JSPromise thenable) async { return thenable.toDart; } - -/// Handles the [Future] object with the provided [mapper] function. -// PromiseJsImpl handleFutureWithMapper( -// Future future, -// Func1 mapper, -// ) { -// return PromiseJsImpl(allowInterop(( -// Function(S) resolve, -// Function(Object) reject, -// ) { -// future.then((value) { -// var mappedValue = mapper(value); -// resolve(mappedValue); -// }).catchError((error) => reject(error)); -// })); -// } From 99e684b8b0062f03efe48aff32a46749ecfb3668 Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Wed, 13 Dec 2023 16:34:31 +0100 Subject: [PATCH 5/9] activate trusted types --- packages/firebase_core/firebase_core/example/web/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/firebase_core/firebase_core/example/web/index.html b/packages/firebase_core/firebase_core/example/web/index.html index d2a55de8e48a..091baec82002 100644 --- a/packages/firebase_core/firebase_core/example/web/index.html +++ b/packages/firebase_core/firebase_core/example/web/index.html @@ -19,10 +19,10 @@ - + /> Firebase Core Example From 1890913fa9487bbb14aba00c6a0fd2b97dcc8cc5 Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Wed, 13 Dec 2023 17:26:35 +0100 Subject: [PATCH 6/9] reintegrate dependencies for other plugins --- .../lib/firebase_core_web_interop.dart | 3 + .../lib/src/interop/app.dart | 4 +- .../lib/src/interop/utils/es6_interop.dart | 19 +++ .../lib/src/interop/utils/func.dart | 13 ++ .../lib/src/interop/utils/js_interop.dart | 39 ++++++ .../lib/src/interop/utils/utils.dart | 128 +++++++++++++++++- 6 files changed, 201 insertions(+), 5 deletions(-) create mode 100644 packages/firebase_core/firebase_core_web/lib/src/interop/utils/es6_interop.dart create mode 100644 packages/firebase_core/firebase_core_web/lib/src/interop/utils/func.dart create mode 100644 packages/firebase_core/firebase_core_web/lib/src/interop/utils/js_interop.dart diff --git a/packages/firebase_core/firebase_core_web/lib/firebase_core_web_interop.dart b/packages/firebase_core/firebase_core_web/lib/firebase_core_web_interop.dart index 854382c11d1d..d7a819627628 100644 --- a/packages/firebase_core/firebase_core_web/lib/firebase_core_web_interop.dart +++ b/packages/firebase_core/firebase_core_web/lib/firebase_core_web_interop.dart @@ -6,5 +6,8 @@ library firebase_core_web_interop; export 'src/interop/core.dart'; +export 'src/interop/utils/es6_interop.dart'; +export 'src/interop/utils/func.dart'; export 'src/interop/utils/js.dart'; +export 'src/interop/utils/js_interop.dart'; export 'src/interop/utils/utils.dart'; diff --git a/packages/firebase_core/firebase_core_web/lib/src/interop/app.dart b/packages/firebase_core/firebase_core_web/lib/src/interop/app.dart index 0eb4146d684d..651d61dcf660 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/interop/app.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/interop/app.dart @@ -5,6 +5,8 @@ // ignore_for_file: public_member_api_docs +import 'dart:js_interop'; + import 'package:firebase_core_web/firebase_core_web_interop.dart'; import 'core.dart' as core_interop; @@ -30,5 +32,5 @@ class App extends JsObjectWrapper { } /// Deletes the app and frees resources of all App's services. - Future delete() => handleThenable(core_interop.deleteApp(jsObject)); + Future delete() => core_interop.deleteApp(jsObject).toDart; } diff --git a/packages/firebase_core/firebase_core_web/lib/src/interop/utils/es6_interop.dart b/packages/firebase_core/firebase_core_web/lib/src/interop/utils/es6_interop.dart new file mode 100644 index 000000000000..3e8aaecce6a2 --- /dev/null +++ b/packages/firebase_core/firebase_core_web/lib/src/interop/utils/es6_interop.dart @@ -0,0 +1,19 @@ +// ignore_for_file: require_trailing_commas +// 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_for_file: public_member_api_docs + +@JS() +library firebase_interop.core.es6; + +import 'package:js/js.dart'; + +import 'func.dart'; + +@JS('Promise') +class PromiseJsImpl { + external PromiseJsImpl(Function resolver); + external PromiseJsImpl then([Func1? onResolve, Func1? onReject]); +} diff --git a/packages/firebase_core/firebase_core_web/lib/src/interop/utils/func.dart b/packages/firebase_core/firebase_core_web/lib/src/interop/utils/func.dart new file mode 100644 index 000000000000..3c91df4a58bd --- /dev/null +++ b/packages/firebase_core/firebase_core_web/lib/src/interop/utils/func.dart @@ -0,0 +1,13 @@ +// ignore_for_file: require_trailing_commas +// Copyright (c) 2018, 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. + +// ignore: public_member_api_docs +typedef Func0 = R Function(); +// ignore: public_member_api_docs +typedef Func1 = R Function(A a); +// ignore: public_member_api_docs +typedef Func3 = R Function(A a, B b, C c); +// ignore: public_member_api_docs +typedef Func2Opt1 = R Function(A a, [B? b]); diff --git a/packages/firebase_core/firebase_core_web/lib/src/interop/utils/js_interop.dart b/packages/firebase_core/firebase_core_web/lib/src/interop/utils/js_interop.dart new file mode 100644 index 000000000000..95783431cae9 --- /dev/null +++ b/packages/firebase_core/firebase_core_web/lib/src/interop/utils/js_interop.dart @@ -0,0 +1,39 @@ +// ignore_for_file: require_trailing_commas +// 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_for_file: public_member_api_docs + +@JS() +library firebase_interop.core.js; + +import 'package:js/js.dart'; +import 'package:js/js_util.dart' as util; + +// TODO(ehesp): Break into own package? +@JS('JSON.stringify') +external String stringify(Object obj); + +@JS('Object.keys') +external List objectKeys(Object obj); + +@JS('Array.from') +external Object toJSArray(List source); + +DateTime? dartifyDate(Object jsObject) { + if (util.hasProperty(jsObject, 'toDateString')) { + try { + var date = jsObject as dynamic; + // ignore: avoid_dynamic_calls + return DateTime.fromMillisecondsSinceEpoch(date.getTime()); + } + // TODO(rrousselGit): document why try/catch is needed here or find an alternative + // ignore: avoid_catching_errors + on NoSuchMethodError { + // so it's not a JsDate! + return null; + } + } + return null; +} diff --git a/packages/firebase_core/firebase_core_web/lib/src/interop/utils/utils.dart b/packages/firebase_core/firebase_core_web/lib/src/interop/utils/utils.dart index 423e297255d1..c0592509b5c6 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/interop/utils/utils.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/interop/utils/utils.dart @@ -5,11 +5,131 @@ // ignore_for_file: public_member_api_docs +// TODO(Lyokone): should be deleted once all plugins are migrated to use js_interop + import 'dart:async'; -import 'dart:js_interop' as js_interop; + +import 'package:js/js.dart'; +import 'package:js/js_util.dart' as util; + +import 'es6_interop.dart'; +import 'func.dart'; +import 'js_interop.dart' as js; + +/// Returns Dart representation from JS Object. +/// +/// The optional [customDartify] function may return `null` to indicate, +/// that it could not handle the given JS Object. +dynamic dartify( + Object? jsObject, [ + Object? Function(Object? object)? customDartify, +]) { + if (_isBasicType(jsObject)) { + return jsObject; + } + + // Handle list + if (jsObject is Iterable) { + return jsObject.map((item) => dartify(item, customDartify)).toList(); + } + + var jsDate = js.dartifyDate(jsObject!); + if (jsDate != null) { + return jsDate; + } + + Object? value = customDartify?.call(jsObject); + + if (value == null) { + var keys = js.objectKeys(jsObject); + var map = {}; + for (final key in keys) { + map[key] = dartify(util.getProperty(jsObject, key), customDartify); + } + return map; + } + + return value; +} + +// Converts an Iterable into a JS Array +dynamic jsifyList( + Iterable list, [ + Object? Function(Object? object)? customJsify, +]) { + return js.toJSArray(list.map((item) => jsify(item, customJsify)).toList()); +} + +/// Returns the JS implementation from Dart Object. +/// +/// The optional [customJsify] function may return `null` to indicate, +/// that it could not handle the given Dart Object. +dynamic jsify( + Object? dartObject, [ + Object? Function(Object? object)? customJsify, +]) { + if (_isBasicType(dartObject)) { + if (dartObject == null) { + return null; + } + return dartObject; + } + + if (dartObject is Iterable) { + return jsifyList(dartObject, customJsify); + } + + if (dartObject is Map) { + var jsMap = util.newObject(); + dartObject.forEach((key, value) { + util.setProperty(jsMap, key, jsify(value, customJsify)); + }); + return jsMap; + } + + if (dartObject is Function) { + return allowInterop(dartObject); + } + + Object? value = customJsify?.call(dartObject); + + if (value == null) { + throw ArgumentError.value(dartObject, 'dartObject', 'Could not convert'); + } + + return value; +} + +/// Calls [method] on JavaScript object [jsObject]. +dynamic callMethod(Object jsObject, String method, List args) => + util.callMethod(jsObject, method, args); + +/// Returns `true` if the [value] is a very basic built-in type - e.g. +/// `null`, [num], [bool] or [String]. It returns `false` in the other case. +bool _isBasicType(Object? value) { + if (value == null || value is num || value is bool || value is String) { + return true; + } + return false; +} /// Handles the [PromiseJsImpl] object. -Future handleThenable( - js_interop.JSPromise thenable) async { - return thenable.toDart; +Future handleThenable(PromiseJsImpl thenable) async { + return util.promiseToFuture(thenable); +} + +/// Handles the [Future] object with the provided [mapper] function. +PromiseJsImpl handleFutureWithMapper( + Future future, + Func1 mapper, +) { + return PromiseJsImpl(allowInterop(( + Function(S) resolve, + Function(Object) reject, + ) { + future.then((value) { + var mappedValue = mapper(value); + resolve(mappedValue); + }).catchError((error) => reject(error)); + })); } From 1257c372f67d13765ceffce199e364ee3de46276 Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Thu, 14 Dec 2023 08:58:28 +0100 Subject: [PATCH 7/9] remove useless tests --- .../lib/firebase_core_web.dart | 15 ++-- .../lib/src/firebase_core_web.dart | 7 +- .../test/firebase_core_web_test.dart | 72 ------------------- 3 files changed, 10 insertions(+), 84 deletions(-) diff --git a/packages/firebase_core/firebase_core_web/lib/firebase_core_web.dart b/packages/firebase_core/firebase_core_web/lib/firebase_core_web.dart index 332739867bfc..02ee42abd378 100644 --- a/packages/firebase_core/firebase_core_web/lib/firebase_core_web.dart +++ b/packages/firebase_core/firebase_core_web/lib/firebase_core_web.dart @@ -12,7 +12,6 @@ import 'dart:js_interop_unsafe'; import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart'; import 'package:firebase_core_web/src/interop/package_web_tweaks.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; -import 'package:js/js_util.dart' as js_util; import 'package:meta/meta.dart'; import 'package:web/web.dart' as web; @@ -46,9 +45,9 @@ FirebaseOptions _createFromJsOptions(firebase.FirebaseOptions options) { /// When the Firebase JS SDK throws an error, it contains a code which can be /// used to identify the specific type of error. This helper function is used /// to keep error messages consistent across different platforms. -String _getJSErrorCode(dynamic e) { - if (js_util.getProperty(e, 'name') == 'FirebaseError') { - return js_util.getProperty(e, 'code') ?? ''; +String _getJSErrorCode(JSObject e) { + if (e.getProperty('name'.toJS)?.toDart == 'FirebaseError') { + return e.getProperty('code'.toJS)?.toDart ?? ''; } return ''; @@ -59,11 +58,11 @@ String _getJSErrorCode(dynamic e) { /// If a JavaScript error is thrown and not manually handled using the code, /// this function ensures that if the error is Firebase related, it is instead /// re-created as a [FirebaseException] with a familiar code and message. -FirebaseException _catchJSError(dynamic e) { - if (js_util.getProperty(e, 'name') == 'FirebaseError') { - String rawCode = js_util.getProperty(e, 'code'); +FirebaseException _catchJSError(JSObject e) { + if (e.getProperty('name'.toJS)?.toDart == 'FirebaseError') { + String rawCode = e.getProperty('code'.toJS).toDart; String code = rawCode; - String message = js_util.getProperty(e, 'message') ?? ''; + String message = e.getProperty('message'.toJS)?.toDart ?? ''; if (code.contains('/')) { List chunks = code.split('/'); diff --git a/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart b/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart index ee4364253435..a7835ea43187 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart @@ -292,7 +292,7 @@ class FirebaseCoreWeb extends FirebasePlatform { measurementId: options.measurementId, ); } catch (e) { - if (_getJSErrorCode(e) == 'app/duplicate-app') { + if (_getJSErrorCode(e as JSObject) == 'app/duplicate-app') { throw duplicateApp(name); } @@ -331,15 +331,14 @@ class FirebaseCoreWeb extends FirebasePlatform { try { app = guardNotInitialized(() => firebase.app(name)); + return _createFromJsApp(app); } catch (e) { - if (_getJSErrorCode(e) == 'app/no-app') { + if (_getJSErrorCode(e as JSObject) == 'app/no-app') { throw noAppExists(name); } throw _catchJSError(e); } - - return _createFromJsApp(app); } } diff --git a/packages/firebase_core/firebase_core_web/test/firebase_core_web_test.dart b/packages/firebase_core/firebase_core_web/test/firebase_core_web_test.dart index fbf87ac6dd27..05126c3ad599 100644 --- a/packages/firebase_core/firebase_core_web/test/firebase_core_web_test.dart +++ b/packages/firebase_core/firebase_core_web/test/firebase_core_web_test.dart @@ -39,77 +39,5 @@ void main() { final List apps = FirebasePlatform.instance.apps; expect(apps, hasLength(0)); }); - - test('.app()', () async { - (js.context['firebase_core'] as js.JsObject)['getApp'] = - js.allowInterop((String name) { - return js.JsObject.jsify({ - 'name': name, - 'options': { - 'apiKey': 'abc', - 'appId': '123', - 'messagingSenderId': 'msg', - 'projectId': 'test', - }, - }); - }); - - final FirebaseAppPlatform app = FirebasePlatform.instance.app('foo'); - - expect(app.name, equals('foo')); - - expect(app.options.apiKey, equals('abc')); - expect(app.options.appId, equals('123')); - expect(app.options.messagingSenderId, equals('msg')); - expect(app.options.projectId, equals('test')); - }); - - test('.initializeApp()', () async { - bool appConfigured = false; - - (js.context['firebase_core'] as js.JsObject)['getApp'] = - js.allowInterop((String name) { - if (appConfigured) { - return js.JsObject.jsify({ - 'name': name, - 'options': { - 'apiKey': 'abc', - 'appId': '123', - 'messagingSenderId': 'msg', - 'projectId': 'test', - }, - }); - } else { - return null; - } - }); - - // Prevents a warning log. - (js.context['firebase_core'] as js.JsObject)['SDK_VERSION'] = - supportedFirebaseJsSdkVersion; - - (js.context['firebase_core'] as js.JsObject)['initializeApp'] = - js.allowInterop((js.JsObject options, String name) { - appConfigured = true; - return js.JsObject.jsify({ - 'name': name, - 'options': options, - }); - }); - - final FirebaseAppPlatform app = - await FirebasePlatform.instance.initializeApp( - name: 'foo', - options: const FirebaseOptions( - apiKey: 'abc', - appId: '123', - messagingSenderId: 'msg', - projectId: 'test', - ), - ); - - expect(app.name, equals('foo')); - expect(app.options.appId, equals('123')); - }); }); } From 91e9db4edc650a8f27c9c685d98846459a5d2612 Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Wed, 10 Jan 2024 08:35:56 +0100 Subject: [PATCH 8/9] add JSError --- .../lib/firebase_core_web.dart | 18 +++++++++--------- .../lib/src/firebase_core_web.dart | 4 ++-- .../lib/src/interop/utils/es6_interop.dart | 10 ++++++++++ 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/packages/firebase_core/firebase_core_web/lib/firebase_core_web.dart b/packages/firebase_core/firebase_core_web/lib/firebase_core_web.dart index 02ee42abd378..faa0a0837ea1 100644 --- a/packages/firebase_core/firebase_core_web/lib/firebase_core_web.dart +++ b/packages/firebase_core/firebase_core_web/lib/firebase_core_web.dart @@ -11,6 +11,7 @@ import 'dart:js_interop_unsafe'; import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart'; import 'package:firebase_core_web/src/interop/package_web_tweaks.dart'; +import 'package:firebase_core_web/src/interop/utils/es6_interop.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:meta/meta.dart'; import 'package:web/web.dart' as web; @@ -45,9 +46,9 @@ FirebaseOptions _createFromJsOptions(firebase.FirebaseOptions options) { /// When the Firebase JS SDK throws an error, it contains a code which can be /// used to identify the specific type of error. This helper function is used /// to keep error messages consistent across different platforms. -String _getJSErrorCode(JSObject e) { - if (e.getProperty('name'.toJS)?.toDart == 'FirebaseError') { - return e.getProperty('code'.toJS)?.toDart ?? ''; +String _getJSErrorCode(JSError e) { + if (e.name == 'FirebaseError') { + return e.code ?? ''; } return ''; @@ -58,11 +59,10 @@ String _getJSErrorCode(JSObject e) { /// If a JavaScript error is thrown and not manually handled using the code, /// this function ensures that if the error is Firebase related, it is instead /// re-created as a [FirebaseException] with a familiar code and message. -FirebaseException _catchJSError(JSObject e) { - if (e.getProperty('name'.toJS)?.toDart == 'FirebaseError') { - String rawCode = e.getProperty('code'.toJS).toDart; - String code = rawCode; - String message = e.getProperty('message'.toJS)?.toDart ?? ''; +FirebaseException _catchJSError(JSError e) { + if (e.name == 'FirebaseError') { + String code = e.code ?? ''; + String message = e.message ?? ''; if (code.contains('/')) { List chunks = code.split('/'); @@ -72,7 +72,7 @@ FirebaseException _catchJSError(JSObject e) { return FirebaseException( plugin: 'core', code: code, - message: message.replaceAll(' ($rawCode)', ''), + message: message.replaceAll(' ($code)', ''), ); } diff --git a/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart b/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart index a7835ea43187..ef309a18516c 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart @@ -292,7 +292,7 @@ class FirebaseCoreWeb extends FirebasePlatform { measurementId: options.measurementId, ); } catch (e) { - if (_getJSErrorCode(e as JSObject) == 'app/duplicate-app') { + if (_getJSErrorCode(e as JSError) == 'app/duplicate-app') { throw duplicateApp(name); } @@ -333,7 +333,7 @@ class FirebaseCoreWeb extends FirebasePlatform { app = guardNotInitialized(() => firebase.app(name)); return _createFromJsApp(app); } catch (e) { - if (_getJSErrorCode(e as JSObject) == 'app/no-app') { + if (_getJSErrorCode(e as JSError) == 'app/no-app') { throw noAppExists(name); } diff --git a/packages/firebase_core/firebase_core_web/lib/src/interop/utils/es6_interop.dart b/packages/firebase_core/firebase_core_web/lib/src/interop/utils/es6_interop.dart index 3e8aaecce6a2..38c582de1e74 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/interop/utils/es6_interop.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/interop/utils/es6_interop.dart @@ -17,3 +17,13 @@ class PromiseJsImpl { external PromiseJsImpl(Function resolver); external PromiseJsImpl then([Func1? onResolve, Func1? onReject]); } + +@JS() +@staticInterop +class JSError {} + +extension JSErrorExtension on JSError { + external String? get name; + external String? get message; + external String? get code; +} From a937886ec163cf8951b3f0974ebe6451aefb15bc Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Wed, 10 Jan 2024 08:43:54 +0100 Subject: [PATCH 9/9] fix overriding web sdk version --- .../firebase_core_web/lib/src/firebase_core_web.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart b/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart index ef309a18516c..68cefba51d31 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart @@ -67,8 +67,9 @@ class FirebaseCoreWeb extends FirebasePlatform { /// own risk as the version might be unsupported or untested against. @visibleForTesting String get firebaseSDKVersion { - return globalContext.getProperty('flutterfire_web_sdk_version'.toJS) ?? - supportedFirebaseJsSdkVersion; + final overridedWebSDKVersion = + (globalContext['flutterfire_web_sdk_version'] as JSString?)?.toDart; + return overridedWebSDKVersion ?? supportedFirebaseJsSdkVersion; } /// Returns a list of services which won't be automatically injected on