diff --git a/lib/web_ui/lib/src/engine/dom_renderer.dart b/lib/web_ui/lib/src/engine/dom_renderer.dart index af01cbc74c483..b14d007ead10b 100644 --- a/lib/web_ui/lib/src/engine/dom_renderer.dart +++ b/lib/web_ui/lib/src/engine/dom_renderer.dart @@ -78,7 +78,8 @@ class DomRenderer { /// This getter calls the `hasFocus` method of the `Document` interface. /// See for more details: /// https://developer.mozilla.org/en-US/docs/Web/API/Document/hasFocus - bool? get windowHasFocus => js_util.callMethod(html.document, 'hasFocus', []); + bool? get windowHasFocus => + js_util.callMethod(html.document, 'hasFocus', []); void _setupHotRestart() { // This persists across hot restarts to clear stale DOM. @@ -172,7 +173,8 @@ class DomRenderer { js_util.setProperty(element, name, value); } - static void setElementStyle(html.Element element, String name, String? value) { + static void setElementStyle( + html.Element element, String name, String? value) { if (value == null) { element.style.removeProperty(name); } else { @@ -181,8 +183,8 @@ class DomRenderer { } static void setElementTransform(html.Element element, String transformValue) { - js_util.setProperty(js_util.getProperty(element, 'style'), 'transform', - transformValue); + js_util.setProperty( + js_util.getProperty(element, 'style'), 'transform', transformValue); } void setText(html.Element element, String text) { @@ -200,7 +202,8 @@ class DomRenderer { } void setThemeColor(ui.Color color) { - html.MetaElement? theme = html.document.querySelector('#flutterweb-theme') as html.MetaElement?; + html.MetaElement? theme = + html.document.querySelector('#flutterweb-theme') as html.MetaElement?; if (theme == null) { theme = html.MetaElement() ..id = 'flutterweb-theme' @@ -315,8 +318,8 @@ flt-glass-pane * { // This css prevents an autofill overlay brought by the browser during // text field autofill by delaying the transition effect. // See: https://github.com/flutter/flutter/issues/61132. - if(browserHasAutofillOverlay()) { - sheet.insertRule(''' + if (browserHasAutofillOverlay()) { + sheet.insertRule(''' .transparentTextEditing:-webkit-autofill, .transparentTextEditing:-webkit-autofill:hover, .transparentTextEditing:-webkit-autofill:focus, @@ -326,7 +329,6 @@ flt-glass-pane * { ''', sheet.cssRules.length); } - final html.BodyElement bodyElement = html.document.body!; setElementStyle(bodyElement, 'position', 'fixed'); setElementStyle(bodyElement, 'top', '0'); @@ -416,8 +418,7 @@ flt-glass-pane * { // pointer events through to the semantics tree. However, for platform // views, the pointer events will not pass through, and will be handled // by the platform view. - glassPaneElement - .insertBefore(_accesibilityPlaceholder, _sceneHostElement); + glassPaneElement.insertBefore(_accesibilityPlaceholder, _sceneHostElement); PointerBinding.initInstance(glassPaneElement); @@ -460,6 +461,66 @@ flt-glass-pane * { _canvasKitScript?.remove(); _canvasKitScript = html.ScriptElement(); _canvasKitScript!.src = canvasKitBaseUrl + 'canvaskit.js'; + + // TODO(hterkelsen): Rather than this monkey-patch hack, we should + // build CanvasKit ourselves. See: + // https://github.com/flutter/flutter/issues/52588 + + // Monkey-patch the top-level `module` and `exports` objects so that + // CanvasKit doesn't attempt to register itself as an anonymous module. + // + // The idea behind making these fake `exports` and `module` objects is + // that `canvaskit.js` contains the following lines of code: + // + // if (typeof exports === 'object' && typeof module === 'object') + // module.exports = CanvasKitInit; + // else if (typeof define === 'function' && define['amd']) + // define([], function() { return CanvasKitInit; }); + // + // We need to avoid hitting the case where CanvasKit defines an anonymous + // module, since this breaks RequireJS, which DDC and some plugins use. + // Temporarily removing the `define` function won't work because RequireJS + // could load in between this code running and the CanvasKit code running. + // Also, we cannot monkey-patch the `define` function because it is + // non-configurable (it is a top-level 'var'). + + // First check if `exports` and `module` are already defined. If so, then + // CommonJS is being used, and we shouldn't have any problems. + js.JsFunction objectConstructor = js.context['Object']; + if (js.context['exports'] == null) { + js.JsObject exportsAccessor = js.JsObject.jsify({ + 'get': js.allowInterop(() { + if (html.document.currentScript == _canvasKitScript) { + return js.JsObject(objectConstructor); + } else { + return js.context['_flutterWebCachedExports']; + } + }), + 'set': js.allowInterop((dynamic value) { + js.context['_flutterWebCachedExports'] = value; + }), + 'configurable': true, + }); + objectConstructor.callMethod('defineProperty', + [js.context, 'exports', exportsAccessor]); + } + if (js.context['module'] == null) { + js.JsObject moduleAccessor = js.JsObject.jsify({ + 'get': js.allowInterop(() { + if (html.document.currentScript == _canvasKitScript) { + return js.JsObject(objectConstructor); + } else { + return js.context['_flutterWebCachedModule']; + } + }), + 'set': js.allowInterop((dynamic value) { + js.context['_flutterWebCachedModule'] = value; + }), + 'configurable': true, + }); + objectConstructor.callMethod( + 'defineProperty', [js.context, 'module', moduleAccessor]); + } html.document.head!.append(_canvasKitScript!); } @@ -469,8 +530,8 @@ flt-glass-pane * { } else { _resizeSubscription = html.window.onResize.listen(_metricsDidChange); } - _localeSubscription = languageChangeEvent.forTarget(html.window) - .listen(_languageDidChange); + _localeSubscription = + languageChangeEvent.forTarget(html.window).listen(_languageDidChange); EnginePlatformDispatcher.instance._updateLocales(); } @@ -484,7 +545,7 @@ flt-glass-pane * { /// Note: always check for rotations for a mobile device. Update the physical /// size if the change is caused by a rotation. void _metricsDidChange(html.Event? event) { - if(isMobile && !window.isRotation() && textEditing.isEditing) { + if (isMobile && !window.isRotation() && textEditing.isEditing) { window.computeOnScreenKeyboardInsets(); EnginePlatformDispatcher.instance.invokeOnMetricsChanged(); } else { @@ -517,13 +578,20 @@ flt-glass-pane * { static bool? _ellipseFeatureDetected; /// Draws CanvasElement ellipse with fallback. - static void ellipse(html.CanvasRenderingContext2D context, - double centerX, double centerY, double radiusX, double radiusY, - double rotation, double startAngle, double endAngle, bool antiClockwise) { + static void ellipse( + html.CanvasRenderingContext2D context, + double centerX, + double centerY, + double radiusX, + double radiusY, + double rotation, + double startAngle, + double endAngle, + bool antiClockwise) { _ellipseFeatureDetected ??= js_util.getProperty(context, 'ellipse') != null; if (_ellipseFeatureDetected!) { - context.ellipse(centerX, centerY, radiusX, radiusY, - rotation, startAngle, endAngle, antiClockwise); + context.ellipse(centerX, centerY, radiusX, radiusY, rotation, startAngle, + endAngle, antiClockwise); } else { context.save(); context.translate(centerX, centerY); @@ -539,9 +607,11 @@ flt-glass-pane * { static const String orientationLockTypeLandscape = 'landscape'; static const String orientationLockTypePortrait = 'portrait'; static const String orientationLockTypePortraitPrimary = 'portrait-primary'; - static const String orientationLockTypePortraitSecondary = 'portrait-secondary'; + static const String orientationLockTypePortraitSecondary = + 'portrait-secondary'; static const String orientationLockTypeLandscapePrimary = 'landscape-primary'; - static const String orientationLockTypeLandscapeSecondary = 'landscape-secondary'; + static const String orientationLockTypeLandscapeSecondary = + 'landscape-secondary'; /// Sets preferred screen orientation. /// @@ -556,8 +626,7 @@ flt-glass-pane * { Future setPreferredOrientation(List? orientations) { final html.Screen screen = html.window.screen!; if (!_unsafeIsNull(screen)) { - final html.ScreenOrientation? screenOrientation = - screen.orientation; + final html.ScreenOrientation? screenOrientation = screen.orientation; if (!_unsafeIsNull(screenOrientation)) { if (orientations!.isEmpty) { screenOrientation!.unlock(); @@ -588,7 +657,7 @@ flt-glass-pane * { // Converts device orientation to w3c OrientationLockType enum. static String? _deviceOrientationToLockType(String deviceOrientation) { - switch(deviceOrientation) { + switch (deviceOrientation) { case 'DeviceOrientation.portraitUp': return orientationLockTypePortraitPrimary; case 'DeviceOrientation.landscapeLeft':