From 0b621d312d6518a5f5dc3e6f05bd5b950cc93906 Mon Sep 17 00:00:00 2001 From: Harry Terkelsen Date: Mon, 23 Nov 2020 10:04:03 -0800 Subject: [PATCH 1/2] Hide the 'require' function while loading CanvasKit. --- .../src/engine/canvaskit/initialization.dart | 17 +++-- lib/web_ui/lib/src/engine/dom_renderer.dart | 69 ++++++++++++------- 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/lib/web_ui/lib/src/engine/canvaskit/initialization.dart b/lib/web_ui/lib/src/engine/canvaskit/initialization.dart index 20a9ecd952c2e..bc1403db9d455 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/initialization.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/initialization.dart @@ -11,8 +11,7 @@ part of engine; external String? get requestedRendererType; /// Whether to use CanvasKit as the rendering backend. -bool get useCanvasKit => - _autoDetect ? _detectRenderer() : _useSkia; +bool get useCanvasKit => _autoDetect ? _detectRenderer() : _useSkia; /// Returns true if CanvasKit is used. /// @@ -42,8 +41,9 @@ const bool _useSkia = bool.fromEnvironment('FLUTTER_WEB_USE_SKIA', defaultValue: false); // If set to true, forces CPU-only rendering (i.e. no WebGL). -const bool canvasKitForceCpuOnly = - bool.fromEnvironment('FLUTTER_WEB_CANVASKIT_FORCE_CPU_ONLY', defaultValue: false); +const bool canvasKitForceCpuOnly = bool.fromEnvironment( + 'FLUTTER_WEB_CANVASKIT_FORCE_CPU_ONLY', + defaultValue: false); /// The URL to use when downloading the CanvasKit script and associated wasm. /// @@ -63,13 +63,18 @@ Future initializeCanvasKit() { late StreamSubscription loadSubscription; loadSubscription = domRenderer.canvasKitScript!.onLoad.listen((_) { loadSubscription.cancel(); - final CanvasKitInitPromise canvasKitInitPromise = CanvasKitInit(CanvasKitInitOptions( - locateFile: js.allowInterop((String file, String unusedBase) => canvasKitBaseUrl + file), + final CanvasKitInitPromise canvasKitInitPromise = + CanvasKitInit(CanvasKitInitOptions( + locateFile: js.allowInterop( + (String file, String unusedBase) => canvasKitBaseUrl + file), )); canvasKitInitPromise.then(js.allowInterop((CanvasKit ck) { canvasKit = ck; windowFlutterCanvasKit = canvasKit; canvasKitCompleter.complete(); + if (domRenderer._cachedDefine != null) { + js_util.setProperty(html.window, 'define', domRenderer._cachedDefine); + } })); }); diff --git a/lib/web_ui/lib/src/engine/dom_renderer.dart b/lib/web_ui/lib/src/engine/dom_renderer.dart index af01cbc74c483..544e5149dee99 100644 --- a/lib/web_ui/lib/src/engine/dom_renderer.dart +++ b/lib/web_ui/lib/src/engine/dom_renderer.dart @@ -49,6 +49,11 @@ class DomRenderer { html.ScriptElement? get canvasKitScript => _canvasKitScript; html.ScriptElement? _canvasKitScript; + /// The cached `define` function which we removed while loading CanvasKit. + /// + /// Once CanvasKit is loaded we will restore it. + dynamic _cachedDefine; + /// The element that contains the [sceneElement]. /// /// This element is created and inserted in the HTML DOM once. It is never @@ -78,7 +83,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 +178,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 +188,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 +207,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 +323,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 +334,6 @@ flt-glass-pane * { ''', sheet.cssRules.length); } - final html.BodyElement bodyElement = html.document.body!; setElementStyle(bodyElement, 'position', 'fixed'); setElementStyle(bodyElement, 'top', '0'); @@ -416,8 +423,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,7 +466,14 @@ flt-glass-pane * { _canvasKitScript?.remove(); _canvasKitScript = html.ScriptElement(); _canvasKitScript!.src = canvasKitBaseUrl + 'canvaskit.js'; - html.document.head!.append(_canvasKitScript!); + // CanvasKit will try to use `define()` if it is available, which will + // cause errors because CanvasKit uses an anonymous module. + if (js_util.hasProperty(html.window, 'define')) { + _cachedDefine = js_util.getProperty(html.window, 'define'); + js_util.setProperty(html.window, 'define', null); + } + html.document.currentScript!.parentNode! + .insertBefore(_canvasKitScript!, html.document.currentScript); } if (html.window.visualViewport != null) { @@ -469,8 +482,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 +497,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 +530,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 +559,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 +578,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 +609,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': From 011414e8a8f40d8c6d7b5d08016bd13d705990eb Mon Sep 17 00:00:00 2001 From: Harry Terkelsen Date: Wed, 25 Nov 2020 10:13:19 -0800 Subject: [PATCH 2/2] Undo formatting changes and add TODO for building CanvasKit --- .../lib/src/engine/canvaskit/initialization.dart | 14 ++++++-------- lib/web_ui/lib/src/engine/dom_renderer.dart | 4 ++++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/web_ui/lib/src/engine/canvaskit/initialization.dart b/lib/web_ui/lib/src/engine/canvaskit/initialization.dart index 2c1e941ea8ee3..20a9ecd952c2e 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/initialization.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/initialization.dart @@ -11,7 +11,8 @@ part of engine; external String? get requestedRendererType; /// Whether to use CanvasKit as the rendering backend. -bool get useCanvasKit => _autoDetect ? _detectRenderer() : _useSkia; +bool get useCanvasKit => + _autoDetect ? _detectRenderer() : _useSkia; /// Returns true if CanvasKit is used. /// @@ -41,9 +42,8 @@ const bool _useSkia = bool.fromEnvironment('FLUTTER_WEB_USE_SKIA', defaultValue: false); // If set to true, forces CPU-only rendering (i.e. no WebGL). -const bool canvasKitForceCpuOnly = bool.fromEnvironment( - 'FLUTTER_WEB_CANVASKIT_FORCE_CPU_ONLY', - defaultValue: false); +const bool canvasKitForceCpuOnly = + bool.fromEnvironment('FLUTTER_WEB_CANVASKIT_FORCE_CPU_ONLY', defaultValue: false); /// The URL to use when downloading the CanvasKit script and associated wasm. /// @@ -63,10 +63,8 @@ Future initializeCanvasKit() { late StreamSubscription loadSubscription; loadSubscription = domRenderer.canvasKitScript!.onLoad.listen((_) { loadSubscription.cancel(); - final CanvasKitInitPromise canvasKitInitPromise = - CanvasKitInit(CanvasKitInitOptions( - locateFile: js.allowInterop( - (String file, String unusedBase) => canvasKitBaseUrl + file), + final CanvasKitInitPromise canvasKitInitPromise = CanvasKitInit(CanvasKitInitOptions( + locateFile: js.allowInterop((String file, String unusedBase) => canvasKitBaseUrl + file), )); canvasKitInitPromise.then(js.allowInterop((CanvasKit ck) { canvasKit = ck; diff --git a/lib/web_ui/lib/src/engine/dom_renderer.dart b/lib/web_ui/lib/src/engine/dom_renderer.dart index 8ff56b0a285eb..b14d007ead10b 100644 --- a/lib/web_ui/lib/src/engine/dom_renderer.dart +++ b/lib/web_ui/lib/src/engine/dom_renderer.dart @@ -462,6 +462,10 @@ flt-glass-pane * { _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. //