[web] Implement setPreferredOrientation (#18432)

* Implement preferred orientation
* update comments
diff --git a/lib/web_ui/lib/src/engine/dom_renderer.dart b/lib/web_ui/lib/src/engine/dom_renderer.dart
index 7884a54..9d4101e 100644
--- a/lib/web_ui/lib/src/engine/dom_renderer.dart
+++ b/lib/web_ui/lib/src/engine/dom_renderer.dart
@@ -516,6 +516,74 @@
     }
   }
 
+  static const String orientationLockTypeAny = 'any';
+  static const String orientationLockTypeNatural = 'natural';
+  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 orientationLockTypeLandscapePrimary = 'landscape-primary';
+  static const String orientationLockTypeLandscapeSecondary = 'landscape-secondary';
+
+  /// Sets preferred screen orientation.
+  ///
+  /// Specifies the set of orientations the application interface can be
+  /// displayed in.
+  ///
+  /// The [orientations] argument is a list of DeviceOrientation values.
+  /// The empty list uses Screen unlock api and causes the application to
+  /// defer to the operating system default.
+  ///
+  /// See w3c screen api: https://www.w3.org/TR/screen-orientation/
+  Future<bool> setPreferredOrientation(List<dynamic> orientations) {
+    final html.Screen screen = html.window.screen;
+    if (screen != null) {
+      final html.ScreenOrientation screenOrientation =
+          screen.orientation;
+      if (screenOrientation != null) {
+        if (orientations.isEmpty) {
+          screenOrientation.unlock();
+          return Future.value(true);
+        } else {
+          String lockType = _deviceOrientationToLockType(orientations.first);
+          if (lockType != null) {
+            final Completer<bool> completer = Completer<bool>();
+            try {
+              screenOrientation.lock(lockType).then((dynamic _) {
+                completer.complete(true);
+              }).catchError((dynamic error) {
+                // On Chrome desktop an error with 'not supported on this device
+                // error' is fired.
+                completer.complete(false);
+              });
+            } catch (_) {
+              return Future.value(false);
+            }
+            return completer.future;
+          }
+        }
+      }
+    }
+    // API is not supported on this browser return false.
+    return Future.value(false);
+  }
+
+  // Converts device orientation to w3c OrientationLockType enum.
+  static String _deviceOrientationToLockType(String deviceOrientation) {
+    switch(deviceOrientation) {
+      case 'DeviceOrientation.portraitUp':
+        return orientationLockTypePortraitPrimary;
+      case 'DeviceOrientation.landscapeLeft':
+        return orientationLockTypePortraitSecondary;
+      case 'DeviceOrientation.portraitDown':
+        return orientationLockTypeLandscapePrimary;
+      case 'DeviceOrientation.landscapeRight':
+        return orientationLockTypeLandscapeSecondary;
+      default:
+        return null;
+    }
+  }
+
   /// The element corresponding to the only child of the root surface.
   html.Element get _rootApplicationElement {
     final html.Element lastElement = rootElement.children.last;
diff --git a/lib/web_ui/lib/src/engine/window.dart b/lib/web_ui/lib/src/engine/window.dart
index 7c94fff..1d29c31 100644
--- a/lib/web_ui/lib/src/engine/window.dart
+++ b/lib/web_ui/lib/src/engine/window.dart
@@ -442,6 +442,13 @@
             domRenderer.setThemeColor(ui.Color(arguments['primaryColor']));
             _replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true));
             return;
+          case 'SystemChrome.setPreferredOrientations':
+            final List<dynamic> arguments = decoded.arguments;
+            domRenderer.setPreferredOrientation(arguments).then((bool success) {
+              _replyToPlatformMessage(callback,
+                codec.encodeSuccessEnvelope(success));
+            });
+            return;
           case 'SystemSound.play':
             // There are no default system sounds on web.
             _replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true));