Skip to content

Commit

Permalink
[dart:html] Update Trusted Types APIs
Browse files Browse the repository at this point in the history
Closes b/195948578

Modifies Trusted Types APIs to be compliant with the spec in
https://w3c.github.io/webappsec-trusted-types/dist/spec/.

Change-Id: I65d52ace12342ce777ab596a9dd2e9a3f74b2f05
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/212270
Commit-Queue: Srujan Gaddam <srujzs@google.com>
Reviewed-by: Riley Porter <rileyporter@google.com>
  • Loading branch information
srujzs authored and commit-bot@chromium.org committed Sep 7, 2021
1 parent b3cb952 commit bda31c2
Show file tree
Hide file tree
Showing 4 changed files with 357 additions and 3 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Expand Up @@ -78,6 +78,20 @@
- The experimental `waitFor` functionality, and the library containing only that
function, are now deprecated.

#### `dart:html`

- **Breaking Change**: Trusted Types APIs have been updated to comply to the
latest [W3C spec]. This includes adding `TrustedScript`, `TrustedScriptURL`,
`TrustedTypePolicy`, and `TrustedTypePolicyFactory`, as well as modifying the
methods within the types, like removing the now deprecated `escape` and
and `unsafelyCreate` methods. These deprecated methods are already unsupported
on most modern browsers, so this would simply make them static failures now.
Users of the old API are encouraged to use the `TrustedTypePolicy` to create
Trusted Types. Please see the [MDN Web Docs] for examples.

[W3C spec]: https://w3c.github.io/webappsec-trusted-types/dist/spec/
[MDN Web Docs]: https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API#interfaces

### Tools

#### Dart command line
Expand Down
151 changes: 148 additions & 3 deletions sdk/lib/html/dart2js/html_dart2js.dart
Expand Up @@ -3285,6 +3285,45 @@ class Coordinates extends Interceptor {
// 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.

// WARNING: Do not edit - generated code.

typedef void CreateHtmlCallback(String input,
[Object? arg1,
Object? arg2,
Object? arg3,
Object? arg4,
Object? arg5,
Object? arg6]);
// Copyright (c) 2012, 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.

// WARNING: Do not edit - generated code.

typedef void CreateScriptCallback(String input,
[Object? arg1,
Object? arg2,
Object? arg3,
Object? arg4,
Object? arg5,
Object? arg6]);
// Copyright (c) 2012, 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.

// WARNING: Do not edit - generated code.

typedef void CreateScriptUrlCallback(String input,
[Object? arg1,
Object? arg2,
Object? arg3,
Object? arg4,
Object? arg5,
Object? arg6]);
// Copyright (c) 2012, 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.

@Native("Credential")
class Credential extends Interceptor {
// To suppress missing implicit constructor warnings.
Expand Down Expand Up @@ -30691,9 +30730,26 @@ class TrustedHtml extends Interceptor {
throw new UnsupportedError("Not supported");
}

static TrustedHtml escape(String html) native;
@JSName('toJSON')
String toJson() native;

String toString() native;
}
// Copyright (c) 2012, 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.

@Native("TrustedScript")
class TrustedScript extends Interceptor {
// To suppress missing implicit constructor warnings.
factory TrustedScript._() {
throw new UnsupportedError("Not supported");
}

@JSName('toJSON')
String toJson() native;

static TrustedHtml unsafelyCreate(String html) native;
String toString() native;
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
Expand All @@ -30706,7 +30762,94 @@ class TrustedScriptUrl extends Interceptor {
throw new UnsupportedError("Not supported");
}

static TrustedScriptUrl unsafelyCreate(String url) native;
@JSName('toJSON')
String toJson() native;

String toString() native;
}
// Copyright (c) 2012, 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.

@Native("TrustedTypePolicy")
class TrustedTypePolicy extends Interceptor {
// To suppress missing implicit constructor warnings.
factory TrustedTypePolicy._() {
throw new UnsupportedError("Not supported");
}

String? get name native;

@JSName('createHTML')
TrustedHtml createHtml(String input,
[Object? arg1,
Object? arg2,
Object? arg3,
Object? arg4,
Object? arg5,
Object? arg6]) native;

TrustedScript createScript(String input,
[Object? arg1,
Object? arg2,
Object? arg3,
Object? arg4,
Object? arg5,
Object? arg6]) native;

@JSName('createScriptURL')
TrustedScriptUrl createScriptUrl(String input,
[Object? arg1,
Object? arg2,
Object? arg3,
Object? arg4,
Object? arg5,
Object? arg6]) native;
}
// Copyright (c) 2012, 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.

@Native("TrustedTypePolicyFactory")
class TrustedTypePolicyFactory extends Interceptor {
// To suppress missing implicit constructor warnings.
factory TrustedTypePolicyFactory._() {
throw new UnsupportedError("Not supported");
}

TrustedTypePolicy? get defaultPolicy native;

@JSName('emptyHTML')
TrustedHtml? get emptyHtml native;

TrustedScript? get emptyScript native;

TrustedTypePolicy createPolicy(String policyName, [Map? policyOptions]) {
if (policyOptions != null) {
var policyOptions_1 = convertDartToNative_Dictionary(policyOptions);
return _createPolicy_1(policyName, policyOptions_1);
}
return _createPolicy_2(policyName);
}

@JSName('createPolicy')
TrustedTypePolicy _createPolicy_1(policyName, policyOptions) native;
@JSName('createPolicy')
TrustedTypePolicy _createPolicy_2(policyName) native;

String? getAttributeType(String tagName, String attribute,
[String? elementNs, String? attrNs]) native;

String? getPropertyType(String tagName, String property, [String? elementNs])
native;

@JSName('isHTML')
bool isHtml(Object value) native;

bool isScript(Object value) native;

@JSName('isScriptURL')
bool isScriptUrl(Object value) native;
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
Expand Down Expand Up @@ -32839,6 +32982,8 @@ class Window extends EventTarget
@Returns('Window|=Object')
dynamic get _get_top native;

TrustedTypePolicyFactory? get trustedTypes native;

VisualViewport? get visualViewport native;

/**
Expand Down
87 changes: 87 additions & 0 deletions tools/dom/dom.json
Expand Up @@ -5133,6 +5133,18 @@
},
"support_level": "deprecated"
},
"CreateHTMLCallback": {
"members": {},
"support_level": "untriaged"
},
"CreateScriptCallback": {
"members": {},
"support_level": "untriaged"
},
"CreateScriptURLCallback": {
"members": {},
"support_level": "untriaged"
},
"Credential": {
"members": {
"avatarURL": {
Expand Down Expand Up @@ -22486,20 +22498,92 @@
"escape": {
"support_level": "untriaged"
},
"toJSON": {
"support_level": "untriaged"
},
"toString": {
"support_level": "untriaged"
},
"unsafelyCreate": {
"support_level": "untriaged"
}
},
"support_level": "untriaged"
},
"TrustedScript": {
"members": {
"toJSON": {
"support_level": "untriaged"
},
"toString": {
"support_level": "untriaged"
}
},
"support_level": "untriaged"
},
"TrustedScriptURL": {
"members": {
"toJSON": {
"support_level": "untriaged"
},
"toString": {
"support_level": "untriaged"
},
"unsafelyCreate": {
"support_level": "untriaged"
}
},
"support_level": "untriaged"
},
"TrustedTypePolicy": {
"members": {
"createHTML": {
"support_level": "untriaged"
},
"createScript": {
"support_level": "untriaged"
},
"createScriptURL": {
"support_level": "untriaged"
},
"name": {
"support_level": "untriaged"
}
},
"support_level": "untriaged"
},
"TrustedTypePolicyFactory": {
"members": {
"createPolicy": {
"support_level": "untriaged"
},
"defaultPolicy": {
"support_level": "untriaged"
},
"emptyHTML": {
"support_level": "untriaged"
},
"emptyScript": {
"support_level": "untriaged"
},
"getAttributeType": {
"support_level": "untriaged"
},
"getPropertyType": {
"support_level": "untriaged"
},
"isHTML": {
"support_level": "untriaged"
},
"isScript": {
"support_level": "untriaged"
},
"isScriptURL": {
"support_level": "untriaged"
}
},
"support_level": "untriaged"
},
"TrustedURL": {
"members": {
"create": {
Expand Down Expand Up @@ -30731,6 +30815,9 @@
"toString": {},
"toolbar": {},
"top": {},
"trustedTypes": {
"support_level": "untriaged"
},
"visualViewport": {
"support_level": "untriaged"
},
Expand Down

6 comments on commit bda31c2

@mnordine
Copy link
Contributor

@mnordine mnordine commented on bda31c2 Sep 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@srujzs There have been many reasonable requests to update the Dart SDK to support various web apis over the years. All seemingly get rejected with, "it's very difficult to update any web apis, we're looking for a better way to do that, stay tuned", but yet, this one gets in. Why is that?

@srujzs
Copy link
Contributor Author

@srujzs srujzs commented on bda31c2 Sep 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi mnordine,

This change is intended to support security conformance of internal packages, making it a higher priority for us, which is why this one met the bar.

As for work in updating web APIs, our interop work is currently focused on enabling static JS interop on Native types, enabling users to sidestep many of the issues with writing their own interfaces for types reserved by dart:html. We also had some recent work in enabling the move of dart:html types to static extensions, which should hopefully enable users to write their own APIs without conflicts as well. There's a bit of logistics left to determine how to properly migrate these types. Thank you for your patience.

@mnordine
Copy link
Contributor

@mnordine mnordine commented on bda31c2 Sep 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@srujzs thank you for taking the time to reply, I figured it was security-related.

As for "enabling static JS interop on Native types", any way you could give an example, as I'm not sure exactly what that entails?

@srujzs
Copy link
Contributor Author

@srujzs srujzs commented on bda31c2 Sep 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be useful to drop #35084 for context. The goal we want to achieve with interop is to move things to something more statically typed. Eventually, extension types/views will help us get there, but that's a WIP, so we're setting up interop such that it becomes a simpler migration to extension types.

To address your question: right now, if we try to use package:js on Native types, we get a static error indicating that the type is reserved. This is because the type hierarchy is set up such that Native types are siblings of JavaScriptObject (which contains all package:js types). This hierarchy is natural, because we need to have a singular type at runtime for dispatch. However, this presents the issue that we can't use package:js types on Native types (because it would get intercepted as the Native type at runtime even if we tried).

Since this restriction is entirely due to dynamic dispatch, we can be a bit smarter and allow package:js to be used as long as the @JS type is "static". So, something like the following:

@JS()
class Window {}

extension WindowExtension on Window {
  external Navigator get navigator;
}

would allow you to use package:js interfaces for Native types. If for whatever reason, navigator did not exist on the dart:html Window or it was broken, this allows you to use the @JS type you wrote instead for that object. Right now, writing extension methods directly on the Native type is a bit unideal, because it may conflict with the definitions within the Native type, so this should make it easier to use interop with Native types.

@mnordine
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what problem this solves if there is no Navigator type to begin with? Do we then have to do something like:

@JS()
class Navigator {
  external GPU get gpu;
}

@JS()
class GPU {
  external Future<GPUAdapter> requestAdapter();
 ...
}

@JS()
class GPUAdapter {
  external String get name;
  external GPUAdapterFeatures get features;
  ...
}

@srujzs
Copy link
Contributor Author

@srujzs srujzs commented on bda31c2 Sep 9, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what problem this solves if there is no Navigator type to begin with?

Right, if there was no conflict to begin with, then you could use interop as usual. The problem comes up when you want to write your own interfaces on types that are reserved by dart:html. The APIs within those types may likely be outdated, incorrect, or broken. Currently, you'd either have to use extensions on those Native types (which has its own conflicts) or use js_util to workaround it, removing the benefit of package:js.

The code sample you gave would work fine if none of those types existed in dart:html (well, maybe not the Future one, since that requires a conversion, but extensions would now allow you to do that conversion). If those types did exist, then with the new changes, you'd just move those members to static extensions.

The critical thing here is we want users to be able to use interop to work around the shortcomings of dart:html in a robust future-proof way that aligns with what we expect a future interop-based web library package to look like.

Please sign in to comment.