Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(530)

Unified Diff: lib/dom/scripts/systemhtml.py

Issue 10378040: Generate and use cross frame wrappers for types in other frames/windows. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Factored out template Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: lib/dom/scripts/systemhtml.py
diff --git a/lib/dom/scripts/systemhtml.py b/lib/dom/scripts/systemhtml.py
index 7d10be8d379322692ce1408bf5ff3fe8055121be..3d4d187dbdcfdf10248dee7cada8e4822a4b380f 100644
--- a/lib/dom/scripts/systemhtml.py
+++ b/lib/dom/scripts/systemhtml.py
@@ -112,6 +112,7 @@ _injected_doc_fragments = {
# TODO(jacobr): cleanup and augment this list.
_html_library_remove = set([
'Window.get:document', # Removed as we have a custom implementation.
+ 'Window.get:window',
'NodeList.item',
"Attr.*",
# "BarProp.*",
@@ -436,6 +437,26 @@ _html_event_names = {
# doesn't match the interface hierarchy.
_html_explicit_event_classes = set(['DocumentFragment'])
+# These types are accessible from different frames. We provide a
+# wrapper to restrict cross-frame access. Ideally, we capture this
+# via the IDL CheckSecurity attribute. Currently, it is sometimes
+# only defined under #define V8_BINDING.
sra1 2012/05/10 00:12:06 What are the tuple elements?
+_cross_frame_wrapped_types = {
+ 'Window': ('get:window', 'window'),
+ 'Location': ('Window.get:location', 'window.location'),
+ 'History': ('Window.get:history', 'window.history'),
+}
+
+_wrapped_types = _cross_frame_wrapped_types.keys()
sra1 2012/05/10 00:12:06 Comment that this list may be extended to non-cros
+
+def _WrapAndReturnObject(container_interface, return_interface, expr, getter):
sra1 2012/05/10 00:12:06 Just make this wrap the expression. _WrapIfCrossFr
+ if return_interface in _cross_frame_wrapped_types.keys():
sra1 2012/05/10 00:12:06 You can use 'in' on dictionaries directly.
+ full_name = container_interface + '.get:' + getter
+ if _cross_frame_wrapped_types[return_interface][0] == full_name:
+ return 'return %s' % expr
+ return 'return _%sCrossFrameImpl._createSafe(%s)' % (return_interface, expr)
+ return 'return %s' % expr
+
def _OnAttributeToEventName(on_method):
event_name = on_method.id[2:]
if event_name in _on_attribute_to_event_name_mapping:
@@ -621,6 +642,48 @@ class HtmlInterfacesSystem(HtmlSystem):
return os.path.join(self._output_dir, 'html', 'interface',
'%s.dart' % interface_name)
+class HtmlCrossFrameSystem(HtmlSystem):
sra1 2012/05/10 00:12:06 I have been ordering them: System1 InterfaceGener
+
+ def __init__(self, templates, database, emitters, output_dir, generator):
+ super(HtmlCrossFrameSystem, self).__init__(
+ templates, database, emitters, output_dir, generator)
+ self._dart_cross_frame_file_paths = []
+
+ def InterfaceGenerator(self,
+ interface,
+ common_prefix,
+ super_interface_name,
+ source_filter):
+ """."""
+ if interface.id not in _cross_frame_wrapped_types.keys():
+ return None
+ template_file = 'crossframe_%s.darttemplate' % interface.id
+ template = self._templates.TryLoad(template_file)
+ if not template:
+ template = self._templates.Load('crossframe_impl.darttemplate')
+
+ dart_code = self._ImplFileEmitter(interface.id)
+ return HtmlCrossFrameGenerator(self, interface, template,
+ super_interface_name, dart_code,
+ self._shared)
+
+ def GenerateLibraries(self, lib_dir):
+ pass
+
+ def Finish(self):
+ pass
+
+ def ProcessCallback(self, interface, info):
+ pass
+
+ def _ImplFileEmitter(self, name):
+ """Returns the file emitter of the Frog implementation file."""
+ # TODO(jmesserly): is this the right path
+ path = os.path.join(self._output_dir, 'html', 'crossframe',
+ '%sCrossFrame.dart' % name)
+ self._dart_cross_frame_file_paths.append(path)
+ return self._emitters.FileEmitter(path)
+
# ------------------------------------------------------------------------------
# TODO(jmesserly): inheritance is probably not the right way to factor this long
@@ -713,6 +776,9 @@ class HtmlDartInterfaceGenerator(DartInterfaceGenerator):
self._EmitEventGetter(self._shared.GetParentEventsClass(self._interface))
def AddAttribute(self, getter, setter):
+ if 'CheckSecurityForNode' in (getter or setter).ext_attrs:
sra1 2012/05/10 00:12:06 Comment on why.
+ return
+
dom_name = DartDomNameOfAttribute(getter)
html_getter_name = self._shared.RenameInHtmlLibrary(
self._interface, dom_name, 'get:')
@@ -762,7 +828,7 @@ class HtmlDartInterfaceGenerator(DartInterfaceGenerator):
self._members_emitter.Emit('\n'
' $TYPE $NAME($PARAMS);\n',
- TYPE=info.type_name,
+ TYPE=info.type_name,
NAME=html_name,
PARAMS=info.ParametersInterfaceDeclaration())
@@ -905,6 +971,8 @@ class HtmlFrogClassGenerator(FrogInterfaceGenerator):
#
# class YImpl extends ListBase<T> { copies of transitive XImpl methods; }
#
+
+ # TODO(vsm): Wrap return value if necessary.
self._members_emitter.Emit(
'\n'
' $TYPE operator[](int index) native "return this[index];";\n',
@@ -935,7 +1003,8 @@ class HtmlFrogClassGenerator(FrogInterfaceGenerator):
self._members_emitter.Emit(template, E=DartType(element_type))
def AddAttribute(self, getter, setter):
-
+ if 'CheckSecurityForNode' in (getter or setter).ext_attrs:
+ return
html_getter_name = self._shared.RenameInHtmlLibrary(
self._interface, DartDomNameOfAttribute(getter), 'get:',
implementation_class=True)
@@ -961,6 +1030,7 @@ class HtmlFrogClassGenerator(FrogInterfaceGenerator):
# If the (getter, setter) pair is shadowing, we can't generate a shadowing
# field (Issue 1633).
+
(super_getter, super_getter_interface) = self._FindShadowedAttribute(getter)
(super_setter, super_setter_interface) = self._FindShadowedAttribute(setter)
if super_getter or super_setter:
@@ -982,20 +1052,22 @@ class HtmlFrogClassGenerator(FrogInterfaceGenerator):
self._AddAttributeUsingProperties(getter, setter)
return
- output_type = getter and self._NarrowOutputType(getter.type.id)
- input_type = setter and self._NarrowInputType(setter.type.id)
- if getter and setter and input_type == output_type:
- self._members_emitter.Emit(
- '\n $TYPE $NAME;\n',
- NAME=DartDomNameOfAttribute(getter),
- TYPE=output_type)
- return
- if getter and not setter:
- self._members_emitter.Emit(
- '\n final $TYPE $NAME;\n',
- NAME=DartDomNameOfAttribute(getter),
- TYPE=output_type)
- return
+ # If the return type is wrapped, we cannot just map to a field.
+ if not getter or getter.type.id not in _wrapped_types:
+ output_type = getter and self._NarrowOutputType(getter.type.id)
+ input_type = setter and self._NarrowInputType(setter.type.id)
+ if getter and setter and input_type == output_type:
+ self._members_emitter.Emit(
+ '\n $TYPE $NAME;\n',
+ NAME=DartDomNameOfAttribute(getter),
+ TYPE=output_type)
+ return
+ if getter and not setter:
+ self._members_emitter.Emit(
+ '\n final $TYPE $NAME;\n',
+ NAME=DartDomNameOfAttribute(getter),
+ TYPE=output_type)
+ return
self._AddAttributeUsingProperties(getter, setter)
def _AddAttributeUsingProperties(self, getter, setter):
@@ -1011,12 +1083,25 @@ class HtmlFrogClassGenerator(FrogInterfaceGenerator):
self._AddRenamingSetter(attr, DartDomNameOfAttribute(attr))
def _AddRenamingGetter(self, attr, html_name):
+ if attr.type.id in _wrapped_types:
+ getter = '_' + html_name
+ else:
+ getter = html_name
return_type = self._NarrowOutputType(attr.type.id)
self._members_emitter.Emit(
'\n $TYPE get $(HTML_NAME)() native "return this.$NAME;";\n',
- HTML_NAME=html_name,
+ HTML_NAME=getter,
NAME=attr.id,
TYPE=return_type)
+ if attr.type.id in _wrapped_types:
+ self._members_emitter.Emit(
+ '\n $TYPE get $(HTML_NAME)() {'
sra1 2012/05/10 00:12:06 Any reason not to use => syntax?
+ '\n $BODY;'
+ '\n }',
+ HTML_NAME=html_name,
+ BODY=_WrapAndReturnObject(self._interface.id, attr.type.id,
+ getter, html_name),
+ TYPE=attr.type.id)
def _AddRenamingSetter(self, attr, html_name):
self._members_emitter.Emit(
@@ -1036,10 +1121,30 @@ class HtmlFrogClassGenerator(FrogInterfaceGenerator):
if not html_name:
return
- # Do we need a native body?
- if html_name != info.declared_name:
+ # Do we need a native body?
sra1 2012/05/10 00:12:06 "Do we need a wrapper or a native body?"
+ if info.type_name in _wrapped_types:
return_type = self._NarrowOutputType(info.type_name)
-
+
+ operation_emitter = self._members_emitter.Emit('$!SCOPE',
+ INTERFACE=info.type_name,
+ TYPE=return_type,
+ HTML_NAME=html_name,
+ NAME=info.declared_name,
+ PARAMS=info.ParametersImplementationDeclaration(
+ lambda type_name: self._NarrowInputType(type_name)),
+ ARGLIST=info.ParametersAsArgumentList())
+
+ operation_emitter.Emit(
+ '\n'
+ ' $TYPE _$(HTML_NAME)($PARAMS) native "$NAME";\n')
+ operation_emitter.Emit(
sra1 2012/05/10 00:12:06 Could fold this into the previous one.
+ '\n'
+ ' $INTERFACE $(HTML_NAME)($PARAMS) =>'
+ ' _$(INTERFACE)CrossFrameImpl._createSafe(_$HTML_NAME($ARGLIST));\n')
+
+ elif html_name != info.declared_name:
+ return_type = self._NarrowOutputType(info.type_name)
+
operation_emitter = self._members_emitter.Emit('$!SCOPE',
TYPE=return_type,
HTML_NAME=html_name,
@@ -1125,6 +1230,7 @@ class HtmlFrogSystem(HtmlSystem):
os.path.join(lib_dir, 'html_frog.dart'),
(self._interface_system._dart_interface_file_paths +
self._interface_system._dart_callback_file_paths +
+ self._cross_frame_system._dart_cross_frame_file_paths +
self._dart_frog_file_paths))
def Finish(self):
@@ -1189,6 +1295,7 @@ class HtmlDartiumSystem(HtmlSystem):
os.path.join(lib_dir, 'html_dartium.dart'),
(self._interface_system._dart_interface_file_paths +
self._interface_system._dart_callback_file_paths +
+ self._cross_frame_system._dart_cross_frame_file_paths +
self._dart_dartium_file_paths
),
AUXILIARY_DIR=MassagePath(auxiliary_dir),
@@ -1386,6 +1493,8 @@ class HtmlDartiumInterfaceGenerator(object):
return method_name
def AddAttribute(self, getter, setter):
+ if 'CheckSecurityForNode' in (getter or setter).ext_attrs:
+ return
dom_name = DartDomNameOfAttribute(getter or setter)
html_getter_name = self._shared.RenameInHtmlLibrary(
self._interface, dom_name, 'get:', implementation_class=True)
@@ -1707,3 +1816,271 @@ class HtmlDartiumInterfaceGenerator(object):
self.GenerateDispatch(
true_code, info, indent + ' ', position + 1, positive)
return True
+
+# ------------------------------------------------------------------------------
+
+# TODO(jacobr): there is far too much duplicated code between these bindings
+# and the Frog bindings. A larger scale refactoring needs to be performed to
+# reduce the duplicated logic.
+class HtmlCrossFrameGenerator(object):
+ """Generates a cross-frame implementation of interfaces that must be
+ securely exposed to other frames/isolates. The cross-frame
+ implementation wraps the underlying implemation, only providing APIs
+ deemded secure."""
+
+ def __init__(self, system, interface, template, super_interface, dart_code,
+ shared):
+ """Generates Dart cross-frame wrapper code for the given interface.
+
+ Args:
+ system: system that is executing this generator.
+ template: template that output is generated into.
+ interface: an IDLInterface instance. It is assumed that all types have
+ been converted to Dart types (e.g. int, String), unless they are in
+ the same package as the interface.
+ super_interface: A string or None, the name of the common interface that
+ this interface implements, if any.
+ dart_code: an Emitter for the file containing the Dart implementation
+ class.
+ shared: functionaly shared across all Html generators.
+ """
+ self._system = system
+ self._interface = interface
+ self._super_interface = super_interface
+ self._dart_code = dart_code
+ self._current_secondary_parent = None
+ self._shared = shared
+ self._template = template
+
+ def NativeObjectName(self):
+ return '_ptr'
+
+ def StartInterface(self):
+ interface = self._interface
+ interface_name = interface.id
+ self._class_name = self._ImplClassName(interface_name)
+
+ self._members_emitter = self._dart_code.Emit(self._template,
+ NAME=interface_name,
+ CLASSNAME=self._class_name,
+ POINTER=self.NativeObjectName(),
+ LOCAL=_cross_frame_wrapped_types[interface_name][1]
+ )
+
+ def _ImplClassName(self, type_name):
+ return self._shared._ImplClassName(type_name)
+
+ def FinishInterface(self):
+ """."""
+ pass
+
+ def AddConstant(self, constant):
+ # Constants are already defined on the interface.
+ pass
+
+ def AddAttribute(self, getter, setter):
+ dom_name = DartDomNameOfAttribute(getter or setter)
+ html_getter_name = self._shared.RenameInHtmlLibrary(
+ self._interface, dom_name, 'get:', implementation_class=True)
+ html_setter_name = self._shared.RenameInHtmlLibrary(
+ self._interface, dom_name, 'set:', implementation_class=True)
+
+ # TODO(vsm): Generate stubs that throws an error if getter or
+ # setter is suppressed.
+ if (getter and html_getter_name and
+ ('DoNotCheckSecurityOnGetter' in getter.ext_attrs or
+ 'DoNotCheckSecurity' in getter.ext_attrs)):
+ self._AddGetter(getter, html_getter_name)
+ if (setter and html_setter_name and
+ ('DoNotCheckSecurityOnSetter' in setter.ext_attrs or
+ 'DoNotCheckSecurity' in setter.ext_attrs)):
+ self._AddSetter(setter, html_setter_name)
+
+ def _AddGetter(self, attr, html_name):
+ self._members_emitter.Emit(
+ '\n'
+ ' $TYPE get $(NAME)() => $(THIS).$NAME;\n',
+ NAME=html_name,
+ TYPE=DartType(attr.type.id),
+ THIS=self.NativeObjectName())
+
+ def _AddSetter(self, attr, html_name):
+ self._members_emitter.Emit(
+ '\n'
+ ' void set $(NAME)($TYPE value) { '
+ '$(THIS).$NAME = value; }\n',
+ NAME=html_name,
+ TYPE=DartType(attr.type.id),
+ THIS=self.NativeObjectName())
+
+ def AddSecondaryAttribute(self, interface, getter, setter):
+ pass
+
+ def AddSecondaryOperation(self, interface, info):
+ pass
+
+ def AddEventAttributes(self, event_attrs):
+ pass
+
+ def AddIndexer(self, element_type):
+ """Adds all the methods required to complete implementation of List."""
+ raise Exception('Indexers not yet supported for cross frame types')
+
+ def AddOperation(self, info):
+ """
+ Arguments:
+ info: An OperationInfo object.
+ """
+ if 'DoNotCheckSecurity' not in info.overloads[0].ext_attrs:
+ # TODO(vsm): Generate stub that throws an error.
+ return
+
+ html_name = self._shared.RenameInHtmlLibrary(
+ self._interface, info.name, implementation_class=True)
+
+ if not html_name:
+ return
+
+ body = self._members_emitter.Emit(
+ '\n'
+ ' $TYPE $NAME($PARAMS) {\n'
+ '$!BODY'
+ ' }\n',
+ TYPE=info.type_name,
+ NAME=html_name,
+ PARAMS=info.ParametersImplementationDeclaration())
+
+ # Process in order of ascending number of arguments to ensure missing
+ # optional arguments are processed early.
+ overloads = sorted(info.overloads,
+ key=lambda overload: len(overload.arguments))
+ self._native_version = 0
+ fallthrough = self.GenerateDispatch(body, info, ' ', 0, overloads)
+ if fallthrough:
+ body.Emit(' throw "Incorrect number or type of arguments";\n');
+
+ def GenerateSingleOperation(self, emitter, info, indent, operation):
sra1 2012/05/10 00:12:06 We really need to avoid another copy of the dispat
+ """Generates a call to a single operation.
+
+ Arguments:
+ emitter: an Emitter for the body of a block of code.
+ info: the compound information about the operation and its overloads.
+ indent: an indentation string for generated code.
+ operation: the IDLOperation to call.
+ """
+ argument_expressions = ', '.join([i.name for i in info.param_infos])
+
+ if info.type_name != 'void':
+ # We could place the logic for handling Document directly in _wrap
+ # but we chose to place it here so that bugs in the wrapper and
+ # wrapperless implementations are more consistent.
+ emitter.Emit('$(INDENT)return $(THIS).$NAME($ARGS);\n',
+ INDENT=indent,
+ THIS=self.NativeObjectName(),
+ NAME=info.name,
+ ARGS=argument_expressions)
+ else:
+ emitter.Emit('$(INDENT)$(THIS).$NAME($ARGS);\n'
+ '$(INDENT)return;\n',
+ INDENT=indent,
+ THIS=self.NativeObjectName(),
+ NAME=info.name,
+ ARGS=argument_expressions)
+
+ def GenerateDispatch(self, emitter, info, indent, position, overloads):
+ """Generates a dispatch to one of the overloads.
+
+ Arguments:
+ emitter: an Emitter for the body of a block of code.
+ info: the compound information about the operation and its overloads.
+ indent: an indentation string for generated code.
+ position: the index of the parameter to dispatch on.
+ overloads: a list of the remaining IDLOperations to dispatch.
+
+ Returns True if the dispatch can fall through on failure, False if the code
+ always dispatches.
+ """
+
+ def NullCheck(name):
+ return '%s === null' % name
+
+ def TypeCheck(name, type):
+ return '%s is %s' % (name, type)
+
+ if position == len(info.param_infos):
+ if len(overloads) > 1:
+ raise Exception('Duplicate operations ' + str(overloads))
+ operation = overloads[0]
+ self.GenerateSingleOperation(emitter, info, indent, operation)
+ return False
+
+ # FIXME: Consider a simpler dispatch that iterates over the
+ # overloads and generates an overload specific check. Revisit
+ # when we move to named optional arguments.
+
+ # Partition the overloads to divide and conquer on the dispatch.
+ positive = []
+ negative = []
+ first_overload = overloads[0]
+ param = info.param_infos[position]
+
+ if position < len(first_overload.arguments):
+ # FIXME: This will not work if the second overload has a more
+ # precise type than the first. E.g.,
+ # void foo(Node x);
+ # void foo(Element x);
+ type = DartType(first_overload.arguments[position].type.id)
+ test = TypeCheck(param.name, type)
+ pred = lambda op: (len(op.arguments) > position and
+ DartType(op.arguments[position].type.id) == type)
+ else:
+ type = None
+ test = NullCheck(param.name)
+ pred = lambda op: position >= len(op.arguments)
+
+ for overload in overloads:
+ if pred(overload):
+ positive.append(overload)
+ else:
+ negative.append(overload)
+
+ if positive and negative:
+ (true_code, false_code) = emitter.Emit(
+ '$(INDENT)if ($COND) {\n'
+ '$!TRUE'
+ '$(INDENT)} else {\n'
+ '$!FALSE'
+ '$(INDENT)}\n',
+ COND=test, INDENT=indent)
+ fallthrough1 = self.GenerateDispatch(
+ true_code, info, indent + ' ', position + 1, positive)
+ fallthrough2 = self.GenerateDispatch(
+ false_code, info, indent + ' ', position, negative)
+ return fallthrough1 or fallthrough2
+
+ if negative:
+ raise Exception('Internal error, must be all positive')
+
+ # All overloads require the same test. Do we bother?
+
+ # If the test is the same as the method's formal parameter then checked mode
+ # will have done the test already. (It could be null too but we ignore that
+ # case since all the overload behave the same and we don't know which types
+ # in the IDL are not nullable.)
+ if type == param.dart_type:
+ return self.GenerateDispatch(
+ emitter, info, indent, position + 1, positive)
+
+ # Otherwise the overloads have the same type but the type is a substype of
+ # the method's synthesized formal parameter. e.g we have overloads f(X) and
+ # f(Y), implemented by the synthesized method f(Z) where X<Z and Y<Z. The
+ # dispatch has removed f(X), leaving only f(Y), but there is no guarantee
+ # that Y = Z-X, so we need to check for Y.
+ true_code = emitter.Emit(
+ '$(INDENT)if ($COND) {\n'
+ '$!TRUE'
+ '$(INDENT)}\n',
+ COND=test, INDENT=indent)
+ self.GenerateDispatch(
+ true_code, info, indent + ' ', position + 1, positive)
+ return True

Powered by Google App Engine
This is Rietveld 408576698