Chromium Code Reviews| 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 |